Import Tint changes from Dawn

Includes manual roll of various DEPS.

Changes: - 0acbb4e047c02713311472a262bf422bcc892791 Remove if-break deprecation by dan sinclair <dsinclair@chromium.org>
  - 669e15e1390960b58a705fa2116c36c868730ef3 [ir] Add EmitBinary by dan sinclair <dsinclair@chromium.org>
  - f6fcf0a3ef949a9d0beffc4457ae4a0daebb932d [ir] Add conditions to if and switch nodes. by dan sinclair <dsinclair@chromium.org>
  - a8bc2962598a16b369840059425754da3c530522 [spirv-reader] Emit break-if as needed. by dan sinclair <dsinclair@chromium.org>
  - a5b73a0fde4848aea9d9724e75d62f868a5153e2 [ir] Implement EmitLiteral. by dan sinclair <dsinclair@chromium.org>
  - 329dfd7cbd2df6025c697e3f00acbf91f65bd0fd tint: Implement const-eval of modf by Ben Clayton <bclayton@google.com>
  - 92d858ac3c497743921251a3b698803aee6ff0c1 tint: const eval of length builtin by Antonio Maiorano <amaiorano@google.com>
  - c982cd45c48304fae850725feaeec4689dd68157 Use TINT_REFLECT for OverrideId by Austin Eng <enga@chromium.org>
  - f9c6633006e84f697996fb72e570114576cc32c3 Update inter stage variable subsetting validation and add... by shrekshao <shrekshao@google.com>
  - 7f760cb25c36c99d09067a4b8022e91057fcf20f Tint truncate interstage variable transform by shrekshao <shrekshao@google.com>
  - 840bf6fc6d2d7c809c845c29e8e4cb26fb97bc1e [ir] Add a Register class. by dan sinclair <dsinclair@chromium.org>
  - 2664229fd5673b311b70ec9764e6435681e91fac Cleanup some grammar. by dan sinclair <dsinclair@chromium.org>
  - 6559903234da5967dd4ac06b545a9e81dab91dbf tint/resolver: Split constant checking to utility by Ben Clayton <bclayton@google.com>
  - d6d30f425653576f9a3da43f462a55327323b30f tint: const eval of dot builtin by Antonio Maiorano <amaiorano@google.com>
  - b2e6b7b8654fb1288c888f6d20fd9a9011b84e1f tint/resolver: Consistently use ConstEval::Result by Ben Clayton <bclayton@google.com>
  - e02377c2b383f1d11b115dc4fc128a5161810b07 [ir] Testing updates. by dan sinclair <dsinclair@chromium.org>
  - 51ecf65d437d0b754b4a0d50b1507c611846bc70 [ir] Stub remaining AST walk structure. by dan sinclair <dsinclair@chromium.org>
  - a32b6b4a0f4f02946072f66808728dda29533c81 [spirv-reader] Remove `forced` param from `MakeBranchDeta... by dan sinclair <dsinclair@chromium.org>
  - 81ce4b6237260ccb598b5d5b03d2a875b8e235f4 tint/transform: Include diagnostics in TINT_PRINT_PROGRAM... by Ben Clayton <bclayton@google.com>
  - 87bccb74d8a885cd1e6cc1dc52b4c003254859fc tint/transform: Handle arrays of complex override lengths by Ben Clayton <bclayton@google.com>
  - efe9c49819a61ba2d43ba6618bd043380e5a97f9 Add const-eval for `degrees` and `radians` by dan sinclair <dsinclair@chromium.org>
  - 619f9bd639aec0df39163f5440a835370930ea7e tint/transform: Fix ICE when combining polyfills by Ben Clayton <bclayton@google.com>
  - f5eec817de0df46d5aa2a7205f5b2f9c02e98dd4 tint: enable smoothstep error tests for f32 and f16 by Antonio Maiorano <amaiorano@google.com>
  - 1eaeb310a76a294fcc795eb5f6a7bed5723dd4dd tint: fail on creation of a non-finite constant by Antonio Maiorano <amaiorano@google.com>
  - 72551392e8871cb169e6ea74674bb840abb4bfca tint: remove ConstEval::current_source member and pass do... by Antonio Maiorano <amaiorano@google.com>
  - 893316014a3dc680226869196dc6bd0a7362c079 tint: emit an error for float conversions that are not re... by Antonio Maiorano <amaiorano@google.com>
  - fcac2f83040eb24461727eeefa1d487186cbffd8 tint: Strip aliases that reference unused overrides by James Price <jrprice@google.com>
  - 542db5ce066151564bba89d85ad08c8a196e1bcd tint: Fix TINT_PRINT_PROGRAM_FOR_EACH_TRANSFORM by Ben Clayton <bclayton@google.com>
  - 0d2aedf097a4c3732dff03fc901dfd73da909968 Run ./tools/format by Ben Clayton <bclayton@google.com>
  - 543dd18d3c517fa80f9fca3523c133a301e0032a tint: Remove textureSampleLevel(texture_external) by Ben Clayton <bclayton@google.com>
  - aedda6a50076c7747e66954a1b55bc0776615d17 tint: remove all inf/nan input cases for const-eval built... by Antonio Maiorano <amaiorano@google.com>
  - aff1656a762cb303715a3be72e58911df5c6a0f4 tint/spriv-reader: emit error on non-finite literal by Antonio Maiorano <amaiorano@google.com>
  - 0da91e06c57a9e45056c1690b972596f44fe7570 tint: make const eval of binary ops on concrete values fa... by Antonio Maiorano <amaiorano@google.com>
  - 9e15952e2d1af7fc7e910ffdc8352b76ac497b87 tint: Fix set of overrides in the shader interface by James Price <jrprice@google.com>
  - 83cef52eb74e208e9073de449f997a014ce45261 tint/resolver: Error when quantizeToF16 value is unrepres... by Ben Clayton <bclayton@google.com>
  - c2e2013c2d37d685776e4302bf7827194eca58c6 tint/resolver: Fix DBZ with explicit strides of invalid a... by Ben Clayton <bclayton@google.com>
  - e412c8d8c6adf0491ac03fa4f9d5eb268cbfa93d Remove tint::Source::data_view by Ben Clayton <bclayton@google.com>
  - 6a4c918cbfc6ea6c2ea0dc377851adb32d929cfd [ir] Sketch out tree walk by dan sinclair <dsinclair@chromium.org>
  - 5fbce71eeacd18295db28881352721eeee5cb875 Change some kNone to kUnknown. by dan sinclair <dsinclair@chromium.org>
  - 4d65fc91bb284269dd723fadb8ecead1eb28696c tint: Implement pointer alias analysis by James Price <jrprice@google.com>
  - d6f9a8a25305a2da47f0fc1d53bdff4ee8b01496 Enable some disabled test. by dan sinclair <dsinclair@chromium.org>
  - 46682e7e6bcd3819d35bee7477061cdaeb0a383f tint: remove redundant insertBits tests by Antonio Maiorano <amaiorano@google.com>
  - 8fbfacfd433fc368dad5c658deec31b9b43005e8 tint: fix insertBits edge case by Antonio Maiorano <amaiorano@google.com>
  - 32c28cbc9047e15bef4aac05b2662bf43e698588 Add const-eval for `smoothstep` by dan sinclair <dsinclair@chromium.org>
  - 19e5042adea7ee1d2e0e20fdbb37c5c74c7179bc Add const-eval for `round` by dan sinclair <dsinclair@chromium.org>
  - c214cbe98bf95d146d4108221036b5527ea14436 Add a `none` format. by dan sinclair <dsinclair@chromium.org>
  - 4d89ce1a680162f2239b6d934c4de416a27962f6 tint/number: add Checked* overloads for f32 and f16 by Antonio Maiorano <amaiorano@google.com>
  - 6be02b607f127b15ab57f35e95491ff253902837 tint: Fix stability of intrinsic_table.inl by Ben Clayton <bclayton@google.com>
  - 2d90dedea415dc1032db77d044573a0b49adbb40 Add const-eval for `sqrt` by dan sinclair <dsinclair@chromium.org>
  - 92af2b56935e3ba774a9499c5065d11e5a053b43 Add const-eval for `trunc` by dan sinclair <dsinclair@chromium.org>
  - c6d46994af9cf10d3d0ea50556cff12ab725ab5a [ir] Add framework to dump IR to text by dan sinclair <dsinclair@chromium.org>
  - acce83d10975dd9c6ef0137ba11198a22754034a Add const-eval for `min` and `max` by dan sinclair <dsinclair@chromium.org>
  - 7f06aa06ac1ce14195bf99ea46bdcbc322cd6927 spirv-reader: support NumWorkgroups by David Neto <dneto@google.com>
  - 267f1748c8287ee6ef27ed0bf65a7ede9f8afc31 Remove infrastructure for fallthrough by dan sinclair <dsinclair@chromium.org>
  - bf586f6dfd24ed223881961f47501c1200e3b6dc Remove fallthrough from WGSL parser. by dan sinclair <dsinclair@chromium.org>
  - d8a986beaebd705568608be3eb2aba1127d5f833 Remove fallthrough support from SPIRV-Reader. by dan sinclair <dsinclair@chromium.org>
  - 3c32758a0e6dbedfd08b731addee74aad5f4eb67 spirv-reader: Support texture and sampler args to user-de... by David Neto <dneto@google.com>
  - 1db8831be4c9c03d6fb1111014214bdfdfefe98b tint: fold error cases into regular case in const eval bi... by Antonio Maiorano <amaiorano@google.com>
  - 208fc35471c35da87b6cb0118fb43c52f629ac8a spirv-reader: refactor getting handle type by David Neto <dneto@google.com>
  - 8cd34f8425e4c7e94e786c2f2a15ff50f8b19760 tint: Fix DemoteToHelper for atomicCmpXchgWeak by James Price <jrprice@google.com>
  - 10fae7ac79e1bcb543efb41188196364f2722106 tint: Rename `frexp().sig` to `frexp().fract` by Ben Clayton <bclayton@google.com>
  - 1ef277db0ede0e4834e753ffcea829bcf8e20781 tint: Skip e2e test validation for known-good by Ben Clayton <bclayton@google.com>
  - c33d10ae79e5a9aa99512a7736da93039824f53b tint/resolver: Fix bad pointer deref (UAF) by Ben Clayton <bclayton@google.com>
  - 4e0eac148d622dd2d7d39937157a3e33a67fc264 Manual Roll third_party/vulkan-deps/ a7a3fb393..c29c532c9... by Ryan Harrison <rharrison@chromium.org>
  - 183b8eb19c87ca468edd7bbf6efe1dc62610b47c spirv-reader: func decl with handle or ptr-to-handle by David Neto <dneto@google.com>
  - b77332edd22fea10a6e145788bf54e9fa289341b Add const-eval for `tan` and `tanh` by dan sinclair <dsinclair@chromium.org>
  - 02d4ea06b9ad4ed7d48664c9518e7241c9425a39 Add const-eval for `cos` and `cosh` by dan sinclair <dsinclair@chromium.org>
  - b7868ff3ee24af868eadc9ee4d089372544dc4c1 Add const-eval for `sin` and `sinh` by dan sinclair <dsinclair@chromium.org>
  - e2fd5e09c6a938a2814906e0ed8ae5a9615fff9e Implement const-eval for `acosh` by dan sinclair <dsinclair@chromium.org>
  - 46ee63933c1e8c395d5dd5e6e284bc830f97f39a tint/transform: Implement div / mod polyfill by Ben Clayton <bclayton@google.com>
  - 9418152d08ec3efef9d62af290359a03c9cfa1db tint/resolver: Move from STL to tint::utils containers by Ben Clayton <bclayton@google.com>
  - 55a8eace750acd7155becfe9f1b240ef93d3bb58 tint/resolver: Forbid workgroup pointer parameters by Ben Clayton <bclayton@google.com>
  - 5ac2a365d90f649f5df8231e9745219997ee2812 Add const-eval for pack and unpack of 2x16float. by dan sinclair <dsinclair@chromium.org>
  - 00d0fd5e840d70d95b254631ef78045fa4220990 Update f16 to have from_bits by dan sinclair <dsinclair@chromium.org>
  - 744d0eb4aace2a6b110ce454152a27cc9f93d2d5 tint: Use "demote-to-helper" semantics for discard by James Price <jrprice@google.com>
  - 78ae4c243b6b99aee36661b1bf4c20be38122c1b tint: Fix phony assignments in DemoteToHelper by James Price <jrprice@google.com>
  - 97519c2cd3c4616966c56382f1895d99d230fc5f tint: Allow captured pointers as function args by James Price <jrprice@google.com>
  - 6251598ad2954427c352c7c36db7af27934cdc9c tint: Add DemoteToHelper transform by James Price <jrprice@google.com>
  - a7cd3aeff08d734a79259dba47066076d5379c42 tint: Rename SourceVariable() to RootIdentifier() by James Price <jrprice@google.com>
GitOrigin-RevId: 0acbb4e047c02713311472a262bf422bcc892791
Change-Id: Iae43e7c286bbf91f79457cd850d17f5cd683ad18
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/111501
Auto-Submit: Ben Clayton <bclayton@google.com>
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Dan Sinclair <dsinclair@chromium.org>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 51252d2..615a71b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -191,6 +191,12 @@
 
 set(TINT_ROOT_SOURCE_DIR ${PROJECT_SOURCE_DIR})
 
+set(IS_DEBUG_BUILD 0)
+string(TOUPPER "${CMAKE_BUILD_TYPE}" build_type)
+if ((NOT ${build_type} STREQUAL "RELEASE") AND (NOT ${build_type} STREQUAL "RELWITHDEBINFO"))
+  set(IS_DEBUG_BUILD 1)
+endif()
+
 # CMake < 3.15 sets /W3 in CMAKE_CXX_FLAGS. Remove it if it's there.
 # See https://gitlab.kitware.com/cmake/cmake/-/issues/18317
 if (MSVC)
diff --git a/DEPS b/DEPS
index 7ad48a3..b39b02d 100644
--- a/DEPS
+++ b/DEPS
@@ -21,24 +21,24 @@
   },
 
   'third_party/vulkan-deps': {
-    'url': '{chromium_git}/vulkan-deps@5d66d3e5917b6dfdafce9cfeaaf6041e74ae5e0d',
+    'url': '{chromium_git}/vulkan-deps@bd2c589d0d34612d9dd071396225d7de772c020c',
   },
 
   # Dependencies required to use GN/Clang in standalone
   'build': {
-    'url': '{chromium_git}/chromium/src/build@555c8b467c21e2c4b22d00e87e3faa0431df9ac2',
+    'url': '{chromium_git}/chromium/src/build@01569374d46a14b225586c564146a8e1749520b6',
   },
 
   'buildtools': {
-    'url': '{chromium_git}/chromium/src/buildtools@f78b4b9f33bd8ef9944d5ce643daff1c31880189',
+    'url': '{chromium_git}/chromium/src/buildtools@cccaf48c82bcf4ddafa6f8aa9f06014a1ef434bf',
   },
 
   'tools/clang': {
-    'url': '{chromium_git}/chromium/src/tools/clang@8b7330592cb85ba09505a6be7bacabd0ad6160a3',
+    'url': '{chromium_git}/chromium/src/tools/clang@a5e0d72349d028a4023927d6d166a8478355fac3',
   },
 
   'buildtools/clang_format/script': {
-    'url': '{chromium_git}/external/github.com/llvm/llvm-project/clang/tools/clang-format.git@2271e89c145a5e27d6c110b6a1113c057a8301a3',
+    'url': '{chromium_git}/external/github.com/llvm/llvm-project/clang/tools/clang-format.git@8b525d2747f2584fc35d8c7e612e66f377858df7',
   },
 
   'buildtools/linux64': {
@@ -67,11 +67,11 @@
   },
 
   'buildtools/third_party/libc++/trunk': {
-    'url': '{chromium_git}/external/github.com/llvm/llvm-project/libcxx.git@79a2e924d96e2fc1e4b937c42efd08898fa472d7',
+    'url': '{chromium_git}/external/github.com/llvm/llvm-project/libcxx.git@26d0ab4151fd10c523fdbb5bbdb59aa5a5774820',
   },
 
   'buildtools/third_party/libc++abi/trunk': {
-    'url': '{chromium_git}/external/github.com/llvm/llvm-project/libcxxabi.git@2715a6c0de8dac4c7674934a6b3d30ba0c685271',
+    'url': '{chromium_git}/external/github.com/llvm/llvm-project/libcxxabi.git@5c3e02e92ae8bbc1bf1001bd9ef0d76e044ddb86',
   },
 
   'third_party/ninja': {
@@ -87,11 +87,11 @@
 
   # Dependencies required for testing
   'testing': {
-    'url': '{chromium_git}/chromium/src/testing@d485ae97b7900c1fb7edfbe2901ae5adcb120865',
+    'url': '{chromium_git}/chromium/src/testing@e3e8c19554e8f47da85d35e4f990cdc30a061196',
   },
 
   'third_party/catapult': {
-    'url': '{chromium_git}/catapult.git@fa35beefb3429605035f98211ddb8750dee6a13d',
+    'url': '{chromium_git}/catapult.git@4a0e6f034e9756605cfc837c8526588d6c13436b',
   },
 
   'third_party/benchmark': {
@@ -99,7 +99,7 @@
   },
 
   'third_party/googletest': {
-    'url': '{chromium_git}/external/github.com/google/googletest.git@6b74da4757a549563d7c37c8fae3e704662a043b',
+    'url': '{chromium_git}/external/github.com/google/googletest.git@d1a0039b97291dd1dc14f123b906bb7622ffe07c',
   },
 
   'third_party/protobuf': {
@@ -115,22 +115,33 @@
     'condition': 'host_os == "win"',
     'action': [ 'download_from_google_storage',
                 '--no_resume',
-                '--platform=win32',
                 '--no_auth',
                 '--bucket', 'chromium-clang-format',
                 '-s', 'buildtools/win/clang-format.exe.sha1',
     ],
   },
   {
-    'name': 'clang_format_mac',
+    'name': 'clang_format_mac_x64',
     'pattern': '.',
-    'condition': 'host_os == "mac"',
+    'condition': 'host_os == "mac" and host_cpu == "x64"',
     'action': [ 'download_from_google_storage',
                 '--no_resume',
-                '--platform=darwin',
                 '--no_auth',
                 '--bucket', 'chromium-clang-format',
-                '-s', 'buildtools/mac/clang-format.sha1',
+                '-s', 'buildtools/mac/clang-format.x64.sha1',
+                '-o', 'buildtools/mac/clang-format',
+    ],
+  },
+  {
+    'name': 'clang_format_mac_arm64',
+    'pattern': '.',
+    'condition': 'host_os == "mac" and host_cpu == "arm64"',
+    'action': [ 'download_from_google_storage',
+                '--no_resume',
+                '--no_auth',
+                '--bucket', 'chromium-clang-format',
+                '-s', 'buildtools/mac/clang-format.arm64.sha1',
+                '-o', 'buildtools/mac/clang-format',
     ],
   },
   {
@@ -139,7 +150,6 @@
     'condition': 'host_os == "linux"',
     'action': [ 'download_from_google_storage',
                 '--no_resume',
-                '--platform=linux*',
                 '--no_auth',
                 '--bucket', 'chromium-clang-format',
                 '-s', 'buildtools/linux64/clang-format.sha1',
diff --git a/include/tint/override_id.h b/include/tint/override_id.h
index 05e56a7..c638117 100644
--- a/include/tint/override_id.h
+++ b/include/tint/override_id.h
@@ -18,11 +18,16 @@
 #include <stdint.h>
 #include <functional>
 
+#include "src/tint/reflection.h"
+
 namespace tint {
 
 /// OverrideId is a numerical identifier for an override variable, unique per program.
 struct OverrideId {
     uint16_t value = 0;
+
+    /// Reflect the fields of this struct so that it can be used by tint::ForeachField()
+    TINT_REFLECT(value);
 };
 
 /// Equality operator for OverrideId
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index 39fae60..491fd7d 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -247,8 +247,6 @@
     "ast/f16.h",
     "ast/f32.cc",
     "ast/f32.h",
-    "ast/fallthrough_statement.cc",
-    "ast/fallthrough_statement.h",
     "ast/float_literal_expression.cc",
     "ast/float_literal_expression.h",
     "ast/for_loop_statement.cc",
@@ -500,6 +498,8 @@
     "transform/decompose_strided_array.h",
     "transform/decompose_strided_matrix.cc",
     "transform/decompose_strided_matrix.h",
+    "transform/demote_to_helper.cc",
+    "transform/demote_to_helper.h",
     "transform/disable_uniformity_analysis.cc",
     "transform/disable_uniformity_analysis.h",
     "transform/expand_compound_assignment.cc",
@@ -550,10 +550,10 @@
     "transform/substitute_override.h",
     "transform/transform.cc",
     "transform/transform.h",
+    "transform/truncate_interstage_variables.cc",
+    "transform/truncate_interstage_variables.h",
     "transform/unshadow.cc",
     "transform/unshadow.h",
-    "transform/unwind_discard_functions.cc",
-    "transform/unwind_discard_functions.h",
     "transform/utils/get_insertion_point.cc",
     "transform/utils/get_insertion_point.h",
     "transform/utils/hoist_to_decl_before.cc",
@@ -1047,7 +1047,6 @@
       "ast/external_texture_test.cc",
       "ast/f16_test.cc",
       "ast/f32_test.cc",
-      "ast/fallthrough_statement_test.cc",
       "ast/float_literal_expression_test.cc",
       "ast/for_loop_statement_test.cc",
       "ast/function_test.cc",
@@ -1119,6 +1118,7 @@
     sources = [
       "resolver/address_space_layout_validation_test.cc",
       "resolver/address_space_validation_test.cc",
+      "resolver/alias_analysis_test.cc",
       "resolver/array_accessor_test.cc",
       "resolver/assignment_validation_test.cc",
       "resolver/atomics_test.cc",
@@ -1159,8 +1159,8 @@
       "resolver/resolver_test.cc",
       "resolver/resolver_test_helper.cc",
       "resolver/resolver_test_helper.h",
+      "resolver/root_identifier_test.cc",
       "resolver/side_effects_test.cc",
-      "resolver/source_variable_test.cc",
       "resolver/static_assert_test.cc",
       "resolver/struct_address_space_use_test.cc",
       "resolver/struct_layout_test.cc",
@@ -1223,6 +1223,7 @@
       "transform/decompose_memory_access_test.cc",
       "transform/decompose_strided_array_test.cc",
       "transform/decompose_strided_matrix_test.cc",
+      "transform/demote_to_helper_test.cc",
       "transform/disable_uniformity_analysis_test.cc",
       "transform/expand_compound_assignment_test.cc",
       "transform/first_index_offset_test.cc",
@@ -1249,8 +1250,8 @@
       "transform/substitute_override_test.cc",
       "transform/test_helper.h",
       "transform/transform_test.cc",
+      "transform/truncate_interstage_variables_test.cc",
       "transform/unshadow_test.cc",
-      "transform/unwind_discard_functions_test.cc",
       "transform/utils/get_insertion_point_test.cc",
       "transform/utils/hoist_to_decl_before_test.cc",
       "transform/var_for_dynamic_index_test.cc",
@@ -1480,7 +1481,6 @@
       "writer/wgsl/generator_impl_continue_test.cc",
       "writer/wgsl/generator_impl_discard_test.cc",
       "writer/wgsl/generator_impl_enable_test.cc",
-      "writer/wgsl/generator_impl_fallthrough_test.cc",
       "writer/wgsl/generator_impl_function_test.cc",
       "writer/wgsl/generator_impl_global_decl_test.cc",
       "writer/wgsl/generator_impl_identifier_test.cc",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index 1d39243..dfe9433 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -139,8 +139,6 @@
   ast/f16.h
   ast/f32.cc
   ast/f32.h
-  ast/fallthrough_statement.cc
-  ast/fallthrough_statement.h
   ast/float_literal_expression.cc
   ast/float_literal_expression.h
   ast/for_loop_statement.cc
@@ -425,6 +423,8 @@
   transform/decompose_strided_array.h
   transform/decompose_strided_matrix.cc
   transform/decompose_strided_matrix.h
+  transform/demote_to_helper.cc
+  transform/demote_to_helper.h
   transform/disable_uniformity_analysis.cc
   transform/disable_uniformity_analysis.h
   transform/expand_compound_assignment.cc
@@ -475,10 +475,10 @@
   transform/substitute_override.h
   transform/transform.cc
   transform/transform.h
+  transform/truncate_interstage_variables.cc
+  transform/truncate_interstage_variables.h
   transform/unshadow.cc
   transform/unshadow.h
-  transform/unwind_discard_functions.cc
-  transform/unwind_discard_functions.h
   transform/utils/get_insertion_point.cc
   transform/utils/get_insertion_point.h
   transform/utils/hoist_to_decl_before.cc
@@ -658,6 +658,8 @@
     ir/builder_impl.h
     ir/debug.cc
     ir/debug.h
+    ir/disassembler.cc
+    ir/disassembler.h
     ir/flow_node.cc
     ir/flow_node.h
     ir/function.cc
@@ -668,6 +670,10 @@
     ir/loop.h
     ir/module.cc
     ir/module.h
+    ir/op.cc
+    ir/op.h
+    ir/register.cc
+    ir/register.h
     ir/switch.cc
     ir/switch.h
     ir/terminator.cc
@@ -786,7 +792,6 @@
     ast/external_texture_test.cc
     ast/f16_test.cc
     ast/f32_test.cc
-    ast/fallthrough_statement_test.cc
     ast/float_literal_expression_test.cc
     ast/for_loop_statement_test.cc
     ast/function_test.cc
@@ -843,6 +848,7 @@
     program_builder_test.cc
     program_test.cc
     reflection_test.cc
+    resolver/alias_analysis_test.cc
     resolver/array_accessor_test.cc
     resolver/assignment_validation_test.cc
     resolver/atomics_test.cc
@@ -885,7 +891,7 @@
     resolver/resolver_test.cc
     resolver/side_effects_test.cc
     resolver/static_assert_test.cc
-    resolver/source_variable_test.cc
+    resolver/root_identifier_test.cc
     resolver/address_space_layout_validation_test.cc
     resolver/address_space_validation_test.cc
     resolver/struct_layout_test.cc
@@ -1150,7 +1156,6 @@
       writer/wgsl/generator_impl_continue_test.cc
       writer/wgsl/generator_impl_discard_test.cc
       writer/wgsl/generator_impl_enable_test.cc
-      writer/wgsl/generator_impl_fallthrough_test.cc
       writer/wgsl/generator_impl_function_test.cc
       writer/wgsl/generator_impl_global_decl_test.cc
       writer/wgsl/generator_impl_identifier_test.cc
@@ -1183,6 +1188,7 @@
       transform/decompose_memory_access_test.cc
       transform/decompose_strided_array_test.cc
       transform/decompose_strided_matrix_test.cc
+      transform/demote_to_helper_test.cc
       transform/disable_uniformity_analysis_test.cc
       transform/expand_compound_assignment_test.cc
       transform/first_index_offset_test.cc
@@ -1209,8 +1215,8 @@
       transform/std140_test.cc
       transform/substitute_override_test.cc
       transform/test_helper.h
+      transform/truncate_interstage_variables_test.cc
       transform/unshadow_test.cc
-      transform/unwind_discard_functions_test.cc
       transform/var_for_dynamic_index_test.cc
       transform/vectorize_matrix_conversions_test.cc
       transform/vectorize_scalar_matrix_initializers_test.cc
@@ -1333,6 +1339,8 @@
   if (${TINT_BUILD_IR})
     list(APPEND TINT_TEST_SRCS
       ir/builder_impl_test.cc
+      ir/op_test.cc
+      ir/register_test.cc
       ir/test_helper.h
     )
   endif()
@@ -1357,9 +1365,8 @@
     # overflows when resolving deeply nested expression chains or statements.
     # Production builds neither use MSVC nor debug, so just bump the stack size
     # for this build combination.
-    string(TOUPPER "${CMAKE_BUILD_TYPE}" build_type)
-    if ((NOT ${build_type} STREQUAL "RELEASE") AND (NOT ${build_type} STREQUAL "RELWITHDEBINFO"))
-      target_link_options(tint_unittests PRIVATE "/STACK 2097152") # 2MB, default is 1MB
+    if (IS_DEBUG_BUILD)
+      target_link_options(tint_unittests PRIVATE "/STACK:4194304") # 4MB, default is 1MB
     endif()
   else()
     target_compile_options(tint_unittests PRIVATE
diff --git a/src/tint/ast/disable_validation_attribute.cc b/src/tint/ast/disable_validation_attribute.cc
index 2ae8c16..465a0ec 100644
--- a/src/tint/ast/disable_validation_attribute.cc
+++ b/src/tint/ast/disable_validation_attribute.cc
@@ -43,6 +43,8 @@
             return "disable_validation__ignore_stride";
         case DisabledValidation::kIgnoreInvalidPointerArgument:
             return "disable_validation__ignore_invalid_pointer_argument";
+        case DisabledValidation::kIgnorePointerAliasing:
+            return "disable_validation__ignore_pointer_aliasing";
     }
     return "<invalid>";
 }
diff --git a/src/tint/ast/disable_validation_attribute.h b/src/tint/ast/disable_validation_attribute.h
index d4c7455..f52b107 100644
--- a/src/tint/ast/disable_validation_attribute.h
+++ b/src/tint/ast/disable_validation_attribute.h
@@ -42,6 +42,9 @@
     /// When applied to a pointer function parameter, the validator will not require a function call
     /// argument passed for that parameter to have a certain form.
     kIgnoreInvalidPointerArgument,
+    /// When applied to a function declaration, the validator will not complain if multiple
+    /// pointer arguments alias when that function is called.
+    kIgnorePointerAliasing,
 };
 
 /// An internal attribute used to tell the validator to ignore specific
diff --git a/src/tint/ast/fallthrough_statement.cc b/src/tint/ast/fallthrough_statement.cc
deleted file mode 100644
index 0d30ae3..0000000
--- a/src/tint/ast/fallthrough_statement.cc
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2020 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/ast/fallthrough_statement.h"
-
-#include "src/tint/program_builder.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::FallthroughStatement);
-
-namespace tint::ast {
-
-FallthroughStatement::FallthroughStatement(ProgramID pid, NodeID nid, const Source& src)
-    : Base(pid, nid, src) {}
-
-FallthroughStatement::FallthroughStatement(FallthroughStatement&&) = default;
-
-FallthroughStatement::~FallthroughStatement() = default;
-
-const FallthroughStatement* FallthroughStatement::Clone(CloneContext* ctx) const {
-    // Clone arguments outside of create() call to have deterministic ordering
-    auto src = ctx->Clone(source);
-    return ctx->dst->create<FallthroughStatement>(src);
-}
-
-}  // namespace tint::ast
diff --git a/src/tint/ast/fallthrough_statement.h b/src/tint/ast/fallthrough_statement.h
deleted file mode 100644
index da2fd3d..0000000
--- a/src/tint/ast/fallthrough_statement.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2020 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_AST_FALLTHROUGH_STATEMENT_H_
-#define SRC_TINT_AST_FALLTHROUGH_STATEMENT_H_
-
-#include "src/tint/ast/statement.h"
-
-namespace tint::ast {
-
-/// An fallthrough statement
-class FallthroughStatement final : public Castable<FallthroughStatement, Statement> {
-  public:
-    /// Constructor
-    /// @param pid the identifier of the program that owns this node
-    /// @param nid the unique node identifier
-    /// @param src the source of this node
-    FallthroughStatement(ProgramID pid, NodeID nid, const Source& src);
-    /// Move constructor
-    FallthroughStatement(FallthroughStatement&&);
-    ~FallthroughStatement() override;
-
-    /// Clones this node and all transitive child nodes using the `CloneContext`
-    /// `ctx`.
-    /// @param ctx the clone context
-    /// @return the newly cloned node
-    const FallthroughStatement* Clone(CloneContext* ctx) const override;
-};
-
-}  // namespace tint::ast
-
-#endif  // SRC_TINT_AST_FALLTHROUGH_STATEMENT_H_
diff --git a/src/tint/ast/fallthrough_statement_test.cc b/src/tint/ast/fallthrough_statement_test.cc
deleted file mode 100644
index f823e91..0000000
--- a/src/tint/ast/fallthrough_statement_test.cc
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2020 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/ast/fallthrough_statement.h"
-
-#include "src/tint/ast/test_helper.h"
-
-namespace tint::ast {
-namespace {
-
-using FallthroughStatementTest = TestHelper;
-
-TEST_F(FallthroughStatementTest, Creation) {
-    auto* stmt = create<FallthroughStatement>();
-    EXPECT_EQ(stmt->source.range.begin.line, 0u);
-    EXPECT_EQ(stmt->source.range.begin.column, 0u);
-    EXPECT_EQ(stmt->source.range.end.line, 0u);
-    EXPECT_EQ(stmt->source.range.end.column, 0u);
-}
-
-TEST_F(FallthroughStatementTest, Creation_WithSource) {
-    auto* stmt = create<FallthroughStatement>(Source{Source::Location{20, 2}});
-    auto src = stmt->source;
-    EXPECT_EQ(src.range.begin.line, 20u);
-    EXPECT_EQ(src.range.begin.column, 2u);
-}
-
-TEST_F(FallthroughStatementTest, IsFallthrough) {
-    auto* stmt = create<FallthroughStatement>();
-    EXPECT_TRUE(stmt->Is<FallthroughStatement>());
-}
-
-}  // namespace
-}  // namespace tint::ast
diff --git a/src/tint/ast/statement.cc b/src/tint/ast/statement.cc
index 6acfff3..7b9ca5d 100644
--- a/src/tint/ast/statement.cc
+++ b/src/tint/ast/statement.cc
@@ -19,7 +19,6 @@
 #include "src/tint/ast/call_statement.h"
 #include "src/tint/ast/continue_statement.h"
 #include "src/tint/ast/discard_statement.h"
-#include "src/tint/ast/fallthrough_statement.h"
 #include "src/tint/ast/if_statement.h"
 #include "src/tint/ast/loop_statement.h"
 #include "src/tint/ast/return_statement.h"
@@ -58,9 +57,6 @@
     if (Is<DiscardStatement>()) {
         return "discard statement";
     }
-    if (Is<FallthroughStatement>()) {
-        return "fallthrough statement";
-    }
     if (Is<IfStatement>()) {
         return "if statement";
     }
diff --git a/src/tint/cmd/main.cc b/src/tint/cmd/main.cc
index 485ca5a..ae2e2e2 100644
--- a/src/tint/cmd/main.cc
+++ b/src/tint/cmd/main.cc
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <charconv>
 #include <cstdio>
 #include <fstream>
 #include <iostream>
@@ -24,7 +25,7 @@
 #include <vector>
 
 #if TINT_BUILD_GLSL_WRITER
-#include "StandAlone/ResourceLimits.h"
+#include "glslang/Public/ResourceLimits.h"
 #include "glslang/Public/ShaderLang.h"
 #endif  // TINT_BUILD_GLSL_WRITER
 
@@ -41,6 +42,7 @@
 
 #if TINT_BUILD_IR
 #include "src/tint/ir/debug.h"
+#include "src/tint/ir/disassembler.h"
 #include "src/tint/ir/module.h"
 #endif  // TINT_BUILD_IR
 
@@ -62,8 +64,14 @@
     exit(1);
 }
 
+/// Prints the given hash value in a format string that the end-to-end test runner can parse.
+void PrintHash(uint32_t hash) {
+    std::cout << "<<HASH: 0x" << std::hex << hash << ">>" << std::endl;
+}
+
 enum class Format {
-    kNone = -1,
+    kUnknown,
+    kNone,
     kSpirv,
     kSpvAsm,
     kWgsl,
@@ -82,10 +90,13 @@
     bool parse_only = false;
     bool disable_workgroup_init = false;
     bool validate = false;
+    bool print_hash = false;
     bool demangle = false;
     bool dump_inspector_bindings = false;
 
-    Format format = Format::kNone;
+    std::unordered_set<uint32_t> skip_hash;
+
+    Format format = Format::kUnknown;
 
     bool emit_single_entry_point = false;
     std::string ep_name;
@@ -101,6 +112,7 @@
     std::optional<tint::sem::BindingPoint> hlsl_root_constant_binding_point;
 
 #if TINT_BUILD_IR
+    bool dump_ir = false;
     bool dump_ir_graph = false;
 #endif  // TINT_BUILD_IR
 };
@@ -108,7 +120,7 @@
 const char kUsage[] = R"(Usage: tint [options] <input-file>
 
  options:
-  --format <spirv|spvasm|wgsl|msl|hlsl>  -- Output format.
+  --format <spirv|spvasm|wgsl|msl|hlsl|none>  -- Output format.
                                If not provided, will be inferred from output
                                filename extension:
                                    .spvasm -> spvasm
@@ -122,8 +134,7 @@
   -o <name>                 -- Output file name.  Use "-" for standard output
   --transform <name list>   -- Runs transforms, name list is comma separated
                                Available transforms:
-${transforms}
-  --parse-only              -- Stop after parsing the input
+${transforms} --parse-only              -- Stop after parsing the input
   --disable-workgroup-init  -- Disable workgroup memory zero initialization.
   --demangle                -- Preserve original source names. Demangle them.
                                Affects AST dumping, and text-based output languages.
@@ -135,6 +146,9 @@
                                default to binding 0 of the largest used group plus 1,
                                or group 0 if no resource bound.
   --validate                -- Validates the generated shader with all available validators
+  --skip-hash <hash list>   -- Skips validation if the hash of the output is equal to any
+                               of the hash codes in the comma separated list of hashes
+  --print-hash              -- Emit the hash of the output program
   --fxc                     -- Path to FXC dll, used to validate HLSL output.
                                When specified, automatically enables HLSL validation with FXC
   --dxc                     -- Path to DXC executable, used to validate HLSL output.
@@ -181,7 +195,11 @@
     }
 #endif  // TINT_BUILD_GLSL_WRITER
 
-    return Format::kNone;
+    if (fmt == "none") {
+        return Format::kNone;
+    }
+
+    return Format::kUnknown;
 }
 
 #if TINT_BUILD_SPV_WRITER || TINT_BUILD_WGSL_WRITER || TINT_BUILD_MSL_WRITER || \
@@ -229,7 +247,7 @@
     }
 #endif  // TINT_BUILD_HLSL_WRITER
 
-    return Format::kNone;
+    return Format::kUnknown;
 }
 
 std::vector<std::string> split_on_char(std::string list, char c) {
@@ -389,7 +407,7 @@
             }
             opts->format = parse_format(args[i]);
 
-            if (opts->format == Format::kNone) {
+            if (opts->format == Format::kUnknown) {
                 std::cerr << "Unknown output format: " << args[i] << std::endl;
                 return false;
             }
@@ -431,6 +449,24 @@
             opts->dump_inspector_bindings = true;
         } else if (arg == "--validate") {
             opts->validate = true;
+        } else if (arg == "--skip-hash") {
+            ++i;
+            if (i >= args.size()) {
+                std::cerr << "Missing hash value for " << arg << std::endl;
+                return false;
+            }
+            for (auto hash : split_on_comma(args[i])) {
+                uint32_t value = 0;
+                int base = 10;
+                if (hash.size() > 2 && hash[0] == '0' && (hash[1] == 'x' || hash[1] == 'X')) {
+                    hash = hash.substr(2);
+                    base = 16;
+                }
+                std::from_chars(hash.data(), hash.data() + hash.size(), value, base);
+                opts->skip_hash.emplace(value);
+            }
+        } else if (arg == "--print-hash") {
+            opts->print_hash = true;
         } else if (arg == "--fxc") {
             ++i;
             if (i >= args.size()) {
@@ -446,6 +482,8 @@
             }
             opts->dxc_path = args[i];
 #if TINT_BUILD_IR
+        } else if (arg == "--dump-ir") {
+            opts->dump_ir = true;
         } else if (arg == "--dump-ir-graph") {
             opts->dump_ir_graph = true;
 #endif  // TINT_BUILD_IR
@@ -682,7 +720,12 @@
         }
     }
 
-    if (options.validate) {
+    const auto hash = tint::utils::CRC32(result.spirv.data(), result.spirv.size());
+    if (options.print_hash) {
+        PrintHash(hash);
+    }
+
+    if (options.validate && options.skip_hash.count(hash) == 0) {
         // Use Vulkan 1.1, since this is what Tint, internally, uses.
         spvtools::SpirvTools tools(SPV_ENV_VULKAN_1_1);
         tools.SetMessageConsumer(
@@ -722,7 +765,12 @@
         return false;
     }
 
-    if (options.validate) {
+    const auto hash = tint::utils::CRC32(result.wgsl.data(), result.wgsl.size());
+    if (options.print_hash) {
+        PrintHash(hash);
+    }
+
+    if (options.validate && options.skip_hash.count(hash) == 0) {
         // Attempt to re-parse the output program with Tint's WGSL reader.
         auto source = std::make_unique<tint::Source::File>(options.input_filename, result.wgsl);
         auto reparsed_program = tint::reader::wgsl::Parse(source.get());
@@ -772,7 +820,12 @@
         return false;
     }
 
-    if (options.validate) {
+    const auto hash = tint::utils::CRC32(result.msl.c_str());
+    if (options.print_hash) {
+        PrintHash(hash);
+    }
+
+    if (options.validate && options.skip_hash.count(hash) == 0) {
         tint::val::Result res;
 #ifdef TINT_ENABLE_MSL_VALIDATION_USING_METAL_API
         res = tint::val::MslUsingMetalAPI(result.msl);
@@ -828,11 +881,17 @@
         return false;
     }
 
+    const auto hash = tint::utils::CRC32(result.hlsl.c_str());
+    if (options.print_hash) {
+        PrintHash(hash);
+    }
+
     // If --fxc or --dxc was passed, then we must explicitly find and validate with that respective
     // compiler.
     const bool must_validate_dxc = !options.dxc_path.empty();
     const bool must_validate_fxc = !options.fxc_path.empty();
-    if (options.validate || must_validate_dxc || must_validate_fxc) {
+    if ((options.validate || must_validate_dxc || must_validate_fxc) &&
+        (options.skip_hash.count(hash) == 0)) {
         tint::val::Result dxc_res;
         bool dxc_found = false;
         if (options.validate || must_validate_dxc) {
@@ -959,7 +1018,12 @@
             return false;
         }
 
-        if (options.validate) {
+        const auto hash = tint::utils::CRC32(result.glsl.c_str());
+        if (options.print_hash) {
+            PrintHash(hash);
+        }
+
+        if (options.validate && options.skip_hash.count(hash) == 0) {
             for (auto entry_pt : result.entry_points) {
                 EShLanguage lang = pipeline_stage_to_esh_language(entry_pt.second);
                 glslang::TShader shader(lang);
@@ -967,8 +1031,8 @@
                 int lengths[1] = {static_cast<int>(result.glsl.length())};
                 shader.setStringsWithLengths(strings, lengths, 1);
                 shader.setEntryPoint("main");
-                bool glslang_result = shader.parse(&glslang::DefaultTBuiltInResource, 310,
-                                                   EEsProfile, false, false, EShMsgDefault);
+                bool glslang_result = shader.parse(GetDefaultResources(), 310, EEsProfile, false,
+                                                   false, EShMsgDefault);
                 if (!glslang_result) {
                     std::cerr << "Error parsing GLSL shader:\n"
                               << shader.getInfoLog() << "\n"
@@ -1150,6 +1214,7 @@
         std::string usage = tint::utils::ReplaceAll(kUsage, "${transforms}", transform_names());
 #if TINT_BUILD_IR
         usage +=
+            "  --dump-ir                 -- Writes the IR to stdout\n"
             "  --dump-ir-graph           -- Writes the IR graph to 'tint.dot' as a dot graph\n";
 #endif  // TINT_BUILD_IR
 
@@ -1158,11 +1223,11 @@
     }
 
     // Implement output format defaults.
-    if (options.format == Format::kNone) {
+    if (options.format == Format::kUnknown) {
         // Try inferring from filename.
         options.format = infer_format(options.output_file);
     }
-    if (options.format == Format::kNone) {
+    if (options.format == Format::kUnknown) {
         // Ultimately, default to SPIR-V assembly. That's nice for interactive use.
         options.format = Format::kSpvAsm;
     }
@@ -1272,14 +1337,20 @@
     }
 
 #if TINT_BUILD_IR
-    if (options.dump_ir_graph) {
+    if (options.dump_ir || 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);
+            if (options.dump_ir) {
+                tint::ir::Disassembler d;
+                std::cout << d.Disassemble(mod) << std::endl;
+            }
+            if (options.dump_ir_graph) {
+                auto graph = tint::ir::Debug::AsDotGraph(&mod);
+                WriteFile("tint.dot", "w", graph);
+            }
         }
     }
 #endif  // TINT_BUILD_IR
@@ -1421,6 +1492,8 @@
         case Format::kGlsl:
             success = GenerateGlsl(program.get(), options);
             break;
+        case Format::kNone:
+            break;
         default:
             std::cerr << "Unknown output format specified" << std::endl;
             return 1;
diff --git a/src/tint/fuzzers/tint_ast_fuzzer/jump_tracker.cc b/src/tint/fuzzers/tint_ast_fuzzer/jump_tracker.cc
index 66bbaf7..d77a762 100644
--- a/src/tint/fuzzers/tint_ast_fuzzer/jump_tracker.cc
+++ b/src/tint/fuzzers/tint_ast_fuzzer/jump_tracker.cc
@@ -29,7 +29,7 @@
 namespace tint::fuzzers::ast_fuzzer {
 
 JumpTracker::JumpTracker(const Program& program) {
-    // Consider every AST node, looking for break, return and discard statements.
+    // Consider every AST node, looking for break and return statements.
     for (auto* node : program.ASTNodes().Objects()) {
         auto* stmt = node->As<ast::Statement>();
         if (stmt == nullptr) {
@@ -63,14 +63,12 @@
                 }
                 candidate_statements.insert(current);
             }
-        } else if (stmt->As<ast::ReturnStatement>() || stmt->As<ast::DiscardStatement>()) {
-            // Walk up the AST from the return or discard statement, recording that every node
-            // encountered along the way contains a return/discard.
-            auto& target_set = stmt->As<ast::ReturnStatement>() ? contains_return_
-                                                                : contains_intraprocedural_discard_;
+        } else if (stmt->As<ast::ReturnStatement>()) {
+            // Walk up the AST from the return statement, recording that every node encountered
+            // along the way contains a return.
             const ast::Statement* current = stmt;
             while (true) {
-                target_set.insert(current);
+                contains_return_.insert(current);
                 auto* parent = program.Sem().Get(current)->Parent();
                 if (parent == nullptr) {
                     break;
diff --git a/src/tint/fuzzers/tint_ast_fuzzer/jump_tracker.h b/src/tint/fuzzers/tint_ast_fuzzer/jump_tracker.h
index 614ceb5..ec40ee8 100644
--- a/src/tint/fuzzers/tint_ast_fuzzer/jump_tracker.h
+++ b/src/tint/fuzzers/tint_ast_fuzzer/jump_tracker.h
@@ -22,7 +22,7 @@
 
 namespace tint::fuzzers::ast_fuzzer {
 
-/// This class computes information on which statements contain loop breaks, returns and discards.
+/// This class computes information on which statements contain loop breaks and returns.
 /// It could be extended to handle other jumps, such as switch breaks and loop continues, should
 /// such information prove useful.
 class JumpTracker {
@@ -47,20 +47,9 @@
         return contains_return_.count(&statement) > 0;
     }
 
-    /// Indicates whether a statement contains a discard statement.
-    /// @param statement - the statement of interest.
-    /// @return true if and only if the statement is, or contains, a discard statement. This is
-    ///    determined in an intraprocedural fashion: the answer will be "false" if no discard occurs
-    ///    inside the statement, even if the statement calls a function that may lead to a discard
-    ///    being performed.
-    bool ContainsIntraproceduralDiscard(const ast::Statement& statement) const {
-        return contains_intraprocedural_discard_.count(&statement) > 0;
-    }
-
   private:
     std::unordered_set<const ast::Statement*> contains_break_for_innermost_loop_;
     std::unordered_set<const ast::Statement*> contains_return_;
-    std::unordered_set<const ast::Statement*> contains_intraprocedural_discard_;
 };
 
 }  // namespace tint::fuzzers::ast_fuzzer
diff --git a/src/tint/fuzzers/tint_ast_fuzzer/jump_tracker_test.cc b/src/tint/fuzzers/tint_ast_fuzzer/jump_tracker_test.cc
index f83e639..2482fee 100644
--- a/src/tint/fuzzers/tint_ast_fuzzer/jump_tracker_test.cc
+++ b/src/tint/fuzzers/tint_ast_fuzzer/jump_tracker_test.cc
@@ -200,93 +200,6 @@
     }
 }
 
-TEST(JumpTrackerTest, Discards) {
-    std::string content = R"(
-fn main() {
-  var x : u32;
-  for (var i : i32 = 0; i < 100; i++) {
-    if (i == 40) {
-      {
-        discard;
-      }
-    }
-    for (var j : i32 = 0; j < 10; j++) {
-      loop {
-        if (i > j) {
-          discard;
-        }
-        continuing {
-          i++;
-          j-=2;
-        }
-      }
-      switch (j) {
-        case 0: {
-          if (i == j) {
-            break;
-          }
-          i = i + 1;
-          continue;
-        }
-        default: {
-          discard;
-        }
-      }
-    }
-  }
-}
-  )";
-    Source::File file("test.wgsl", content);
-    auto program = reader::wgsl::Parse(&file);
-    ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
-
-    JumpTracker jump_tracker(program);
-
-    const auto* function_body = program.AST().Functions()[0]->body;
-    const auto* outer_loop = function_body->statements[1]->As<ast::ForLoopStatement>();
-    const auto* outer_loop_body = outer_loop->body;
-    const auto* first_if = outer_loop_body->statements[0]->As<ast::IfStatement>();
-    const auto* first_if_body = first_if->body;
-    const auto* block_in_first_if = first_if_body->statements[0]->As<ast::BlockStatement>();
-    const auto* discard_in_first_if = block_in_first_if->statements[0]->As<ast::DiscardStatement>();
-    const auto* inner_for_loop = outer_loop_body->statements[1]->As<ast::ForLoopStatement>();
-    const auto* inner_for_loop_body = inner_for_loop->body;
-    const auto* innermost_loop = inner_for_loop_body->statements[0]->As<ast::LoopStatement>();
-    const auto* innermost_loop_body = innermost_loop->body;
-    const auto* innermost_loop_if = innermost_loop_body->statements[0]->As<ast::IfStatement>();
-    const auto* innermost_loop_if_body = innermost_loop_if->body;
-    const auto* discard_in_innermost_loop =
-        innermost_loop_if_body->statements[0]->As<ast::DiscardStatement>();
-    const auto* switch_statement = inner_for_loop_body->statements[1]->As<ast::SwitchStatement>();
-    const auto* default_statement = switch_statement->body[1];
-    const auto* default_statement_body = default_statement->body;
-    const auto* discard_in_default_statement =
-        default_statement_body->statements[0]->As<ast::DiscardStatement>();
-
-    std::unordered_set<const ast::Statement*> containing_discard = {
-        function_body,          outer_loop,
-        outer_loop_body,        first_if,
-        first_if_body,          block_in_first_if,
-        discard_in_first_if,    inner_for_loop,
-        inner_for_loop_body,    innermost_loop,
-        innermost_loop_body,    innermost_loop_if,
-        innermost_loop_if_body, discard_in_innermost_loop,
-        switch_statement,       default_statement,
-        default_statement_body, discard_in_default_statement};
-
-    for (auto* node : program.ASTNodes().Objects()) {
-        auto* stmt = node->As<ast::Statement>();
-        if (stmt == nullptr) {
-            continue;
-        }
-        if (containing_discard.count(stmt) > 0) {
-            ASSERT_TRUE(jump_tracker.ContainsIntraproceduralDiscard(*stmt));
-        } else {
-            ASSERT_FALSE(jump_tracker.ContainsIntraproceduralDiscard(*stmt));
-        }
-    }
-}
-
 TEST(JumpTrackerTest, WhileLoop) {
     std::string content = R"(
 fn main() {
diff --git a/src/tint/fuzzers/tint_ast_fuzzer/mutations/delete_statement.cc b/src/tint/fuzzers/tint_ast_fuzzer/mutations/delete_statement.cc
index 1ec3ca4..89ab66b 100644
--- a/src/tint/fuzzers/tint_ast_fuzzer/mutations/delete_statement.cc
+++ b/src/tint/fuzzers/tint_ast_fuzzer/mutations/delete_statement.cc
@@ -135,10 +135,9 @@
         return false;
     }
 
-    if (jump_tracker.ContainsReturn(statement_node) ||
-        jump_tracker.ContainsIntraproceduralDiscard(statement_node)) {
-        // This is conservative. It would be possible to delete a return/discard statement as long
-        // as there is still a return/discard on every control flow path.
+    if (jump_tracker.ContainsReturn(statement_node)) {
+        // This is conservative. It would be possible to delete a return statement as long as there
+        // is still a return on every control flow path.
         return false;
     }
 
diff --git a/src/tint/fuzzers/tint_ast_fuzzer/mutations/delete_statement_test.cc b/src/tint/fuzzers/tint_ast_fuzzer/mutations/delete_statement_test.cc
index 275a19e..a2ab29c 100644
--- a/src/tint/fuzzers/tint_ast_fuzzer/mutations/delete_statement_test.cc
+++ b/src/tint/fuzzers/tint_ast_fuzzer/mutations/delete_statement_test.cc
@@ -606,48 +606,6 @@
     CheckStatementDeletionNotAllowed(original, statement_finder);
 }
 
-TEST(DeleteStatementTest, DoNotRemoveDiscard) {
-    auto original = R"(
-fn main() {
-  discard;
-})";
-    auto statement_finder = [](const Program& program) -> const ast::Statement* {
-        return program.AST().Functions()[0]->body->statements[0]->As<ast::DiscardStatement>();
-    };
-    CheckStatementDeletionNotAllowed(original, statement_finder);
-}
-
-TEST(DeleteStatementTest, DoNotRemoveStatementContainingDiscard) {
-    auto original = R"(
-fn foo() -> i32 {
-  if (true) {
-    discard;
-  } else {
-    discard;
-  }
-})";
-    auto statement_finder = [](const Program& program) -> const ast::Statement* {
-        return program.AST().Functions()[0]->body->statements[0]->As<ast::IfStatement>();
-    };
-    CheckStatementDeletionNotAllowed(original, statement_finder);
-}
-
-TEST(DeleteStatementTest, DoNotRemoveLoopBody) {
-    auto original = R"(
-fn foo() {
-  discard;
-}
-fn main() {
-  loop {
-    foo();
-  }
-})";
-    auto statement_finder = [](const Program& program) -> const ast::Statement* {
-        return program.AST().Functions()[1]->body->statements[0]->As<ast::LoopStatement>()->body;
-    };
-    CheckStatementDeletionNotAllowed(original, statement_finder);
-}
-
 TEST(DeleteStatementTest, DoNotRemoveForLoopBody) {
     auto original = R"(
 fn main() {
diff --git a/src/tint/inspector/entry_point.cc b/src/tint/inspector/entry_point.cc
index 8cbca14..a6c3b8c 100644
--- a/src/tint/inspector/entry_point.cc
+++ b/src/tint/inspector/entry_point.cc
@@ -35,5 +35,4 @@
 EntryPoint::EntryPoint(EntryPoint&&) = default;
 EntryPoint::~EntryPoint() = default;
 
-
 }  // namespace tint::inspector
diff --git a/src/tint/inspector/inspector.cc b/src/tint/inspector/inspector.cc
index 516c1dc..b641f53 100644
--- a/src/tint/inspector/inspector.cc
+++ b/src/tint/inspector/inspector.cc
@@ -892,10 +892,10 @@
     utils::UniqueVector<const ast::CallExpression*, 8> callsites;
 
     for (size_t i = 0; i < N; i++) {
-        const sem::Variable* source_var = sem.Get(exprs[i])->SourceVariable();
-        if (auto* global = source_var->As<sem::GlobalVariable>()) {
+        const sem::Variable* root_ident = sem.Get(exprs[i])->RootIdentifier();
+        if (auto* global = root_ident->As<sem::GlobalVariable>()) {
             globals[i] = global;
-        } else if (auto* param = source_var->As<sem::Parameter>()) {
+        } else if (auto* param = root_ident->As<sem::Parameter>()) {
             auto* func = tint::As<sem::Function>(param->Owner());
             if (func->CallSites().empty()) {
                 // One or more of the expressions is a parameter, but this function
diff --git a/src/tint/inspector/inspector_test.cc b/src/tint/inspector/inspector_test.cc
index 6f3aa93..05f3c19 100644
--- a/src/tint/inspector/inspector_test.cc
+++ b/src/tint/inspector/inspector_test.cc
@@ -716,6 +716,193 @@
     EXPECT_EQ(1, result[0].overrides[0].id.value);
 }
 
+TEST_F(InspectorGetEntryPointTest, OverrideReferencedIndirectly) {
+    Override("foo", ty.f32());
+    Override("bar", ty.f32(), Mul(2_a, "foo"));
+    MakePlainGlobalReferenceBodyFunction("ep_func", "bar", ty.f32(),
+                                         utils::Vector{
+                                             Stage(ast::PipelineStage::kCompute),
+                                             WorkgroupSize(1_i),
+                                         });
+
+    Inspector& inspector = Build();
+
+    auto result = inspector.GetEntryPoints();
+
+    ASSERT_EQ(1u, result.size());
+    ASSERT_EQ(2u, result[0].overrides.size());
+    EXPECT_EQ("bar", result[0].overrides[0].name);
+    EXPECT_TRUE(result[0].overrides[0].is_initialized);
+    EXPECT_EQ("foo", result[0].overrides[1].name);
+    EXPECT_FALSE(result[0].overrides[1].is_initialized);
+}
+
+TEST_F(InspectorGetEntryPointTest, OverrideReferencedIndirectly_ViaPrivateInitializer) {
+    Override("foo", ty.f32());
+    GlobalVar("bar", ast::AddressSpace::kPrivate, ty.f32(), Mul(2_a, "foo"));
+    MakePlainGlobalReferenceBodyFunction("ep_func", "bar", ty.f32(),
+                                         utils::Vector{
+                                             Stage(ast::PipelineStage::kCompute),
+                                             WorkgroupSize(1_i),
+                                         });
+
+    Inspector& inspector = Build();
+
+    auto result = inspector.GetEntryPoints();
+
+    ASSERT_EQ(1u, result.size());
+    ASSERT_EQ(1u, result[0].overrides.size());
+    EXPECT_EQ("foo", result[0].overrides[0].name);
+    EXPECT_FALSE(result[0].overrides[0].is_initialized);
+}
+
+TEST_F(InspectorGetEntryPointTest, OverrideReferencedIndirectly_MultipleEntryPoints) {
+    Override("foo1", ty.f32());
+    Override("bar1", ty.f32(), Mul(2_a, "foo1"));
+    MakePlainGlobalReferenceBodyFunction("ep_func1", "bar1", ty.f32(),
+                                         utils::Vector{
+                                             Stage(ast::PipelineStage::kCompute),
+                                             WorkgroupSize(1_i),
+                                         });
+    Override("foo2", ty.f32());
+    Override("bar2", ty.f32(), Mul(2_a, "foo2"));
+    MakePlainGlobalReferenceBodyFunction("ep_func2", "bar2", ty.f32(),
+                                         utils::Vector{
+                                             Stage(ast::PipelineStage::kCompute),
+                                             WorkgroupSize(1_i),
+                                         });
+
+    Inspector& inspector = Build();
+
+    auto result = inspector.GetEntryPoints();
+
+    ASSERT_EQ(2u, result.size());
+
+    ASSERT_EQ(2u, result[0].overrides.size());
+    EXPECT_EQ("bar1", result[0].overrides[0].name);
+    EXPECT_TRUE(result[0].overrides[0].is_initialized);
+    EXPECT_EQ("foo1", result[0].overrides[1].name);
+    EXPECT_FALSE(result[0].overrides[1].is_initialized);
+
+    ASSERT_EQ(2u, result[1].overrides.size());
+    EXPECT_EQ("bar2", result[1].overrides[0].name);
+    EXPECT_TRUE(result[1].overrides[0].is_initialized);
+    EXPECT_EQ("foo2", result[1].overrides[1].name);
+    EXPECT_FALSE(result[1].overrides[1].is_initialized);
+}
+
+TEST_F(InspectorGetEntryPointTest, OverrideReferencedByAttribute) {
+    Override("wgsize", ty.u32());
+    MakeEmptyBodyFunction("ep_func", utils::Vector{
+                                         Stage(ast::PipelineStage::kCompute),
+                                         WorkgroupSize("wgsize"),
+                                     });
+
+    Inspector& inspector = Build();
+
+    auto result = inspector.GetEntryPoints();
+
+    ASSERT_EQ(1u, result.size());
+    ASSERT_EQ(1u, result[0].overrides.size());
+    EXPECT_EQ("wgsize", result[0].overrides[0].name);
+    EXPECT_FALSE(result[0].overrides[0].is_initialized);
+}
+
+TEST_F(InspectorGetEntryPointTest, OverrideReferencedByAttributeIndirectly) {
+    Override("foo", ty.u32());
+    Override("bar", ty.u32(), Mul(2_a, "foo"));
+    MakeEmptyBodyFunction("ep_func", utils::Vector{
+                                         Stage(ast::PipelineStage::kCompute),
+                                         WorkgroupSize(Mul(2_a, Expr("bar"))),
+                                     });
+
+    Inspector& inspector = Build();
+
+    auto result = inspector.GetEntryPoints();
+
+    ASSERT_EQ(1u, result.size());
+    ASSERT_EQ(2u, result[0].overrides.size());
+    EXPECT_EQ("bar", result[0].overrides[0].name);
+    EXPECT_TRUE(result[0].overrides[0].is_initialized);
+    EXPECT_EQ("foo", result[0].overrides[1].name);
+    EXPECT_FALSE(result[0].overrides[1].is_initialized);
+}
+
+TEST_F(InspectorGetEntryPointTest, OverrideReferencedByArraySize) {
+    Override("size", ty.u32());
+    GlobalVar("v", ast::AddressSpace::kWorkgroup, ty.array(ty.f32(), "size"));
+    Func("ep", utils::Empty, ty.void_(),
+         utils::Vector{
+             Assign(Phony(), IndexAccessor("v", 0_a)),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kCompute),
+             WorkgroupSize(1_i),
+         });
+
+    Inspector& inspector = Build();
+
+    auto result = inspector.GetEntryPoints();
+
+    ASSERT_EQ(1u, result.size());
+    ASSERT_EQ(1u, result[0].overrides.size());
+    EXPECT_EQ("size", result[0].overrides[0].name);
+    EXPECT_FALSE(result[0].overrides[0].is_initialized);
+}
+
+TEST_F(InspectorGetEntryPointTest, OverrideReferencedByArraySizeIndirectly) {
+    Override("foo", ty.u32());
+    Override("bar", ty.u32(), Mul(2_a, "foo"));
+    GlobalVar("v", ast::AddressSpace::kWorkgroup, ty.array(ty.f32(), Mul(2_a, Expr("bar"))));
+    Func("ep", utils::Empty, ty.void_(),
+         utils::Vector{
+             Assign(Phony(), IndexAccessor("v", 0_a)),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kCompute),
+             WorkgroupSize(1_i),
+         });
+
+    Inspector& inspector = Build();
+
+    auto result = inspector.GetEntryPoints();
+
+    ASSERT_EQ(1u, result.size());
+    ASSERT_EQ(2u, result[0].overrides.size());
+    EXPECT_EQ("bar", result[0].overrides[0].name);
+    EXPECT_TRUE(result[0].overrides[0].is_initialized);
+    EXPECT_EQ("foo", result[0].overrides[1].name);
+    EXPECT_FALSE(result[0].overrides[1].is_initialized);
+}
+
+TEST_F(InspectorGetEntryPointTest, OverrideReferencedByArraySizeViaAlias) {
+    Override("foo", ty.u32());
+    Override("bar", ty.u32(), Expr("foo"));
+    Alias("MyArray", ty.array(ty.f32(), Mul(2_a, Expr("bar"))));
+    Override("zoo", ty.u32());
+    Alias("MyArrayUnused", ty.array(ty.f32(), Mul(2_a, Expr("zoo"))));
+    GlobalVar("v", ast::AddressSpace::kWorkgroup, ty.type_name("MyArray"));
+    Func("ep", utils::Empty, ty.void_(),
+         utils::Vector{
+             Assign(Phony(), IndexAccessor("v", 0_a)),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kCompute),
+             WorkgroupSize(1_i),
+         });
+
+    Inspector& inspector = Build();
+
+    auto result = inspector.GetEntryPoints();
+
+    ASSERT_EQ(1u, result.size());
+    ASSERT_EQ(2u, result[0].overrides.size());
+    EXPECT_EQ("bar", result[0].overrides[0].name);
+    EXPECT_TRUE(result[0].overrides[0].is_initialized);
+    EXPECT_EQ("foo", result[0].overrides[1].name);
+    EXPECT_FALSE(result[0].overrides[1].is_initialized);
+}
+
 TEST_F(InspectorGetEntryPointTest, OverrideTypes) {
     Override("bool_var", ty.bool_());
     Override("float_var", ty.f32());
diff --git a/src/tint/intrinsics.def b/src/tint/intrinsics.def
index 96f6d08..da58aea 100644
--- a/src/tint/intrinsics.def
+++ b/src/tint/intrinsics.def
@@ -408,8 +408,8 @@
 @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.96891242171) T) -> T
 @const fn acos<N: num, T: fa_f32_f16>(@test_value(0.96891242171) 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>
+@const fn acosh<T: fa_f32_f16>(@test_value(2.0) T) -> T
+@const fn acosh<N: num, T: fa_f32_f16>(@test_value(2.0) vec<N, T>) -> vec<N, T>
 @const fn all(bool) -> bool
 @const fn all<N: num>(vec<N, bool>) -> bool
 @const fn any(bool) -> bool
@@ -429,10 +429,10 @@
 @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
-fn cos<N: num, T: f32_f16>(vec<N, T>) -> vec<N, T>
-fn cosh<T: f32_f16>(T) -> T
-fn cosh<N: num, T: f32_f16>(vec<N, T>) -> vec<N, T>
+@const fn cos<T: fa_f32_f16>(@test_value(0) T) -> T
+@const fn cos<N: num, T: fa_f32_f16>(@test_value(0) vec<N, T>) -> vec<N, T>
+@const fn cosh<T: fa_f32_f16>(@test_value(0) T) -> T
+@const fn cosh<N: num, T: fa_f32_f16>(@test_value(0) vec<N, T>) -> vec<N, T>
 @const fn countLeadingZeros<T: iu32>(T) -> T
 @const fn countLeadingZeros<N: num, T: iu32>(vec<N, T>) -> vec<N, T>
 @const fn countOneBits<T: iu32>(T) -> T
@@ -440,12 +440,12 @@
 @const fn countTrailingZeros<T: iu32>(T) -> T
 @const fn countTrailingZeros<N: num, T: iu32>(vec<N, T>) -> vec<N, T>
 @const fn cross<T: fa_f32_f16>(vec3<T>, vec3<T>) -> vec3<T>
-fn degrees<T: f32_f16>(T) -> T
-fn degrees<N: num, T: f32_f16>(vec<N, T>) -> vec<N, T>
+@const fn degrees<T: fa_f32_f16>(T) -> T
+@const fn degrees<N: num, T: fa_f32_f16>(vec<N, T>) -> vec<N, T>
 fn determinant<N: num, T: f32_f16>(mat<N, N, T>) -> T
 fn distance<T: f32_f16>(T, T) -> T
 fn distance<N: num, T: f32_f16>(vec<N, T>, vec<N, T>) -> T
-fn dot<N: num, T: fiu32_f16>(vec<N, T>, vec<N, T>) -> T
+@const fn dot<N: num, T: fia_fiu32_f16>(vec<N, T>, vec<N, T>) -> T
 fn dot4I8Packed(u32, u32) -> i32
 fn dot4U8Packed(u32, u32) -> u32
 @stage("fragment") fn dpdx(f32) -> f32
@@ -491,23 +491,23 @@
 fn inverseSqrt<N: num, T: f32_f16>(vec<N, T>) -> vec<N, T>
 fn ldexp<T: f32_f16>(T, i32) -> T
 fn ldexp<N: num, T: f32_f16>(vec<N, T>, vec<N, i32>) -> vec<N, T>
-fn length<T: f32_f16>(T) -> T
-fn length<N: num, T: f32_f16>(vec<N, T>) -> T
+@const fn length<T: fa_f32_f16>(@test_value(0.0) T) -> T
+@const fn length<N: num, T: fa_f32_f16>(@test_value(0.0) vec<N, T>) -> T
 fn log<T: f32_f16>(T) -> T
 fn log<N: num, T: f32_f16>(vec<N, T>) -> vec<N, T>
 fn log2<T: f32_f16>(T) -> T
 fn log2<N: num, T: f32_f16>(vec<N, T>) -> vec<N, T>
-fn max<T: fiu32_f16>(T, T) -> T
-fn max<N: num, T: fiu32_f16>(vec<N, T>, vec<N, T>) -> vec<N, T>
-fn min<T: fiu32_f16>(T, T) -> T
-fn min<N: num, T: fiu32_f16>(vec<N, T>, vec<N, T>) -> vec<N, T>
+@const fn max<T: fia_fiu32_f16>(T, T) -> T
+@const fn max<N: num, T: fia_fiu32_f16>(vec<N, T>, vec<N, T>) -> vec<N, T>
+@const fn min<T: fia_fiu32_f16>(T, T) -> T
+@const fn min<N: num, T: fia_fiu32_f16>(vec<N, T>, vec<N, T>) -> vec<N, T>
 fn mix<T: f32_f16>(T, T, T) -> T
 fn mix<N: num, T: f32_f16>(vec<N, T>, vec<N, T>, vec<N, T>) -> vec<N, T>
 fn mix<N: num, T: f32_f16>(vec<N, T>, vec<N, T>, T) -> vec<N, T>
-fn modf<T: f32_f16>(T) -> __modf_result<T>
-fn modf<N: num, T: f32_f16>(vec<N, T>) -> __modf_result_vec<N, T>
+@const fn modf<T: f32_f16>(@test_value(-1.5) T) -> __modf_result<T>
+@const fn modf<N: num, T: f32_f16>(@test_value(-1.5) vec<N, T>) -> __modf_result_vec<N, T>
 fn normalize<N: num, T: f32_f16>(vec<N, T>) -> vec<N, T>
-fn pack2x16float(vec2<f32>) -> u32
+@const fn pack2x16float(vec2<f32>) -> u32
 @const fn pack2x16snorm(vec2<f32>) -> u32
 @const fn pack2x16unorm(vec2<f32>) -> u32
 @const fn pack4x8snorm(vec4<f32>) -> u32
@@ -516,14 +516,14 @@
 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>
+@const fn radians<T: fa_f32_f16>(T) -> T
+@const fn radians<N: num, T: fa_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>
 @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 round<T: fa_f32_f16>(@test_value(3.4) T) -> T
+@const fn round<N: num, T: fa_f32_f16>(@test_value(3.4) vec<N, T>) -> vec<N, T>
 @const fn saturate<T: fa_f32_f16>(@test_value(2) T) -> T
 @const fn saturate<T: fa_f32_f16, N: num>(@test_value(2) vec<N, T>) -> vec<N, T>
 @const("select_bool") fn select<T: scalar>(T, T, bool) -> T
@@ -531,25 +531,25 @@
 @const("select_boolvec") fn select<N: num, T: scalar>(vec<N, T>, vec<N, T>, vec<N, bool>) -> vec<N, T>
 @const fn sign<T: fa_f32_f16>(T) -> T
 @const fn sign<N: num, T: fa_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>
-fn sinh<T: f32_f16>(T) -> T
-fn sinh<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>
-fn sqrt<T: f32_f16>(T) -> T
-fn sqrt<N: num, T: f32_f16>(vec<N, T>) -> vec<N, T>
+@const fn sin<T: fa_f32_f16>(T) -> T
+@const fn sin<N: num, T: fa_f32_f16>(vec<N, T>) -> vec<N, T>
+@const fn sinh<T: fa_f32_f16>(T) -> T
+@const fn sinh<N: num, T: fa_f32_f16>(vec<N, T>) -> vec<N, T>
+@const fn smoothstep<T: fa_f32_f16>(@test_value(2) T, @test_value(4) T, @test_value(3) T) -> T
+@const fn smoothstep<N: num, T: fa_f32_f16>(@test_value(2) vec<N, T>, @test_value(4) vec<N, T>, @test_value(3) vec<N, T>) -> vec<N, T>
+@const fn sqrt<T: fa_f32_f16>(T) -> T
+@const fn sqrt<N: num, T: fa_f32_f16>(vec<N, T>) -> vec<N, T>
 @const fn step<T: fa_f32_f16>(T, T) -> T
 @const fn step<N: num, T: fa_f32_f16>(vec<N, T>, vec<N, T>) -> vec<N, T>
 @stage("compute") fn storageBarrier()
-fn tan<T: f32_f16>(T) -> T
-fn tan<N: num, T: f32_f16>(vec<N, T>) -> vec<N, T>
-fn tanh<T: f32_f16>(T) -> T
-fn tanh<N: num, T: f32_f16>(vec<N, T>) -> vec<N, T>
+@const fn tan<T: fa_f32_f16>(T) -> T
+@const fn tan<N: num, T: fa_f32_f16>(vec<N, T>) -> vec<N, T>
+@const fn tanh<T: fa_f32_f16>(T) -> T
+@const fn tanh<N: num, T: fa_f32_f16>(vec<N, T>) -> vec<N, T>
 fn transpose<M: num, N: num, T: f32_f16>(mat<M, N, T>) -> mat<N, M, T>
-fn trunc<T: f32_f16>(T) -> T
-fn trunc<N: num, T: f32_f16>(vec<N, T>) -> vec<N, T>
-fn unpack2x16float(u32) -> vec2<f32>
+@const fn trunc<T: fa_f32_f16>(@test_value(1.5) T) -> T
+@const fn trunc<N: num, T: fa_f32_f16>(@test_value(1.5) vec<N, T>) -> vec<N, T>
+@const fn unpack2x16float(u32) -> vec2<f32>
 @const fn unpack2x16snorm(u32) -> vec2<f32>
 @const fn unpack2x16unorm(u32) -> vec2<f32>
 @const fn unpack4x8snorm(u32) -> vec4<f32>
@@ -675,7 +675,6 @@
 fn textureSampleLevel<A: iu32, L: iu32>(texture: texture_depth_2d_array, sampler: sampler, coords: vec2<f32>, array_index: A, level: L, @const offset: vec2<i32>) -> f32
 fn textureSampleLevel<L: iu32>(texture: texture_depth_cube, sampler: sampler, coords: vec3<f32>, level: L) -> f32
 fn textureSampleLevel<A: iu32, L: iu32>(texture: texture_depth_cube_array,sampler: sampler, coords: vec3<f32>, array_index: A, level: L) -> f32
-@deprecated fn textureSampleLevel(texture: texture_external, sampler: sampler, coords: vec2<f32>) -> vec4<f32>
 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>
 fn textureStore<C: iu32>(texture: texture_storage_1d<f32_texel_format, write>, coords: C, value: vec4<f32>)
diff --git a/src/tint/ir/block.h b/src/tint/ir/block.h
index 022aff2..850c13c 100644
--- a/src/tint/ir/block.h
+++ b/src/tint/ir/block.h
@@ -16,6 +16,8 @@
 #define SRC_TINT_IR_BLOCK_H_
 
 #include "src/tint/ir/flow_node.h"
+#include "src/tint/ir/op.h"
+#include "src/tint/utils/vector.h"
 
 namespace tint::ir {
 
@@ -30,6 +32,9 @@
 
     /// The node this block branches too.
     const FlowNode* branch_target = nullptr;
+
+    /// The operations in the block
+    utils::Vector<Op, 16> ops;
 };
 
 }  // namespace tint::ir
diff --git a/src/tint/ir/builder.cc b/src/tint/ir/builder.cc
index 851d2d6..57a40a7 100644
--- a/src/tint/ir/builder.cc
+++ b/src/tint/ir/builder.cc
@@ -93,4 +93,84 @@
     to->inbound_branches.Push(from);
 }
 
+Register::Id Builder::AllocateRegister() {
+    return next_register_id++;
+}
+
+Op Builder::CreateOp(Op::Kind kind, Register lhs, Register rhs) {
+    return Op(kind, Register(AllocateRegister()), lhs, rhs);
+}
+
+Op Builder::And(Register lhs, Register rhs) {
+    return CreateOp(Op::Kind::kAnd, lhs, rhs);
+}
+
+Op Builder::Or(Register lhs, Register rhs) {
+    return CreateOp(Op::Kind::kOr, lhs, rhs);
+}
+
+Op Builder::Xor(Register lhs, Register rhs) {
+    return CreateOp(Op::Kind::kXor, lhs, rhs);
+}
+
+Op Builder::LogicalAnd(Register lhs, Register rhs) {
+    return CreateOp(Op::Kind::kLogicalAnd, lhs, rhs);
+}
+
+Op Builder::LogicalOr(Register lhs, Register rhs) {
+    return CreateOp(Op::Kind::kLogicalOr, lhs, rhs);
+}
+
+Op Builder::Equal(Register lhs, Register rhs) {
+    return CreateOp(Op::Kind::kEqual, lhs, rhs);
+}
+
+Op Builder::NotEqual(Register lhs, Register rhs) {
+    return CreateOp(Op::Kind::kNotEqual, lhs, rhs);
+}
+
+Op Builder::LessThan(Register lhs, Register rhs) {
+    return CreateOp(Op::Kind::kLessThan, lhs, rhs);
+}
+
+Op Builder::GreaterThan(Register lhs, Register rhs) {
+    return CreateOp(Op::Kind::kGreaterThan, lhs, rhs);
+}
+
+Op Builder::LessThanEqual(Register lhs, Register rhs) {
+    return CreateOp(Op::Kind::kLessThanEqual, lhs, rhs);
+}
+
+Op Builder::GreaterThanEqual(Register lhs, Register rhs) {
+    return CreateOp(Op::Kind::kGreaterThanEqual, lhs, rhs);
+}
+
+Op Builder::ShiftLeft(Register lhs, Register rhs) {
+    return CreateOp(Op::Kind::kShiftLeft, lhs, rhs);
+}
+
+Op Builder::ShiftRight(Register lhs, Register rhs) {
+    return CreateOp(Op::Kind::kShiftRight, lhs, rhs);
+}
+
+Op Builder::Add(Register lhs, Register rhs) {
+    return CreateOp(Op::Kind::kAdd, lhs, rhs);
+}
+
+Op Builder::Subtract(Register lhs, Register rhs) {
+    return CreateOp(Op::Kind::kSubtract, lhs, rhs);
+}
+
+Op Builder::Multiply(Register lhs, Register rhs) {
+    return CreateOp(Op::Kind::kMultiply, lhs, rhs);
+}
+
+Op Builder::Divide(Register lhs, Register rhs) {
+    return CreateOp(Op::Kind::kDivide, lhs, rhs);
+}
+
+Op Builder::Modulo(Register lhs, Register rhs) {
+    return CreateOp(Op::Kind::kModulo, lhs, rhs);
+}
+
 }  // namespace tint::ir
diff --git a/src/tint/ir/builder.h b/src/tint/ir/builder.h
index cf0de70..091aac8 100644
--- a/src/tint/ir/builder.h
+++ b/src/tint/ir/builder.h
@@ -19,6 +19,8 @@
 #include "src/tint/ir/if.h"
 #include "src/tint/ir/loop.h"
 #include "src/tint/ir/module.h"
+#include "src/tint/ir/op.h"
+#include "src/tint/ir/register.h"
 #include "src/tint/ir/switch.h"
 #include "src/tint/ir/terminator.h"
 
@@ -81,8 +83,129 @@
     /// @param to the node to branch too
     void Branch(Block* from, FlowNode* to);
 
+    /// Creates an op for `lhs kind rhs`
+    /// @param kind the kind of operation
+    /// @param lhs the left-hand-side of the operation
+    /// @param rhs the right-hand-side of the operation
+    /// @returns the operation
+    Op CreateOp(Op::Kind kind, Register lhs, Register rhs);
+
+    /// Creates an And operation
+    /// @param lhs the lhs of the add
+    /// @param rhs the rhs of the add
+    /// @returns the operation
+    Op And(Register lhs, Register rhs);
+
+    /// Creates an Or operation
+    /// @param lhs the lhs of the add
+    /// @param rhs the rhs of the add
+    /// @returns the operation
+    Op Or(Register lhs, Register rhs);
+
+    /// Creates an Xor operation
+    /// @param lhs the lhs of the add
+    /// @param rhs the rhs of the add
+    /// @returns the operation
+    Op Xor(Register lhs, Register rhs);
+
+    /// Creates an LogicalAnd operation
+    /// @param lhs the lhs of the add
+    /// @param rhs the rhs of the add
+    /// @returns the operation
+    Op LogicalAnd(Register lhs, Register rhs);
+
+    /// Creates an LogicalOr operation
+    /// @param lhs the lhs of the add
+    /// @param rhs the rhs of the add
+    /// @returns the operation
+    Op LogicalOr(Register lhs, Register rhs);
+
+    /// Creates an Equal operation
+    /// @param lhs the lhs of the add
+    /// @param rhs the rhs of the add
+    /// @returns the operation
+    Op Equal(Register lhs, Register rhs);
+
+    /// Creates an NotEqual operation
+    /// @param lhs the lhs of the add
+    /// @param rhs the rhs of the add
+    /// @returns the operation
+    Op NotEqual(Register lhs, Register rhs);
+
+    /// Creates an LessThan operation
+    /// @param lhs the lhs of the add
+    /// @param rhs the rhs of the add
+    /// @returns the operation
+    Op LessThan(Register lhs, Register rhs);
+
+    /// Creates an GreaterThan operation
+    /// @param lhs the lhs of the add
+    /// @param rhs the rhs of the add
+    /// @returns the operation
+    Op GreaterThan(Register lhs, Register rhs);
+
+    /// Creates an LessThanEqual operation
+    /// @param lhs the lhs of the add
+    /// @param rhs the rhs of the add
+    /// @returns the operation
+    Op LessThanEqual(Register lhs, Register rhs);
+
+    /// Creates an GreaterThanEqual operation
+    /// @param lhs the lhs of the add
+    /// @param rhs the rhs of the add
+    /// @returns the operation
+    Op GreaterThanEqual(Register lhs, Register rhs);
+
+    /// Creates an ShiftLeft operation
+    /// @param lhs the lhs of the add
+    /// @param rhs the rhs of the add
+    /// @returns the operation
+    Op ShiftLeft(Register lhs, Register rhs);
+
+    /// Creates an ShiftRight operation
+    /// @param lhs the lhs of the add
+    /// @param rhs the rhs of the add
+    /// @returns the operation
+    Op ShiftRight(Register lhs, Register rhs);
+
+    /// Creates an Add operation
+    /// @param lhs the lhs of the add
+    /// @param rhs the rhs of the add
+    /// @returns the operation
+    Op Add(Register lhs, Register rhs);
+
+    /// Creates an Subtract operation
+    /// @param lhs the lhs of the add
+    /// @param rhs the rhs of the add
+    /// @returns the operation
+    Op Subtract(Register lhs, Register rhs);
+
+    /// Creates an Multiply operation
+    /// @param lhs the lhs of the add
+    /// @param rhs the rhs of the add
+    /// @returns the operation
+    Op Multiply(Register lhs, Register rhs);
+
+    /// Creates an Divide operation
+    /// @param lhs the lhs of the add
+    /// @param rhs the rhs of the add
+    /// @returns the operation
+    Op Divide(Register lhs, Register rhs);
+
+    /// Creates an Modulo operation
+    /// @param lhs the lhs of the add
+    /// @param rhs the rhs of the add
+    /// @returns the operation
+    Op Modulo(Register lhs, Register rhs);
+
+    /// @returns a unique register id
+    Register::Id AllocateRegister();
+
     /// The IR module.
     Module ir;
+
+    /// The next register number to allocate
+    Register::Id next_register_id = 1;
 };
 
 }  // namespace tint::ir
diff --git a/src/tint/ir/builder_impl.cc b/src/tint/ir/builder_impl.cc
index 6bbcf6d..06ed837 100644
--- a/src/tint/ir/builder_impl.cc
+++ b/src/tint/ir/builder_impl.cc
@@ -15,19 +15,24 @@
 #include "src/tint/ir/builder_impl.h"
 
 #include "src/tint/ast/alias.h"
+#include "src/tint/ast/binary_expression.h"
 #include "src/tint/ast/block_statement.h"
+#include "src/tint/ast/bool_literal_expression.h"
 #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/float_literal_expression.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/int_literal_expression.h"
+#include "src/tint/ast/literal_expression.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/variable_decl_statement.h"
 #include "src/tint/ast/while_statement.h"
 #include "src/tint/ir/function.h"
 #include "src/tint/ir/if.h"
@@ -76,20 +81,20 @@
 
 }  // namespace
 
-BuilderImpl::BuilderImpl(const Program* program) : builder_(program) {}
+BuilderImpl::BuilderImpl(const Program* program) : builder(program) {}
 
 BuilderImpl::~BuilderImpl() = default;
 
 void BuilderImpl::BranchTo(FlowNode* node) {
-    TINT_ASSERT(IR, current_flow_block_);
-    TINT_ASSERT(IR, !IsBranched(current_flow_block_));
+    TINT_ASSERT(IR, current_flow_block);
+    TINT_ASSERT(IR, !IsBranched(current_flow_block));
 
-    builder_.Branch(current_flow_block_, node);
-    current_flow_block_ = nullptr;
+    builder.Branch(current_flow_block, node);
+    current_flow_block = nullptr;
 }
 
 void BuilderImpl::BranchToIfNeeded(FlowNode* node) {
-    if (!current_flow_block_ || IsBranched(current_flow_block_)) {
+    if (!current_flow_block || IsBranched(current_flow_block)) {
         return;
     }
     BranchTo(node);
@@ -111,28 +116,19 @@
 }
 
 ResultType BuilderImpl::Build() {
-    auto* sem = builder_.ir.program->Sem().Module();
+    auto* sem = builder.ir.program->Sem().Module();
 
     for (auto* decl : sem->DependencyOrderedDeclarations()) {
         bool ok = tint::Switch(
             decl,  //
-            // [&](const ast::Struct* str) {
-            //   return false;
-            // },
+            // [&](const ast::Struct* str) { },
             [&](const ast::Alias*) {
                 // Folded away and doesn't appear in the IR.
                 return true;
             },
-            // [&](const ast::Const*) {
-            //   return false;
-            // },
-            // [&](const ast::Override*) {
-            //   return false;
-            // },
+            // [&](const ast::Variable* var) { },
             [&](const ast::Function* func) { return EmitFunction(func); },
-            // [&](const ast::Enable*) {
-            //   return false;
-            // },
+            // [&](const ast::Enable*) { },
             [&](const ast::StaticAssert*) {
                 // Evaluated by the resolver, drop from the IR.
                 return true;
@@ -148,27 +144,27 @@
         }
     }
 
-    return ResultType{std::move(builder_.ir)};
+    return ResultType{std::move(builder.ir)};
 }
 
 bool BuilderImpl::EmitFunction(const ast::Function* ast_func) {
     // The flow stack should have been emptied when the previous function finshed building.
     TINT_ASSERT(IR, flow_stack.IsEmpty());
 
-    auto* ir_func = builder_.CreateFunction(ast_func);
+    auto* ir_func = builder.CreateFunction(ast_func);
     current_function_ = ir_func;
-    builder_.ir.functions.Push(ir_func);
+    builder.ir.functions.Push(ir_func);
 
     ast_to_flow_[ast_func] = ir_func;
 
     if (ast_func->IsEntryPoint()) {
-        builder_.ir.entry_points.Push(ir_func);
+        builder.ir.entry_points.Push(ir_func);
     }
 
     {
         FlowStackScope scope(this, ir_func);
 
-        current_flow_block_ = ir_func->start_target;
+        current_flow_block = ir_func->start_target;
         if (!EmitStatements(ast_func->body->statements)) {
             return false;
         }
@@ -179,7 +175,7 @@
     }
 
     TINT_ASSERT(IR, flow_stack.IsEmpty());
-    current_flow_block_ = nullptr;
+    current_flow_block = nullptr;
     current_function_ = nullptr;
 
     return true;
@@ -193,7 +189,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_ || IsBranched(current_flow_block_)) {
+        if (!current_flow_block || IsBranched(current_flow_block)) {
             break;
         }
     }
@@ -203,21 +199,21 @@
 bool BuilderImpl::EmitStatement(const ast::Statement* stmt) {
     return tint::Switch(
         stmt,
-        //        [&](const ast::AssignmentStatement* a) { },
+        // [&](const ast::AssignmentStatement* a) { },
         [&](const ast::BlockStatement* b) { return EmitBlock(b); },
         [&](const ast::BreakStatement* b) { return EmitBreak(b); },
         [&](const ast::BreakIfStatement* b) { return EmitBreakIf(b); },
-        //        [&](const ast::CallStatement* c) { },
+        // [&](const ast::CallStatement* c) { },
+        // [&](const ast::CompoundAssignmentStatement* c) { },
         [&](const ast::ContinueStatement* c) { return EmitContinue(c); },
-        //        [&](const ast::DiscardStatement* d) { },
-        [&](const ast::FallthroughStatement*) { return EmitFallthrough(); },
+        // [&](const ast::DiscardStatement* d) { },
         [&](const ast::IfStatement* i) { return EmitIf(i); },
         [&](const ast::LoopStatement* l) { return EmitLoop(l); },
         [&](const ast::ForLoopStatement* l) { return EmitForLoop(l); },
         [&](const ast::WhileStatement* l) { return EmitWhile(l); },
         [&](const ast::ReturnStatement* r) { return EmitReturn(r); },
         [&](const ast::SwitchStatement* s) { return EmitSwitch(s); },
-        //        [&](const ast::VariableDeclStatement* v) { },
+        [&](const ast::VariableDeclStatement* v) { return EmitVariable(v->variable); },
         [&](const ast::StaticAssert*) {
             return true;  // Not emitted
         },
@@ -237,9 +233,14 @@
 }
 
 bool BuilderImpl::EmitIf(const ast::IfStatement* stmt) {
-    auto* if_node = builder_.CreateIf(stmt);
+    auto* if_node = builder.CreateIf(stmt);
 
-    // TODO(dsinclair): Emit the condition expression into the current block
+    // Emit the if condition into the end of the preceeding block
+    auto reg = EmitExpression(stmt->condition);
+    if (!reg) {
+        return false;
+    }
+    if_node->condition = reg.Get();
 
     BranchTo(if_node);
 
@@ -248,36 +249,34 @@
     {
         FlowStackScope scope(this, if_node);
 
-        // TODO(dsinclair): set if condition register into if flow node
-
-        current_flow_block_ = if_node->true_target;
+        current_flow_block = if_node->true_target;
         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;
+        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;
+    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 (IsConnected(if_node->merge_target)) {
-        current_flow_block_ = if_node->merge_target;
+        current_flow_block = if_node->merge_target;
     }
 
     return true;
 }
 
 bool BuilderImpl::EmitLoop(const ast::LoopStatement* stmt) {
-    auto* loop_node = builder_.CreateLoop(stmt);
+    auto* loop_node = builder.CreateLoop(stmt);
 
     BranchTo(loop_node);
 
@@ -286,7 +285,7 @@
     {
         FlowStackScope scope(this, loop_node);
 
-        current_flow_block_ = loop_node->start_target;
+        current_flow_block = loop_node->start_target;
         if (!EmitStatement(stmt->body)) {
             return false;
         }
@@ -294,7 +293,7 @@
         // The current block didn't `break`, `return` or `continue`, go to the continuing block.
         BranchToIfNeeded(loop_node->continuing_target);
 
-        current_flow_block_ = loop_node->continuing_target;
+        current_flow_block = loop_node->continuing_target;
         if (stmt->continuing) {
             if (!EmitStatement(stmt->continuing)) {
                 return false;
@@ -307,17 +306,17 @@
 
     // 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;
+    current_flow_block = loop_node->merge_target;
     if (!IsConnected(loop_node->merge_target)) {
-        current_flow_block_ = nullptr;
+        current_flow_block = nullptr;
     }
     return true;
 }
 
 bool BuilderImpl::EmitWhile(const ast::WhileStatement* stmt) {
-    auto* loop_node = builder_.CreateLoop(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);
+    builder.Branch(loop_node->continuing_target, loop_node->start_target);
 
     BranchTo(loop_node);
 
@@ -326,19 +325,23 @@
     {
         FlowStackScope scope(this, loop_node);
 
-        current_flow_block_ = loop_node->start_target;
+        current_flow_block = loop_node->start_target;
 
-        // TODO(dsinclair): Emit the instructions for the condition
+        // Emit the while condition into the start target of the loop
+        auto reg = EmitExpression(stmt->condition);
+        if (!reg) {
+            return false;
+        }
 
         // 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
+        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);
+        if_node->condition = reg.Get();
 
         BranchTo(if_node);
 
-        current_flow_block_ = if_node->merge_target;
+        current_flow_block = if_node->merge_target;
         if (!EmitStatement(stmt->body)) {
             return false;
         }
@@ -347,13 +350,13 @@
     }
     // 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;
+    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);
+    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
@@ -369,19 +372,23 @@
     {
         FlowStackScope scope(this, loop_node);
 
-        current_flow_block_ = loop_node->start_target;
+        current_flow_block = loop_node->start_target;
 
         if (stmt->condition) {
-            // TODO(dsinclair): Emit the instructions for the condition
+            // Emit the condition into the target target of the loop
+            auto reg = EmitExpression(stmt->condition);
+            if (!reg) {
+                return false;
+            }
 
             // 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
+            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);
+            if_node->condition = reg.Get();
 
             BranchTo(if_node);
-            current_flow_block_ = if_node->merge_target;
+            current_flow_block = if_node->merge_target;
         }
 
         if (!EmitStatement(stmt->body)) {
@@ -391,7 +398,7 @@
         BranchToIfNeeded(loop_node->continuing_target);
 
         if (stmt->continuing) {
-            current_flow_block_ = loop_node->continuing_target;
+            current_flow_block = loop_node->continuing_target;
             if (!EmitStatement(stmt->continuing)) {
                 return false;
             }
@@ -399,14 +406,19 @@
     }
     // 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;
+    current_flow_block = loop_node->merge_target;
     return true;
 }
 
 bool BuilderImpl::EmitSwitch(const ast::SwitchStatement* stmt) {
-    auto* switch_node = builder_.CreateSwitch(stmt);
+    auto* switch_node = builder.CreateSwitch(stmt);
 
-    // TODO(dsinclair): Emit the condition expression into the current block
+    // Emit the condition into the preceeding block
+    auto reg = EmitExpression(stmt->condition);
+    if (!reg) {
+        return false;
+    }
+    switch_node->condition = reg.Get();
 
     BranchTo(switch_node);
 
@@ -415,31 +427,18 @@
     {
         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)) {
+            current_flow_block = builder.CreateCase(switch_node, c->selectors);
+            if (!EmitStatement(c->body)) {
                 return false;
             }
             BranchToIfNeeded(switch_node->merge_target);
-
-            fallthrough_target_ = nullptr;
         }
     }
-    current_flow_block_ = nullptr;
+    current_flow_block = nullptr;
 
     if (IsConnected(switch_node->merge_target)) {
-        current_flow_block_ = switch_node->merge_target;
+        current_flow_block = switch_node->merge_target;
     }
 
     return true;
@@ -482,9 +481,14 @@
 }
 
 bool BuilderImpl::EmitBreakIf(const ast::BreakIfStatement* stmt) {
-    auto* if_node = builder_.CreateIf(stmt);
+    auto* if_node = builder.CreateIf(stmt);
 
-    // TODO(dsinclair): Emit the condition expression into the current block
+    // Emit the break-if condition into the end of the preceeding block
+    auto reg = EmitExpression(stmt->condition);
+    if (!reg) {
+        return false;
+    }
+    if_node->condition = reg.Get();
 
     BranchTo(if_node);
 
@@ -496,15 +500,13 @@
 
     auto* loop = current_control->As<Loop>();
 
-    // TODO(dsinclair): set if condition register into if flow node
-
-    current_flow_block_ = if_node->true_target;
+    current_flow_block = if_node->true_target;
     BranchTo(loop->merge_target);
 
-    current_flow_block_ = if_node->false_target;
+    current_flow_block = if_node->false_target;
     BranchTo(if_node->merge_target);
 
-    current_flow_block_ = if_node->merge_target;
+    current_flow_block = if_node->merge_target;
 
     // The `break-if` has to be the last item in the continuing block. The false branch of the
     // `break-if` will always take us back to the start of the loop.
@@ -514,10 +516,214 @@
     return true;
 }
 
-bool BuilderImpl::EmitFallthrough() {
-    TINT_ASSERT(IR, fallthrough_target_ != nullptr);
-    BranchTo(fallthrough_target_);
+utils::Result<Register> BuilderImpl::EmitExpression(const ast::Expression* expr) {
+    return tint::Switch(
+        expr,
+        // [&](const ast::IndexAccessorExpression* a) { return EmitIndexAccessor(a); },
+        [&](const ast::BinaryExpression* b) { return EmitBinary(b); },
+        // [&](const ast::BitcastExpression* b) { return EmitBitcast(b); },
+        // [&](const ast::CallExpression* c) { return EmitCall(c); },
+        // [&](const ast::IdentifierExpression* i) { return EmitIdentifier(i); },
+        [&](const ast::LiteralExpression* l) { return EmitLiteral(l); },
+        // [&](const ast::MemberAccessorExpression* m) { return EmitMemberAccessor(m); },
+        // [&](const ast::PhonyExpression*) { return true; },
+        // [&](const ast::UnaryOpExpression* u) { return EmitUnaryOp(u); },
+        [&](Default) {
+            diagnostics_.add_warning(
+                tint::diag::System::IR,
+                "unknown expression type: " + std::string(expr->TypeInfo().name), expr->source);
+            return utils::Failure;
+        });
+}
+
+bool BuilderImpl::EmitVariable(const ast::Variable* var) {
+    return tint::Switch(  //
+        var,
+        // [&](const ast::Var* var) {},
+        // [&](const ast::Let*) {},
+        // [&](const ast::Override*) { },
+        // [&](const ast::Const* c) { },
+        [&](Default) {
+            diagnostics_.add_warning(tint::diag::System::IR,
+                                     "unknown variable: " + std::string(var->TypeInfo().name),
+                                     var->source);
+            return false;
+        });
+}
+
+utils::Result<Register> BuilderImpl::EmitBinary(const ast::BinaryExpression* expr) {
+    auto lhs = EmitExpression(expr->lhs);
+    if (!lhs) {
+        return utils::Failure;
+    }
+
+    auto rhs = EmitExpression(expr->rhs);
+    if (!rhs) {
+        return utils::Failure;
+    }
+
+    Op op;
+    switch (expr->op) {
+        case ast::BinaryOp::kAnd:
+            op = builder.And(lhs.Get(), rhs.Get());
+            break;
+        case ast::BinaryOp::kOr:
+            op = builder.Or(lhs.Get(), rhs.Get());
+            break;
+        case ast::BinaryOp::kXor:
+            op = builder.Xor(lhs.Get(), rhs.Get());
+            break;
+        case ast::BinaryOp::kLogicalAnd:
+            op = builder.LogicalAnd(lhs.Get(), rhs.Get());
+            break;
+        case ast::BinaryOp::kLogicalOr:
+            op = builder.LogicalOr(lhs.Get(), rhs.Get());
+            break;
+        case ast::BinaryOp::kEqual:
+            op = builder.Equal(lhs.Get(), rhs.Get());
+            break;
+        case ast::BinaryOp::kNotEqual:
+            op = builder.NotEqual(lhs.Get(), rhs.Get());
+            break;
+        case ast::BinaryOp::kLessThan:
+            op = builder.LessThan(lhs.Get(), rhs.Get());
+            break;
+        case ast::BinaryOp::kGreaterThan:
+            op = builder.GreaterThan(lhs.Get(), rhs.Get());
+            break;
+        case ast::BinaryOp::kLessThanEqual:
+            op = builder.LessThanEqual(lhs.Get(), rhs.Get());
+            break;
+        case ast::BinaryOp::kGreaterThanEqual:
+            op = builder.GreaterThanEqual(lhs.Get(), rhs.Get());
+            break;
+        case ast::BinaryOp::kShiftLeft:
+            op = builder.ShiftLeft(lhs.Get(), rhs.Get());
+            break;
+        case ast::BinaryOp::kShiftRight:
+            op = builder.ShiftRight(lhs.Get(), rhs.Get());
+            break;
+        case ast::BinaryOp::kAdd:
+            op = builder.Add(lhs.Get(), rhs.Get());
+            break;
+        case ast::BinaryOp::kSubtract:
+            op = builder.Subtract(lhs.Get(), rhs.Get());
+            break;
+        case ast::BinaryOp::kMultiply:
+            op = builder.Multiply(lhs.Get(), rhs.Get());
+            break;
+        case ast::BinaryOp::kDivide:
+            op = builder.Divide(lhs.Get(), rhs.Get());
+            break;
+        case ast::BinaryOp::kModulo:
+            op = builder.Modulo(lhs.Get(), rhs.Get());
+            break;
+        case ast::BinaryOp::kNone:
+            TINT_ICE(IR, diagnostics_) << "missing binary operand type";
+            return utils::Failure;
+    }
+
+    auto result = op.Result();
+    current_flow_block->ops.Push(op);
+    return result;
+}
+
+utils::Result<Register> BuilderImpl::EmitLiteral(const ast::LiteralExpression* lit) {
+    return tint::Switch(  //
+        lit,
+        [&](const ast::BoolLiteralExpression* l) {
+            return utils::Result<Register>{Register(l->value)};
+        },
+        [&](const ast::FloatLiteralExpression* l) {
+            if (l->suffix == ast::FloatLiteralExpression::Suffix::kF) {
+                return utils::Result<Register>{Register(f32(static_cast<float>(l->value)))};
+            }
+            return utils::Result<Register>{Register(f16(static_cast<float>(l->value)))};
+        },
+        [&](const ast::IntLiteralExpression* l) {
+            if (l->suffix == ast::IntLiteralExpression::Suffix::kI) {
+                return utils::Result<Register>{Register(i32(l->value))};
+            }
+            return utils::Result<Register>{Register(u32(l->value))};
+        },
+        [&](Default) {
+            diagnostics_.add_warning(tint::diag::System::IR,
+                                     "unknown literal type: " + std::string(lit->TypeInfo().name),
+                                     lit->source);
+            return utils::Failure;
+        });
+}
+
+bool BuilderImpl::EmitType(const ast::Type* ty) {
+    return tint::Switch(
+        ty,
+        // [&](const ast::Array* ary) { },
+        // [&](const ast::Bool* b) { },
+        // [&](const ast::F32* f) { },
+        // [&](const ast::F16* f) { },
+        // [&](const ast::I32* i) { },
+        // [&](const ast::U32* u) { },
+        // [&](const ast::Vector* v) { },
+        // [&](const ast::Matrix* mat) { },
+        // [&](const ast::Pointer* ptr) { },'
+        // [&](const ast::Atomic* a) { },
+        // [&](const ast::Sampler* s) { },
+        // [&](const ast::ExternalTexture* t) { },
+        // [&](const ast::Texture* t) {
+        //      return tint::Switch(
+        //          t,
+        //          [&](const ast::DepthTexture*) { },
+        //          [&](const ast::DepthMultisampledTexture*) { },
+        //          [&](const ast::SampledTexture*) { },
+        //          [&](const ast::MultisampledTexture*) { },
+        //          [&](const ast::StorageTexture*) {  },
+        //          [&](Default) {
+        //              diagnostics_.add_warning(tint::diag::System::IR,
+        //                  "unknown texture: " + std::string(t->TypeInfo().name), t->source);
+        //              return false;
+        //          });
+        // },
+        // [&](const ast::Void* v) { },
+        // [&](const ast::TypeName* tn) { },
+        [&](Default) {
+            diagnostics_.add_warning(tint::diag::System::IR,
+                                     "unknown type: " + std::string(ty->TypeInfo().name),
+                                     ty->source);
+            return false;
+        });
+}
+
+bool BuilderImpl::EmitAttributes(utils::VectorRef<const ast::Attribute*> attrs) {
+    for (auto* attr : attrs) {
+        if (!EmitAttribute(attr)) {
+            return false;
+        }
+    }
     return true;
 }
 
+bool BuilderImpl::EmitAttribute(const ast::Attribute* attr) {
+    return tint::Switch(  //
+        attr,
+        // [&](const ast::WorkgroupAttribute* wg) {},
+        // [&](const ast::StageAttribute* s) {},
+        // [&](const ast::BindingAttribute* b) {},
+        // [&](const ast::GroupAttribute* g) {},
+        // [&](const ast::LocationAttribute* l) {},
+        // [&](const ast::BuiltinAttribute* b) {},
+        // [&](const ast::InterpolateAttribute* i) {},
+        // [&](const ast::InvariantAttribute* i) {},
+        // [&](const ast::IdAttribute* i) {},
+        // [&](const ast::StructMemberSizeAttribute* s) {},
+        // [&](const ast::StructMemberAlignAttribute* a) {},
+        // [&](const ast::StrideAttribute* s) {}
+        // [&](const ast::InternalAttribute *i) {},
+        [&](Default) {
+            diagnostics_.add_warning(tint::diag::System::IR,
+                                     "unknown attribute: " + std::string(attr->TypeInfo().name),
+                                     attr->source);
+            return false;
+        });
+}
+
 }  // namespace tint::ir
diff --git a/src/tint/ir/builder_impl.h b/src/tint/ir/builder_impl.h
index d6c4bc4..ef5238e 100644
--- a/src/tint/ir/builder_impl.h
+++ b/src/tint/ir/builder_impl.h
@@ -23,6 +23,7 @@
 #include "src/tint/ir/builder.h"
 #include "src/tint/ir/flow_node.h"
 #include "src/tint/ir/module.h"
+#include "src/tint/ir/register.h"
 #include "src/tint/utils/result.h"
 
 // Forward Declarations
@@ -30,6 +31,7 @@
 class Program;
 }  // namespace tint
 namespace tint::ast {
+class BinaryExpression;
 class BlockStatement;
 class BreakIfStatement;
 class BreakStatement;
@@ -38,6 +40,7 @@
 class Function;
 class IfStatement;
 class LoopStatement;
+class LiteralExpression;
 class ReturnStatement;
 class Statement;
 class WhileStatement;
@@ -134,9 +137,40 @@
     /// @returns true if successful, false otherwise.
     bool EmitBreakIf(const ast::BreakIfStatement* stmt);
 
-    /// Emits a fallthrough statement
+    /// Emits an expression
+    /// @param expr the expression to emit
     /// @returns true if successful, false otherwise
-    bool EmitFallthrough();
+    utils::Result<Register> EmitExpression(const ast::Expression* expr);
+
+    /// Emits a variable
+    /// @param var the variable to emit
+    /// @returns true if successful, false otherwise
+    bool EmitVariable(const ast::Variable* var);
+
+    /// Emits a binary expression
+    /// @param expr the binary expression
+    /// @returns the register storing the result if successful, utils::Failure otherwise
+    utils::Result<Register> EmitBinary(const ast::BinaryExpression* expr);
+
+    /// Emits a literal expression
+    /// @param lit the literal to emit
+    /// @returns true if successful, false otherwise
+    utils::Result<Register> EmitLiteral(const ast::LiteralExpression* lit);
+
+    /// Emits a type
+    /// @param ty the type to emit
+    /// @returns true if successful, false otherwise
+    bool EmitType(const ast::Type* ty);
+
+    /// Emits a set of attributes
+    /// @param attrs the attributes to emit
+    /// @returns true if successful, false otherwise
+    bool EmitAttributes(utils::VectorRef<const ast::Attribute*> attrs);
+
+    /// Emits an attribute
+    /// @param attr the attribute to emit
+    /// @returns true if successful, false otherwise
+    bool EmitAttribute(const ast::Attribute* attr);
 
     /// Retrieve the IR Flow node for a given AST node.
     /// @param n the node to lookup
@@ -151,6 +185,12 @@
     /// The stack of flow control blocks.
     utils::Vector<FlowNode*, 8> flow_stack;
 
+    /// The IR builder being used by the impl.
+    Builder builder;
+
+    /// The current flow block for expressions
+    Block* current_flow_block = nullptr;
+
   private:
     enum class ControlFlags { kNone, kExcludeSwitch };
 
@@ -159,16 +199,10 @@
 
     FlowNode* FindEnclosingControl(ControlFlags flags);
 
-    Builder builder_;
-
     diag::List diagnostics_;
 
-    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 1ee66e5..4a1efd5 100644
--- a/src/tint/ir/builder_impl_test.cc
+++ b/src/tint/ir/builder_impl_test.cc
@@ -22,13 +22,13 @@
 
 using namespace tint::number_suffixes;  // NOLINT
 
-using IRBuilderImplTest = TestHelper;
+using IR_BuilderImplTest = TestHelper;
 
-TEST_F(IRBuilderImplTest, Func) {
+TEST_F(IR_BuilderImplTest, Func) {
     // func -> start -> end
 
     Func("f", utils::Empty, ty.void_(), utils::Empty);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -47,10 +47,10 @@
     EXPECT_EQ(f->start_target->branch_target, f->end_target);
 }
 
-TEST_F(IRBuilderImplTest, EntryPoint) {
+TEST_F(IR_BuilderImplTest, EntryPoint) {
     Func("f", utils::Empty, ty.void_(), utils::Empty,
          utils::Vector{Stage(ast::PipelineStage::kFragment)});
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -60,7 +60,7 @@
     EXPECT_EQ(m.functions[0], m.entry_points[0]);
 }
 
-TEST_F(IRBuilderImplTest, IfStatement) {
+TEST_F(IR_BuilderImplTest, IfStatement) {
     // func -> start -> if -> true block
     //                     -> false block
     //
@@ -70,7 +70,7 @@
     //
     auto* ast_if = If(true, Block(), Else(Block()));
     WrapInFunction(ast_if);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -80,8 +80,6 @@
     ASSERT_NE(ir_if, nullptr);
     EXPECT_TRUE(ir_if->Is<ir::If>());
 
-    // TODO(dsinclair): check condition
-
     auto* flow = ir_if->As<ir::If>();
     ASSERT_NE(flow->true_target, nullptr);
     ASSERT_NE(flow->false_target, nullptr);
@@ -101,9 +99,14 @@
     EXPECT_EQ(flow->true_target->branch_target, flow->merge_target);
     EXPECT_EQ(flow->false_target->branch_target, flow->merge_target);
     EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
+
+    // Check condition
+    auto op = flow->condition;
+    ASSERT_TRUE(op.IsBool());
+    EXPECT_TRUE(op.AsBool());
 }
 
-TEST_F(IRBuilderImplTest, IfStatement_TrueReturns) {
+TEST_F(IR_BuilderImplTest, IfStatement_TrueReturns) {
     // func -> start -> if -> true block
     //                     -> false block
     //
@@ -113,7 +116,7 @@
     //
     auto* ast_if = If(true, Block(Return()));
     WrapInFunction(ast_if);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -144,7 +147,7 @@
     EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
 }
 
-TEST_F(IRBuilderImplTest, IfStatement_FalseReturns) {
+TEST_F(IR_BuilderImplTest, IfStatement_FalseReturns) {
     // func -> start -> if -> true block
     //                     -> false block
     //
@@ -154,7 +157,7 @@
     //
     auto* ast_if = If(true, Block(), Else(Block(Return())));
     WrapInFunction(ast_if);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -185,7 +188,7 @@
     EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
 }
 
-TEST_F(IRBuilderImplTest, IfStatement_BothReturn) {
+TEST_F(IR_BuilderImplTest, IfStatement_BothReturn) {
     // func -> start -> if -> true block
     //                     -> false block
     //
@@ -195,7 +198,7 @@
     //
     auto* ast_if = If(true, Block(Return()), Else(Block(Return())));
     WrapInFunction(ast_if);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -225,7 +228,7 @@
     EXPECT_EQ(flow->false_target->branch_target, func->end_target);
 }
 
-TEST_F(IRBuilderImplTest, IfStatement_JumpChainToMerge) {
+TEST_F(IR_BuilderImplTest, IfStatement_JumpChainToMerge) {
     // if (true) {
     //   loop {
     //     break;
@@ -246,7 +249,7 @@
     auto* ast_loop = Loop(Block(Break()));
     auto* ast_if = If(true, Block(ast_loop));
     WrapInFunction(ast_if);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -282,14 +285,14 @@
     EXPECT_EQ(if_flow->merge_target->branch_target, func->end_target);
 }
 
-TEST_F(IRBuilderImplTest, Loop_WithBreak) {
+TEST_F(IR_BuilderImplTest, Loop_WithBreak) {
     // func -> start -> loop -> loop start -> loop merge -> func end
     //
     //   [continuing] -> loop start
     //
     auto* ast_loop = Loop(Block(Break()));
     WrapInFunction(ast_loop);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -320,7 +323,7 @@
     EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
 }
 
-TEST_F(IRBuilderImplTest, Loop_WithContinue) {
+TEST_F(IR_BuilderImplTest, Loop_WithContinue) {
     // func -> start -> loop -> loop start -> if -> true block
     //                                           -> false block
     //
@@ -333,7 +336,7 @@
     auto* ast_if = If(true, Block(Break()));
     auto* ast_loop = Loop(Block(ast_if, Continue()));
     WrapInFunction(ast_loop);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -379,7 +382,7 @@
     EXPECT_EQ(loop_flow->merge_target->branch_target, func->end_target);
 }
 
-TEST_F(IRBuilderImplTest, Loop_WithContinuing_BreakIf) {
+TEST_F(IR_BuilderImplTest, Loop_WithContinuing_BreakIf) {
     // func -> start -> loop -> loop start -> continuing
     //
     //   [loop continuing] -> if -> true branch
@@ -392,7 +395,7 @@
     auto* ast_break_if = BreakIf(true);
     auto* ast_loop = Loop(Block(), Block(ast_break_if));
     WrapInFunction(ast_loop);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -439,7 +442,7 @@
     EXPECT_EQ(loop_flow->merge_target->branch_target, func->end_target);
 }
 
-TEST_F(IRBuilderImplTest, Loop_WithReturn) {
+TEST_F(IR_BuilderImplTest, Loop_WithReturn) {
     // func -> start -> loop -> loop start -> if -> true block
     //                                           -> false block
     //
@@ -452,7 +455,7 @@
     auto* ast_if = If(true, Block(Return()));
     auto* ast_loop = Loop(Block(ast_if, Continue()));
     WrapInFunction(ast_loop);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -497,9 +500,14 @@
 
     EXPECT_EQ(func->start_target->branch_target, ir_loop);
     EXPECT_EQ(loop_flow->merge_target->branch_target, nullptr);
+
+    // Check condition
+    auto op = if_flow->condition;
+    ASSERT_TRUE(op.IsBool());
+    EXPECT_TRUE(op.AsBool());
 }
 
-TEST_F(IRBuilderImplTest, Loop_WithOnlyReturn) {
+TEST_F(IR_BuilderImplTest, Loop_WithOnlyReturn) {
     // {
     //   loop {
     //     return;
@@ -520,7 +528,7 @@
     // block.
     auto* ast_loop = Loop(Block(Return(), Continue()));
     WrapInFunction(ast_loop, If(true, Block(Return())));
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -551,7 +559,7 @@
     EXPECT_EQ(func->start_target->branch_target, ir_loop);
 }
 
-TEST_F(IRBuilderImplTest, Loop_WithOnlyReturn_ContinuingBreakIf) {
+TEST_F(IR_BuilderImplTest, Loop_WithOnlyReturn_ContinuingBreakIf) {
     // {
     //   loop {
     //     return;
@@ -578,7 +586,7 @@
     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& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -622,7 +630,7 @@
     EXPECT_EQ(func->start_target->branch_target, ir_loop);
 }
 
-TEST_F(IRBuilderImplTest, Loop_WithIf_BothBranchesBreak) {
+TEST_F(IR_BuilderImplTest, Loop_WithIf_BothBranchesBreak) {
     // func -> start -> loop -> loop start -> if -> true branch
     //                                           -> false branch
     //
@@ -635,7 +643,7 @@
     auto* ast_if = If(true, Block(Break()), Else(Block(Break())));
     auto* ast_loop = Loop(Block(ast_if, Continue()));
     WrapInFunction(ast_loop);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -684,7 +692,7 @@
     EXPECT_EQ(loop_flow->merge_target->branch_target, func->end_target);
 }
 
-TEST_F(IRBuilderImplTest, Loop_Nested) {
+TEST_F(IR_BuilderImplTest, Loop_Nested) {
     // loop {   // loop_a
     //   loop {  // loop_b
     //      if (true) { break; }  // if_a
@@ -744,7 +752,7 @@
     auto* ast_loop_a = Loop(Block(ast_loop_b, ast_if_d));
 
     WrapInFunction(ast_loop_a);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -879,7 +887,7 @@
     EXPECT_EQ(loop_flow_a->merge_target->branch_target, func->end_target);
 }
 
-TEST_F(IRBuilderImplTest, While) {
+TEST_F(IR_BuilderImplTest, While) {
     // {
     //   while false {
     //   }
@@ -896,7 +904,7 @@
     //
     auto* ast_while = While(false, Block());
     WrapInFunction(ast_while);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -937,9 +945,14 @@
     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);
+
+    // Check condition
+    auto op = if_flow->condition;
+    ASSERT_TRUE(op.IsBool());
+    EXPECT_FALSE(op.AsBool());
 }
 
-TEST_F(IRBuilderImplTest, While_Return) {
+TEST_F(IR_BuilderImplTest, While_Return) {
     // {
     //   while true {
     //     return;
@@ -956,7 +969,7 @@
     //
     auto* ast_while = While(true, Block(Return()));
     WrapInFunction(ast_while);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -1000,7 +1013,7 @@
 }
 
 // TODO(dsinclair): Enable when variable declarations and increment are supported
-TEST_F(IRBuilderImplTest, DISABLED_For) {
+TEST_F(IR_BuilderImplTest, DISABLED_For) {
     // for(var i: 0; i < 10; i++) {
     // }
     //
@@ -1015,7 +1028,7 @@
     //
     auto* ast_for = For(Decl(Var("i", ty.i32())), LessThan("i", 10_a), Increment("i"), Block());
     WrapInFunction(ast_for);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -1056,9 +1069,14 @@
     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);
+
+    // Check condition
+    auto op = if_flow->condition;
+    ASSERT_TRUE(op.IsBool());
+    EXPECT_FALSE(op.AsBool());
 }
 
-TEST_F(IRBuilderImplTest, For_NoInitCondOrContinuing) {
+TEST_F(IR_BuilderImplTest, For_NoInitCondOrContinuing) {
     // for (;;) {
     //   break;
     // }
@@ -1067,7 +1085,7 @@
     //
     auto* ast_for = For(nullptr, nullptr, nullptr, Block(Break()));
     WrapInFunction(ast_for);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -1096,7 +1114,7 @@
     EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
 }
 
-TEST_F(IRBuilderImplTest, Switch) {
+TEST_F(IR_BuilderImplTest, Switch) {
     // func -> switch -> case 1
     //                -> case 2
     //                -> default
@@ -1111,7 +1129,7 @@
                            Case(utils::Vector{CaseSelector(1_i)}, Block()), DefaultCase(Block())});
 
     WrapInFunction(ast_switch);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -1151,15 +1169,20 @@
     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);
+
+    // Check condition
+    auto op = flow->condition;
+    ASSERT_TRUE(op.IsI32());
+    EXPECT_EQ(1_i, op.AsI32());
 }
 
-TEST_F(IRBuilderImplTest, Switch_OnlyDefault) {
+TEST_F(IR_BuilderImplTest, 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& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -1189,7 +1212,7 @@
     EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
 }
 
-TEST_F(IRBuilderImplTest, Switch_WithBreak) {
+TEST_F(IR_BuilderImplTest, Switch_WithBreak) {
     // {
     //   switch(1) {
     //     case 0: {
@@ -1211,7 +1234,7 @@
                                                  DefaultCase(Block())});
 
     WrapInFunction(ast_switch);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -1248,7 +1271,7 @@
     EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
 }
 
-TEST_F(IRBuilderImplTest, Switch_AllReturn) {
+TEST_F(IR_BuilderImplTest, Switch_AllReturn) {
     // {
     //   switch(1) {
     //     case 0: {
@@ -1275,7 +1298,7 @@
     auto* ast_if = If(true, Block(Return()));
 
     WrapInFunction(ast_switch, ast_if);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -1313,63 +1336,280 @@
     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())});
+TEST_F(IR_BuilderImplTest, EmitLiteral_Bool_True) {
+    auto& b = CreateEmptyBuilder();
+    auto r = b.EmitLiteral(Expr(true));
+    ASSERT_TRUE(r);
 
-    WrapInFunction(ast_switch);
-    auto& b = Build();
+    auto reg = r.Get();
+    EXPECT_TRUE(reg.IsBool());
+    EXPECT_TRUE(reg.AsBool());
+}
 
-    auto r = b.Build();
-    ASSERT_TRUE(r) << b.error();
-    auto m = r.Move();
+TEST_F(IR_BuilderImplTest, EmitLiteral_Bool_False) {
+    auto& b = CreateEmptyBuilder();
+    auto r = b.EmitLiteral(Expr(false));
+    ASSERT_TRUE(r);
 
-    auto* ir_switch = b.FlowNodeForAstNode(ast_switch);
-    ASSERT_NE(ir_switch, nullptr);
-    ASSERT_TRUE(ir_switch->Is<ir::Switch>());
+    auto reg = r.Get();
+    EXPECT_TRUE(reg.IsBool());
+    EXPECT_FALSE(reg.AsBool());
+}
 
-    auto* flow = ir_switch->As<ir::Switch>();
-    ASSERT_NE(flow->merge_target, nullptr);
-    ASSERT_EQ(3u, flow->cases.Length());
+TEST_F(IR_BuilderImplTest, EmitLiteral_F32) {
+    auto& b = CreateEmptyBuilder();
+    auto r = b.EmitLiteral(Expr(1.2_f));
+    ASSERT_TRUE(r);
 
-    ASSERT_EQ(1u, m.functions.Length());
-    auto* func = m.functions[0];
+    auto reg = r.Get();
+    EXPECT_TRUE(reg.IsF32());
+    EXPECT_EQ(1.2_f, reg.AsF32());
+}
 
-    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);
+TEST_F(IR_BuilderImplTest, EmitLiteral_F16) {
+    auto& b = CreateEmptyBuilder();
+    auto r = b.EmitLiteral(Expr(1.2_h));
+    ASSERT_TRUE(r);
 
-    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);
+    auto reg = r.Get();
+    EXPECT_TRUE(reg.IsF16());
+    EXPECT_EQ(1.2_h, reg.AsF16());
+}
 
-    ASSERT_EQ(1u, flow->cases[2].selectors.Length());
-    EXPECT_TRUE(flow->cases[2].selectors[0]->IsDefault());
+TEST_F(IR_BuilderImplTest, EmitLiteral_I32) {
+    auto& b = CreateEmptyBuilder();
+    auto r = b.EmitLiteral(Expr(-2_i));
+    ASSERT_TRUE(r);
 
-    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());
+    auto reg = r.Get();
+    EXPECT_TRUE(reg.IsI32());
+    EXPECT_EQ(-2_i, reg.AsI32());
+}
 
-    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);
+TEST_F(IR_BuilderImplTest, EmitLiteral_U32) {
+    auto& b = CreateEmptyBuilder();
+    auto r = b.EmitLiteral(Expr(2_u));
+    ASSERT_TRUE(r);
+
+    auto reg = r.Get();
+    EXPECT_TRUE(reg.IsU32());
+    EXPECT_EQ(2_u, reg.AsU32());
+}
+
+TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Add) {
+    auto& b = CreateEmptyBuilder();
+    auto r = b.EmitExpression(Add(3_u, 4_u));
+    ASSERT_TRUE(r);
+
+    Disassembler d;
+    d.EmitBlockOps(b.current_flow_block);
+    EXPECT_EQ(d.AsString(), R"(%1 = 3 + 4
+)");
+}
+
+TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Subtract) {
+    auto& b = CreateEmptyBuilder();
+    auto r = b.EmitExpression(Sub(3_u, 4_u));
+    ASSERT_TRUE(r);
+
+    Disassembler d;
+    d.EmitBlockOps(b.current_flow_block);
+    EXPECT_EQ(d.AsString(), R"(%1 = 3 - 4
+)");
+}
+
+TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Multiply) {
+    auto& b = CreateEmptyBuilder();
+    auto r = b.EmitExpression(Mul(3_u, 4_u));
+    ASSERT_TRUE(r);
+
+    Disassembler d;
+    d.EmitBlockOps(b.current_flow_block);
+    EXPECT_EQ(d.AsString(), R"(%1 = 3 * 4
+)");
+}
+
+TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Div) {
+    auto& b = CreateEmptyBuilder();
+    auto r = b.EmitExpression(Div(3_u, 4_u));
+    ASSERT_TRUE(r);
+
+    Disassembler d;
+    d.EmitBlockOps(b.current_flow_block);
+    EXPECT_EQ(d.AsString(), R"(%1 = 3 / 4
+)");
+}
+
+TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Modulo) {
+    auto& b = CreateEmptyBuilder();
+    auto r = b.EmitExpression(Mod(3_u, 4_u));
+    ASSERT_TRUE(r);
+
+    Disassembler d;
+    d.EmitBlockOps(b.current_flow_block);
+    EXPECT_EQ(d.AsString(), R"(%1 = 3 % 4
+)");
+}
+
+TEST_F(IR_BuilderImplTest, EmitExpression_Binary_And) {
+    auto& b = CreateEmptyBuilder();
+    auto r = b.EmitExpression(And(3_u, 4_u));
+    ASSERT_TRUE(r);
+
+    Disassembler d;
+    d.EmitBlockOps(b.current_flow_block);
+    EXPECT_EQ(d.AsString(), R"(%1 = 3 & 4
+)");
+}
+
+TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Or) {
+    auto& b = CreateEmptyBuilder();
+    auto r = b.EmitExpression(Or(3_u, 4_u));
+    ASSERT_TRUE(r);
+
+    Disassembler d;
+    d.EmitBlockOps(b.current_flow_block);
+    EXPECT_EQ(d.AsString(), R"(%1 = 3 | 4
+)");
+}
+
+TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Xor) {
+    auto& b = CreateEmptyBuilder();
+    auto r = b.EmitExpression(Xor(3_u, 4_u));
+    ASSERT_TRUE(r);
+
+    Disassembler d;
+    d.EmitBlockOps(b.current_flow_block);
+    EXPECT_EQ(d.AsString(), R"(%1 = 3 ^ 4
+)");
+}
+
+TEST_F(IR_BuilderImplTest, EmitExpression_Binary_LogicalAnd) {
+    auto& b = CreateEmptyBuilder();
+    auto r = b.EmitExpression(LogicalAnd(3_u, 4_u));
+    ASSERT_TRUE(r);
+
+    Disassembler d;
+    d.EmitBlockOps(b.current_flow_block);
+    EXPECT_EQ(d.AsString(), R"(%1 = 3 && 4
+)");
+}
+
+TEST_F(IR_BuilderImplTest, EmitExpression_Binary_LogicalOr) {
+    auto& b = CreateEmptyBuilder();
+    auto r = b.EmitExpression(LogicalOr(3_u, 4_u));
+    ASSERT_TRUE(r);
+
+    Disassembler d;
+    d.EmitBlockOps(b.current_flow_block);
+    EXPECT_EQ(d.AsString(), R"(%1 = 3 || 4
+)");
+}
+
+TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Eqaul) {
+    auto& b = CreateEmptyBuilder();
+    auto r = b.EmitExpression(Equal(3_u, 4_u));
+    ASSERT_TRUE(r);
+
+    Disassembler d;
+    d.EmitBlockOps(b.current_flow_block);
+    EXPECT_EQ(d.AsString(), R"(%1 = 3 == 4
+)");
+}
+
+TEST_F(IR_BuilderImplTest, EmitExpression_Binary_NotEqual) {
+    auto& b = CreateEmptyBuilder();
+    auto r = b.EmitExpression(NotEqual(3_u, 4_u));
+    ASSERT_TRUE(r);
+
+    Disassembler d;
+    d.EmitBlockOps(b.current_flow_block);
+    EXPECT_EQ(d.AsString(), R"(%1 = 3 != 4
+)");
+}
+
+TEST_F(IR_BuilderImplTest, EmitExpression_Binary_LessThan) {
+    auto& b = CreateEmptyBuilder();
+    auto r = b.EmitExpression(LessThan(3_u, 4_u));
+    ASSERT_TRUE(r);
+
+    Disassembler d;
+    d.EmitBlockOps(b.current_flow_block);
+    EXPECT_EQ(d.AsString(), R"(%1 = 3 < 4
+)");
+}
+
+TEST_F(IR_BuilderImplTest, EmitExpression_Binary_GreaterThan) {
+    auto& b = CreateEmptyBuilder();
+    auto r = b.EmitExpression(GreaterThan(3_u, 4_u));
+    ASSERT_TRUE(r);
+
+    Disassembler d;
+    d.EmitBlockOps(b.current_flow_block);
+    EXPECT_EQ(d.AsString(), R"(%1 = 3 > 4
+)");
+}
+
+TEST_F(IR_BuilderImplTest, EmitExpression_Binary_LessThanEqual) {
+    auto& b = CreateEmptyBuilder();
+    auto r = b.EmitExpression(LessThanEqual(3_u, 4_u));
+    ASSERT_TRUE(r);
+
+    Disassembler d;
+    d.EmitBlockOps(b.current_flow_block);
+    EXPECT_EQ(d.AsString(), R"(%1 = 3 <= 4
+)");
+}
+
+TEST_F(IR_BuilderImplTest, EmitExpression_Binary_GreaterThanEqual) {
+    auto& b = CreateEmptyBuilder();
+    auto r = b.EmitExpression(GreaterThanEqual(3_u, 4_u));
+    ASSERT_TRUE(r);
+
+    Disassembler d;
+    d.EmitBlockOps(b.current_flow_block);
+    EXPECT_EQ(d.AsString(), R"(%1 = 3 >= 4
+)");
+}
+
+TEST_F(IR_BuilderImplTest, EmitExpression_Binary_ShiftLeft) {
+    auto& b = CreateEmptyBuilder();
+    auto r = b.EmitExpression(Shl(3_u, 4_u));
+    ASSERT_TRUE(r);
+
+    Disassembler d;
+    d.EmitBlockOps(b.current_flow_block);
+    EXPECT_EQ(d.AsString(), R"(%1 = 3 << 4
+)");
+}
+
+TEST_F(IR_BuilderImplTest, EmitExpression_Binary_ShiftRight) {
+    auto& b = CreateEmptyBuilder();
+    auto r = b.EmitExpression(Shr(3_u, 4_u));
+    ASSERT_TRUE(r);
+
+    Disassembler d;
+    d.EmitBlockOps(b.current_flow_block);
+    EXPECT_EQ(d.AsString(), R"(%1 = 3 >> 4
+)");
+}
+
+TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Compound) {
+    auto& b = CreateEmptyBuilder();
+    auto r = b.EmitExpression(LogicalOr(  //
+        LessThan(1_u, Add(Shr(3_u, 4_u), 9_u)), GreaterThan(2.5_f, Div(6.7_f, Mul(2.3_f, 5.5_f)))));
+    ASSERT_TRUE(r);
+
+    Disassembler d;
+    d.EmitBlockOps(b.current_flow_block);
+    EXPECT_EQ(d.AsString(), R"(%1 = 3 >> 4
+%2 = %1 + 9
+%3 = 1 < %2
+%4 = 2.300000 * 5.500000
+%5 = 6.700000 / %4
+%6 = 2.500000 > %5
+%7 = %3 || %6
+)");
 }
 
 }  // namespace
diff --git a/src/tint/ir/debug.h b/src/tint/ir/debug.h
index bd0570b..2363776 100644
--- a/src/tint/ir/debug.h
+++ b/src/tint/ir/debug.h
@@ -28,6 +28,11 @@
     /// @param mod the module to emit
     /// @returns the dot graph for the given module
     static std::string AsDotGraph(const Module* mod);
+
+    /// Returns the module as a string
+    /// @param mod the module to emit
+    /// @returns the string representation of the module
+    static std::string AsString(const Module* mod);
 };
 
 }  // namespace tint::ir
diff --git a/src/tint/ir/disassembler.cc b/src/tint/ir/disassembler.cc
new file mode 100644
index 0000000..88e2e69
--- /dev/null
+++ b/src/tint/ir/disassembler.cc
@@ -0,0 +1,154 @@
+// 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/disassembler.h"
+
+#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 {
+namespace {
+
+class ScopedStopNode {
+  public:
+    ScopedStopNode(std::unordered_set<const FlowNode*>* stop_nodes_, const FlowNode* node)
+        : stop_nodes__(stop_nodes_), node_(node) {
+        stop_nodes__->insert(node_);
+    }
+
+    ~ScopedStopNode() { stop_nodes__->erase(node_); }
+
+  private:
+    std::unordered_set<const FlowNode*>* stop_nodes__;
+    const FlowNode* node_;
+};
+
+class ScopedIndent {
+  public:
+    explicit ScopedIndent(uint32_t* indent) : indent_(indent) { (*indent_) += 2; }
+
+    ~ScopedIndent() { (*indent_) -= 2; }
+
+  private:
+    uint32_t* indent_;
+};
+
+}  // namespace
+
+Disassembler::Disassembler() = default;
+
+Disassembler::~Disassembler() = default;
+
+std::ostream& Disassembler::Indent() {
+    for (uint32_t i = 0; i < indent_size_; i++) {
+        out_ << " ";
+    }
+    return out_;
+}
+
+void Disassembler::EmitBlockOps(const Block* b) {
+    for (const auto& op : b->ops) {
+        out_ << op << std::endl;
+    }
+}
+
+void Disassembler::Walk(const FlowNode* node) {
+    if ((visited_.count(node) > 0) || (stop_nodes_.count(node) > 0)) {
+        return;
+    }
+    visited_.insert(node);
+
+    tint::Switch(
+        node,
+        [&](const ir::Function* f) {
+            Indent() << "Function" << std::endl;
+
+            {
+                ScopedIndent func_indent(&indent_size_);
+                ScopedStopNode scope(&stop_nodes_, f->end_target);
+                Walk(f->start_target);
+            }
+            Walk(f->end_target);
+        },
+        [&](const ir::Block* b) {
+            Indent() << "Block" << std::endl;
+            EmitBlockOps(b);
+            Walk(b->branch_target);
+        },
+        [&](const ir::Switch* s) {
+            Indent() << "Switch (" << s->condition << ")" << std::endl;
+
+            {
+                ScopedIndent switch_indent(&indent_size_);
+                ScopedStopNode scope(&stop_nodes_, s->merge_target);
+                for (const auto& c : s->cases) {
+                    Indent() << "Case" << std::endl;
+                    ScopedIndent case_indent(&indent_size_);
+                    Walk(c.start_target);
+                }
+            }
+
+            Indent() << "Switch Merge" << std::endl;
+            Walk(s->merge_target);
+        },
+        [&](const ir::If* i) {
+            Indent() << "if (" << i->condition << ")" << std::endl;
+            {
+                ScopedIndent if_indent(&indent_size_);
+                ScopedStopNode scope(&stop_nodes_, i->merge_target);
+
+                Indent() << "true branch" << std::endl;
+                Walk(i->true_target);
+
+                Indent() << "false branch" << std::endl;
+                Walk(i->false_target);
+            }
+
+            Indent() << "if merge" << std::endl;
+            Walk(i->merge_target);
+        },
+        [&](const ir::Loop* l) {
+            Indent() << "loop" << std::endl;
+            {
+                ScopedStopNode loop_scope(&stop_nodes_, l->merge_target);
+                ScopedIndent loop_indent(&indent_size_);
+                {
+                    ScopedStopNode inner_scope(&stop_nodes_, l->continuing_target);
+                    Indent() << "loop start" << std::endl;
+                    Walk(l->start_target);
+                }
+
+                Indent() << "loop continuing" << std::endl;
+                ScopedIndent continuing_indent(&indent_size_);
+                Walk(l->continuing_target);
+            }
+
+            Indent() << "loop merge" << std::endl;
+            Walk(l->merge_target);
+        },
+        [&](const ir::Terminator*) { Indent() << "Function end" << std::endl; });
+}
+
+std::string Disassembler::Disassemble(const Module& mod) {
+    for (const auto* func : mod.functions) {
+        Walk(func);
+    }
+    return out_.str();
+}
+
+}  // namespace tint::ir
diff --git a/src/tint/ir/disassembler.h b/src/tint/ir/disassembler.h
new file mode 100644
index 0000000..9cb8993
--- /dev/null
+++ b/src/tint/ir/disassembler.h
@@ -0,0 +1,58 @@
+// 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_DISASSEMBLER_H_
+#define SRC_TINT_IR_DISASSEMBLER_H_
+
+#include <sstream>
+#include <string>
+#include <unordered_set>
+
+#include "src/tint/ir/flow_node.h"
+#include "src/tint/ir/module.h"
+
+namespace tint::ir {
+
+/// Helper class to disassemble the IR
+class Disassembler {
+  public:
+    /// Constructor
+    Disassembler();
+    ~Disassembler();
+
+    /// Returns the module as a string
+    /// @param mod the module to emit
+    /// @returns the string representation of the module
+    std::string Disassemble(const Module& mod);
+
+    /// Writes the block ops to the stream
+    /// @param b the block containing the ops
+    void EmitBlockOps(const Block* b);
+
+    /// @returns the string representation
+    std::string AsString() const { return out_.str(); }
+
+  private:
+    std::ostream& Indent();
+    void Walk(const FlowNode* node);
+
+    std::stringstream out_;
+    std::unordered_set<const FlowNode*> visited_;
+    std::unordered_set<const FlowNode*> stop_nodes_;
+    uint32_t indent_size_ = 0;
+};
+
+}  // namespace tint::ir
+
+#endif  // SRC_TINT_IR_DISASSEMBLER_H_
diff --git a/src/tint/ir/if.h b/src/tint/ir/if.h
index 2d28aa1..109b990 100644
--- a/src/tint/ir/if.h
+++ b/src/tint/ir/if.h
@@ -17,6 +17,7 @@
 
 #include "src/tint/ast/if_statement.h"
 #include "src/tint/ir/flow_node.h"
+#include "src/tint/ir/register.h"
 
 // Forward declarations
 namespace tint::ir {
@@ -43,6 +44,8 @@
     /// 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;
+    /// Register holding the condition result
+    Register condition;
 };
 
 }  // namespace tint::ir
diff --git a/src/tint/ir/op.cc b/src/tint/ir/op.cc
new file mode 100644
index 0000000..3e6d993
--- /dev/null
+++ b/src/tint/ir/op.cc
@@ -0,0 +1,105 @@
+// 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/op.h"
+
+namespace tint::ir {
+
+Op::Op() {}
+
+Op::Op(Kind kind, Register result, Register lhs, Register rhs)
+    : kind_(kind), result_(result), args_({lhs, rhs}) {}
+
+Op::Op(const Op&) = default;
+
+Op::Op(Op&& o) = default;
+
+Op::~Op() = default;
+
+Op& Op::operator=(const Op& o) = default;
+
+Op& Op::operator=(Op&& o) = default;
+
+std::ostream& operator<<(std::ostream& out, const Op& op) {
+    out << op.Result() << " = ";
+    if (op.HasLHS()) {
+        out << op.LHS();
+    }
+    out << " ";
+
+    switch (op.GetKind()) {
+        case Op::Kind::kAdd:
+            out << "+";
+            break;
+        case Op::Kind::kSubtract:
+            out << "-";
+            break;
+        case Op::Kind::kMultiply:
+            out << "*";
+            break;
+        case Op::Kind::kDivide:
+            out << "/";
+            break;
+        case Op::Kind::kModulo:
+            out << "%";
+            break;
+        case Op::Kind::kAnd:
+            out << "&";
+            break;
+        case Op::Kind::kOr:
+            out << "|";
+            break;
+        case Op::Kind::kXor:
+            out << "^";
+            break;
+        case Op::Kind::kLogicalAnd:
+            out << "&&";
+            break;
+        case Op::Kind::kLogicalOr:
+            out << "||";
+            break;
+        case Op::Kind::kEqual:
+            out << "==";
+            break;
+        case Op::Kind::kNotEqual:
+            out << "!=";
+            break;
+        case Op::Kind::kLessThan:
+            out << "<";
+            break;
+        case Op::Kind::kGreaterThan:
+            out << ">";
+            break;
+        case Op::Kind::kLessThanEqual:
+            out << "<=";
+            break;
+        case Op::Kind::kGreaterThanEqual:
+            out << ">=";
+            break;
+        case Op::Kind::kShiftLeft:
+            out << "<<";
+            break;
+        case Op::Kind::kShiftRight:
+            out << ">>";
+            break;
+    }
+
+    if (op.HasRHS()) {
+        out << " " << op.RHS();
+    }
+
+    return out;
+}
+
+}  // namespace tint::ir
diff --git a/src/tint/ir/op.h b/src/tint/ir/op.h
new file mode 100644
index 0000000..a9fc78f
--- /dev/null
+++ b/src/tint/ir/op.h
@@ -0,0 +1,113 @@
+// 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_OP_H_
+#define SRC_TINT_IR_OP_H_
+
+#include <ostream>
+
+#include "src/tint/ir/register.h"
+#include "src/tint/utils/vector.h"
+
+namespace tint::ir {
+
+/// An operation in the IR.
+class Op {
+  public:
+    /// The kind of operation.
+    enum class Kind {
+        kAdd,
+        kSubtract,
+        kMultiply,
+        kDivide,
+        kModulo,
+
+        kAnd,
+        kOr,
+        kXor,
+
+        kLogicalAnd,
+        kLogicalOr,
+
+        kEqual,
+        kNotEqual,
+        kLessThan,
+        kGreaterThan,
+        kLessThanEqual,
+        kGreaterThanEqual,
+
+        kShiftLeft,
+        kShiftRight
+    };
+
+    /// Constructor
+    Op();
+    /// Constructor
+    /// @param kind the kind of operation
+    /// @param result the result register
+    /// @param lhs the lhs of the operation
+    /// @param rhs the rhs of the operation
+    Op(Kind kind, Register result, Register lhs, Register rhs);
+    /// Copy constructor
+    /// @param o the op to copy from
+    Op(const Op& o);
+    /// Move constructor
+    /// @param o the op to move from
+    Op(Op&& o);
+    /// Destructor
+    ~Op();
+
+    /// Copy assign
+    /// @param o the op to copy from
+    /// @returns a reference to this
+    Op& operator=(const Op& o);
+    /// Move assign
+    /// @param o the op to move from
+    /// @returns a reference to this
+    Op& operator=(Op&& o);
+
+    /// @returns the kind of operation
+    Kind GetKind() const { return kind_; }
+
+    /// @returns the result register for the operation
+    const Register& Result() const { return result_; }
+
+    /// @returns true if the op has a LHS
+    bool HasLHS() const { return args_.Length() >= 1; }
+    /// @returns the left-hand-side register for the operation
+    const Register& LHS() const {
+        TINT_ASSERT(IR, HasLHS());
+        return args_[0];
+    }
+
+    /// @returns true if the op has a RHS
+    bool HasRHS() const { return args_.Length() >= 2; }
+    /// @returns the right-hand-side register for the operation
+    const Register& RHS() const {
+        TINT_ASSERT(IR, HasRHS());
+        return args_[1];
+    }
+
+  private:
+    Kind kind_;
+
+    Register result_;
+    utils::Vector<Register, 2> args_;
+};
+
+std::ostream& operator<<(std::ostream& out, const Op&);
+
+}  // namespace tint::ir
+
+#endif  // SRC_TINT_IR_OP_H_
diff --git a/src/tint/ir/op_test.cc b/src/tint/ir/op_test.cc
new file mode 100644
index 0000000..01604d1
--- /dev/null
+++ b/src/tint/ir/op_test.cc
@@ -0,0 +1,494 @@
+// 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 <sstream>
+
+#include "src/tint/ir/op.h"
+#include "src/tint/ir/test_helper.h"
+
+namespace tint::ir {
+namespace {
+
+using IR_OpTest = TestHelper;
+
+TEST_F(IR_OpTest, CreateAnd) {
+    auto& b = CreateEmptyBuilder();
+
+    b.builder.next_register_id = Register::Id(42);
+    auto o = b.builder.And(Register(i32(4)), Register(i32(2)));
+
+    EXPECT_EQ(o.GetKind(), Op::Kind::kAnd);
+
+    ASSERT_TRUE(o.Result().IsTemp());
+    EXPECT_EQ(Register::Id(42), o.Result().AsId());
+
+    ASSERT_TRUE(o.HasLHS());
+    auto& lhs = o.LHS();
+    ASSERT_TRUE(lhs.IsI32());
+    EXPECT_EQ(i32(4), lhs.AsI32());
+
+    ASSERT_TRUE(o.HasRHS());
+    auto& rhs = o.RHS();
+    ASSERT_TRUE(rhs.IsI32());
+    EXPECT_EQ(i32(2), rhs.AsI32());
+
+    std::stringstream str;
+    str << o;
+    EXPECT_EQ(str.str(), "%42 = 4 & 2");
+}
+
+TEST_F(IR_OpTest, CreateOr) {
+    auto& b = CreateEmptyBuilder();
+
+    b.builder.next_register_id = Register::Id(42);
+    auto o = b.builder.Or(Register(i32(4)), Register(i32(2)));
+
+    EXPECT_EQ(o.GetKind(), Op::Kind::kOr);
+
+    ASSERT_TRUE(o.Result().IsTemp());
+    EXPECT_EQ(Register::Id(42), o.Result().AsId());
+
+    ASSERT_TRUE(o.HasLHS());
+    auto& lhs = o.LHS();
+    ASSERT_TRUE(lhs.IsI32());
+    EXPECT_EQ(i32(4), lhs.AsI32());
+
+    ASSERT_TRUE(o.HasRHS());
+    auto& rhs = o.RHS();
+    ASSERT_TRUE(rhs.IsI32());
+    EXPECT_EQ(i32(2), rhs.AsI32());
+
+    std::stringstream str;
+    str << o;
+    EXPECT_EQ(str.str(), "%42 = 4 | 2");
+}
+
+TEST_F(IR_OpTest, CreateXor) {
+    auto& b = CreateEmptyBuilder();
+
+    b.builder.next_register_id = Register::Id(42);
+    auto o = b.builder.Xor(Register(i32(4)), Register(i32(2)));
+
+    EXPECT_EQ(o.GetKind(), Op::Kind::kXor);
+
+    ASSERT_TRUE(o.Result().IsTemp());
+    EXPECT_EQ(Register::Id(42), o.Result().AsId());
+
+    ASSERT_TRUE(o.HasLHS());
+    auto& lhs = o.LHS();
+    ASSERT_TRUE(lhs.IsI32());
+    EXPECT_EQ(i32(4), lhs.AsI32());
+
+    ASSERT_TRUE(o.HasRHS());
+    auto& rhs = o.RHS();
+    ASSERT_TRUE(rhs.IsI32());
+    EXPECT_EQ(i32(2), rhs.AsI32());
+
+    std::stringstream str;
+    str << o;
+    EXPECT_EQ(str.str(), "%42 = 4 ^ 2");
+}
+
+TEST_F(IR_OpTest, CreateLogicalAnd) {
+    auto& b = CreateEmptyBuilder();
+
+    b.builder.next_register_id = Register::Id(42);
+    auto o = b.builder.LogicalAnd(Register(i32(4)), Register(i32(2)));
+
+    EXPECT_EQ(o.GetKind(), Op::Kind::kLogicalAnd);
+
+    ASSERT_TRUE(o.Result().IsTemp());
+    EXPECT_EQ(Register::Id(42), o.Result().AsId());
+
+    ASSERT_TRUE(o.HasLHS());
+    auto& lhs = o.LHS();
+    ASSERT_TRUE(lhs.IsI32());
+    EXPECT_EQ(i32(4), lhs.AsI32());
+
+    ASSERT_TRUE(o.HasRHS());
+    auto& rhs = o.RHS();
+    ASSERT_TRUE(rhs.IsI32());
+    EXPECT_EQ(i32(2), rhs.AsI32());
+
+    std::stringstream str;
+    str << o;
+    EXPECT_EQ(str.str(), "%42 = 4 && 2");
+}
+
+TEST_F(IR_OpTest, CreateLogicalOr) {
+    auto& b = CreateEmptyBuilder();
+
+    b.builder.next_register_id = Register::Id(42);
+    auto o = b.builder.LogicalOr(Register(i32(4)), Register(i32(2)));
+
+    EXPECT_EQ(o.GetKind(), Op::Kind::kLogicalOr);
+
+    ASSERT_TRUE(o.Result().IsTemp());
+    EXPECT_EQ(Register::Id(42), o.Result().AsId());
+
+    ASSERT_TRUE(o.HasLHS());
+    auto& lhs = o.LHS();
+    ASSERT_TRUE(lhs.IsI32());
+    EXPECT_EQ(i32(4), lhs.AsI32());
+
+    ASSERT_TRUE(o.HasRHS());
+    auto& rhs = o.RHS();
+    ASSERT_TRUE(rhs.IsI32());
+    EXPECT_EQ(i32(2), rhs.AsI32());
+
+    std::stringstream str;
+    str << o;
+    EXPECT_EQ(str.str(), "%42 = 4 || 2");
+}
+
+TEST_F(IR_OpTest, CreateEqual) {
+    auto& b = CreateEmptyBuilder();
+
+    b.builder.next_register_id = Register::Id(42);
+    auto o = b.builder.Equal(Register(i32(4)), Register(i32(2)));
+
+    EXPECT_EQ(o.GetKind(), Op::Kind::kEqual);
+
+    ASSERT_TRUE(o.Result().IsTemp());
+    EXPECT_EQ(Register::Id(42), o.Result().AsId());
+
+    ASSERT_TRUE(o.HasLHS());
+    auto& lhs = o.LHS();
+    ASSERT_TRUE(lhs.IsI32());
+    EXPECT_EQ(i32(4), lhs.AsI32());
+
+    ASSERT_TRUE(o.HasRHS());
+    auto& rhs = o.RHS();
+    ASSERT_TRUE(rhs.IsI32());
+    EXPECT_EQ(i32(2), rhs.AsI32());
+
+    std::stringstream str;
+    str << o;
+    EXPECT_EQ(str.str(), "%42 = 4 == 2");
+}
+
+TEST_F(IR_OpTest, CreateNotEqual) {
+    auto& b = CreateEmptyBuilder();
+
+    b.builder.next_register_id = Register::Id(42);
+    auto o = b.builder.NotEqual(Register(i32(4)), Register(i32(2)));
+
+    EXPECT_EQ(o.GetKind(), Op::Kind::kNotEqual);
+
+    ASSERT_TRUE(o.Result().IsTemp());
+    EXPECT_EQ(Register::Id(42), o.Result().AsId());
+
+    ASSERT_TRUE(o.HasLHS());
+    auto& lhs = o.LHS();
+    ASSERT_TRUE(lhs.IsI32());
+    EXPECT_EQ(i32(4), lhs.AsI32());
+
+    ASSERT_TRUE(o.HasRHS());
+    auto& rhs = o.RHS();
+    ASSERT_TRUE(rhs.IsI32());
+    EXPECT_EQ(i32(2), rhs.AsI32());
+
+    std::stringstream str;
+    str << o;
+    EXPECT_EQ(str.str(), "%42 = 4 != 2");
+}
+
+TEST_F(IR_OpTest, CreateLessThan) {
+    auto& b = CreateEmptyBuilder();
+
+    b.builder.next_register_id = Register::Id(42);
+    auto o = b.builder.LessThan(Register(i32(4)), Register(i32(2)));
+
+    EXPECT_EQ(o.GetKind(), Op::Kind::kLessThan);
+
+    ASSERT_TRUE(o.Result().IsTemp());
+    EXPECT_EQ(Register::Id(42), o.Result().AsId());
+
+    ASSERT_TRUE(o.HasLHS());
+    auto& lhs = o.LHS();
+    ASSERT_TRUE(lhs.IsI32());
+    EXPECT_EQ(i32(4), lhs.AsI32());
+
+    ASSERT_TRUE(o.HasRHS());
+    auto& rhs = o.RHS();
+    ASSERT_TRUE(rhs.IsI32());
+    EXPECT_EQ(i32(2), rhs.AsI32());
+
+    std::stringstream str;
+    str << o;
+    EXPECT_EQ(str.str(), "%42 = 4 < 2");
+}
+
+TEST_F(IR_OpTest, CreateGreaterThan) {
+    auto& b = CreateEmptyBuilder();
+
+    b.builder.next_register_id = Register::Id(42);
+    auto o = b.builder.GreaterThan(Register(i32(4)), Register(i32(2)));
+
+    EXPECT_EQ(o.GetKind(), Op::Kind::kGreaterThan);
+
+    ASSERT_TRUE(o.Result().IsTemp());
+    EXPECT_EQ(Register::Id(42), o.Result().AsId());
+
+    ASSERT_TRUE(o.HasLHS());
+    auto& lhs = o.LHS();
+    ASSERT_TRUE(lhs.IsI32());
+    EXPECT_EQ(i32(4), lhs.AsI32());
+
+    ASSERT_TRUE(o.HasRHS());
+    auto& rhs = o.RHS();
+    ASSERT_TRUE(rhs.IsI32());
+    EXPECT_EQ(i32(2), rhs.AsI32());
+
+    std::stringstream str;
+    str << o;
+    EXPECT_EQ(str.str(), "%42 = 4 > 2");
+}
+
+TEST_F(IR_OpTest, CreateLessThanEqual) {
+    auto& b = CreateEmptyBuilder();
+
+    b.builder.next_register_id = Register::Id(42);
+    auto o = b.builder.LessThanEqual(Register(i32(4)), Register(i32(2)));
+
+    EXPECT_EQ(o.GetKind(), Op::Kind::kLessThanEqual);
+
+    ASSERT_TRUE(o.Result().IsTemp());
+    EXPECT_EQ(Register::Id(42), o.Result().AsId());
+
+    ASSERT_TRUE(o.HasLHS());
+    auto& lhs = o.LHS();
+    ASSERT_TRUE(lhs.IsI32());
+    EXPECT_EQ(i32(4), lhs.AsI32());
+
+    ASSERT_TRUE(o.HasRHS());
+    auto& rhs = o.RHS();
+    ASSERT_TRUE(rhs.IsI32());
+    EXPECT_EQ(i32(2), rhs.AsI32());
+
+    std::stringstream str;
+    str << o;
+    EXPECT_EQ(str.str(), "%42 = 4 <= 2");
+}
+
+TEST_F(IR_OpTest, CreateGreaterThanEqual) {
+    auto& b = CreateEmptyBuilder();
+
+    b.builder.next_register_id = Register::Id(42);
+    auto o = b.builder.GreaterThanEqual(Register(i32(4)), Register(i32(2)));
+
+    EXPECT_EQ(o.GetKind(), Op::Kind::kGreaterThanEqual);
+
+    ASSERT_TRUE(o.Result().IsTemp());
+    EXPECT_EQ(Register::Id(42), o.Result().AsId());
+
+    ASSERT_TRUE(o.HasLHS());
+    auto& lhs = o.LHS();
+    ASSERT_TRUE(lhs.IsI32());
+    EXPECT_EQ(i32(4), lhs.AsI32());
+
+    ASSERT_TRUE(o.HasRHS());
+    auto& rhs = o.RHS();
+    ASSERT_TRUE(rhs.IsI32());
+    EXPECT_EQ(i32(2), rhs.AsI32());
+
+    std::stringstream str;
+    str << o;
+    EXPECT_EQ(str.str(), "%42 = 4 >= 2");
+}
+
+TEST_F(IR_OpTest, CreateShiftLeft) {
+    auto& b = CreateEmptyBuilder();
+
+    b.builder.next_register_id = Register::Id(42);
+    auto o = b.builder.ShiftLeft(Register(i32(4)), Register(i32(2)));
+
+    EXPECT_EQ(o.GetKind(), Op::Kind::kShiftLeft);
+
+    ASSERT_TRUE(o.Result().IsTemp());
+    EXPECT_EQ(Register::Id(42), o.Result().AsId());
+
+    ASSERT_TRUE(o.HasLHS());
+    auto& lhs = o.LHS();
+    ASSERT_TRUE(lhs.IsI32());
+    EXPECT_EQ(i32(4), lhs.AsI32());
+
+    ASSERT_TRUE(o.HasRHS());
+    auto& rhs = o.RHS();
+    ASSERT_TRUE(rhs.IsI32());
+    EXPECT_EQ(i32(2), rhs.AsI32());
+
+    std::stringstream str;
+    str << o;
+    EXPECT_EQ(str.str(), "%42 = 4 << 2");
+}
+
+TEST_F(IR_OpTest, CreateShiftRight) {
+    auto& b = CreateEmptyBuilder();
+
+    b.builder.next_register_id = Register::Id(42);
+    auto o = b.builder.ShiftRight(Register(i32(4)), Register(i32(2)));
+
+    EXPECT_EQ(o.GetKind(), Op::Kind::kShiftRight);
+
+    ASSERT_TRUE(o.Result().IsTemp());
+    EXPECT_EQ(Register::Id(42), o.Result().AsId());
+
+    ASSERT_TRUE(o.HasLHS());
+    auto& lhs = o.LHS();
+    ASSERT_TRUE(lhs.IsI32());
+    EXPECT_EQ(i32(4), lhs.AsI32());
+
+    ASSERT_TRUE(o.HasRHS());
+    auto& rhs = o.RHS();
+    ASSERT_TRUE(rhs.IsI32());
+    EXPECT_EQ(i32(2), rhs.AsI32());
+
+    std::stringstream str;
+    str << o;
+    EXPECT_EQ(str.str(), "%42 = 4 >> 2");
+}
+
+TEST_F(IR_OpTest, CreateAdd) {
+    auto& b = CreateEmptyBuilder();
+
+    b.builder.next_register_id = Register::Id(42);
+    auto o = b.builder.Add(Register(i32(4)), Register(i32(2)));
+
+    EXPECT_EQ(o.GetKind(), Op::Kind::kAdd);
+
+    ASSERT_TRUE(o.Result().IsTemp());
+    EXPECT_EQ(Register::Id(42), o.Result().AsId());
+
+    ASSERT_TRUE(o.HasLHS());
+    auto& lhs = o.LHS();
+    ASSERT_TRUE(lhs.IsI32());
+    EXPECT_EQ(i32(4), lhs.AsI32());
+
+    ASSERT_TRUE(o.HasRHS());
+    auto& rhs = o.RHS();
+    ASSERT_TRUE(rhs.IsI32());
+    EXPECT_EQ(i32(2), rhs.AsI32());
+
+    std::stringstream str;
+    str << o;
+    EXPECT_EQ(str.str(), "%42 = 4 + 2");
+}
+
+TEST_F(IR_OpTest, CreateSubtract) {
+    auto& b = CreateEmptyBuilder();
+
+    b.builder.next_register_id = Register::Id(42);
+    auto o = b.builder.Subtract(Register(i32(4)), Register(i32(2)));
+
+    EXPECT_EQ(o.GetKind(), Op::Kind::kSubtract);
+
+    ASSERT_TRUE(o.Result().IsTemp());
+    EXPECT_EQ(Register::Id(42), o.Result().AsId());
+
+    ASSERT_TRUE(o.HasLHS());
+    auto& lhs = o.LHS();
+    ASSERT_TRUE(lhs.IsI32());
+    EXPECT_EQ(i32(4), lhs.AsI32());
+
+    ASSERT_TRUE(o.HasRHS());
+    auto& rhs = o.RHS();
+    ASSERT_TRUE(rhs.IsI32());
+    EXPECT_EQ(i32(2), rhs.AsI32());
+
+    std::stringstream str;
+    str << o;
+    EXPECT_EQ(str.str(), "%42 = 4 - 2");
+}
+
+TEST_F(IR_OpTest, CreateMultiply) {
+    auto& b = CreateEmptyBuilder();
+
+    b.builder.next_register_id = Register::Id(42);
+    auto o = b.builder.Multiply(Register(i32(4)), Register(i32(2)));
+
+    EXPECT_EQ(o.GetKind(), Op::Kind::kMultiply);
+
+    ASSERT_TRUE(o.Result().IsTemp());
+    EXPECT_EQ(Register::Id(42), o.Result().AsId());
+
+    ASSERT_TRUE(o.HasLHS());
+    auto& lhs = o.LHS();
+    ASSERT_TRUE(lhs.IsI32());
+    EXPECT_EQ(i32(4), lhs.AsI32());
+
+    ASSERT_TRUE(o.HasRHS());
+    auto& rhs = o.RHS();
+    ASSERT_TRUE(rhs.IsI32());
+    EXPECT_EQ(i32(2), rhs.AsI32());
+
+    std::stringstream str;
+    str << o;
+    EXPECT_EQ(str.str(), "%42 = 4 * 2");
+}
+
+TEST_F(IR_OpTest, CreateDivide) {
+    auto& b = CreateEmptyBuilder();
+
+    b.builder.next_register_id = Register::Id(42);
+    auto o = b.builder.Divide(Register(i32(4)), Register(i32(2)));
+
+    EXPECT_EQ(o.GetKind(), Op::Kind::kDivide);
+
+    ASSERT_TRUE(o.Result().IsTemp());
+    EXPECT_EQ(Register::Id(42), o.Result().AsId());
+
+    ASSERT_TRUE(o.HasLHS());
+    auto& lhs = o.LHS();
+    ASSERT_TRUE(lhs.IsI32());
+    EXPECT_EQ(i32(4), lhs.AsI32());
+
+    ASSERT_TRUE(o.HasRHS());
+    auto& rhs = o.RHS();
+    ASSERT_TRUE(rhs.IsI32());
+    EXPECT_EQ(i32(2), rhs.AsI32());
+
+    std::stringstream str;
+    str << o;
+    EXPECT_EQ(str.str(), "%42 = 4 / 2");
+}
+
+TEST_F(IR_OpTest, CreateModulo) {
+    auto& b = CreateEmptyBuilder();
+
+    b.builder.next_register_id = Register::Id(42);
+    auto o = b.builder.Modulo(Register(i32(4)), Register(i32(2)));
+
+    EXPECT_EQ(o.GetKind(), Op::Kind::kModulo);
+
+    ASSERT_TRUE(o.Result().IsTemp());
+    EXPECT_EQ(Register::Id(42), o.Result().AsId());
+
+    ASSERT_TRUE(o.HasLHS());
+    auto& lhs = o.LHS();
+    ASSERT_TRUE(lhs.IsI32());
+    EXPECT_EQ(i32(4), lhs.AsI32());
+
+    ASSERT_TRUE(o.HasRHS());
+    auto& rhs = o.RHS();
+    ASSERT_TRUE(rhs.IsI32());
+    EXPECT_EQ(i32(2), rhs.AsI32());
+
+    std::stringstream str;
+    str << o;
+    EXPECT_EQ(str.str(), "%42 = 4 % 2");
+}
+
+}  // namespace
+}  // namespace tint::ir
diff --git a/src/tint/ir/register.cc b/src/tint/ir/register.cc
new file mode 100644
index 0000000..f3caa76
--- /dev/null
+++ b/src/tint/ir/register.cc
@@ -0,0 +1,76 @@
+// 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/register.h"
+
+namespace tint::ir {
+
+Register::Register() : kind_(Kind::kUninitialized), data_(Id(0)) {}
+
+Register::Register(Id id) : kind_(Kind::kTemp), data_(id) {}
+
+Register::Register(f32 f) : kind_(Kind::kF32), data_(f) {}
+
+Register::Register(f16 f) : kind_(Kind::kF16), data_(f) {}
+
+Register::Register(u32 u) : kind_(Kind::kU32), data_(u) {}
+
+Register::Register(i32 i) : kind_(Kind::kI32), data_(i) {}
+
+Register::Register(bool b) : kind_(Kind::kBool), data_(b) {}
+
+Register::Register(Symbol s, Id id) : kind_(Kind::kVar), data_(VarData{s, id}) {}
+
+Register::~Register() = default;
+
+Register::Register(const Register& o) = default;
+
+Register::Register(Register&& o) = default;
+
+Register& Register::operator=(const Register& o) = default;
+
+Register& Register::operator=(Register&& o) = default;
+
+std::ostream& operator<<(std::ostream& out, const Register& r) {
+    switch (r.GetKind()) {
+        case Register::Kind::kTemp:
+            out << "%" << std::to_string(r.AsId());
+            break;
+        case Register::Kind::kF32:
+            out << std::to_string(r.AsF32().value);
+            break;
+        case Register::Kind::kF16:
+            out << std::to_string(r.AsF16().value);
+            break;
+        case Register::Kind::kI32:
+            out << std::to_string(r.AsI32().value);
+            break;
+        case Register::Kind::kU32:
+            out << std::to_string(r.AsU32().value);
+            break;
+            // TODO(dsinclair): Emit the symbol instead of v
+        case Register::Kind::kVar:
+            out << "%v" << std::to_string(r.AsVarData().id);
+            break;
+        case Register::Kind::kBool:
+            out << (r.AsBool() ? "true" : "false");
+            break;
+        case Register::Kind::kUninitialized:
+            out << "unknown register";
+            break;
+    }
+    return out;
+}
+
+}  // namespace tint::ir
diff --git a/src/tint/ir/register.h b/src/tint/ir/register.h
new file mode 100644
index 0000000..d1c41eb
--- /dev/null
+++ b/src/tint/ir/register.h
@@ -0,0 +1,169 @@
+// 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_REGISTER_H_
+#define SRC_TINT_IR_REGISTER_H_
+
+#include <ostream>
+#include <variant>
+
+#include "src/tint/number.h"
+#include "src/tint/symbol.h"
+
+namespace tint::ir {
+
+/// Register in the IR. The register can be one of several types these include, but aren't limited
+/// to, `f32`, `u32`, `temp`, `var`. The type of the register determines the type of data stored
+/// in the register.
+class Register {
+  public:
+    /// A register id.
+    using Id = uint32_t;
+
+    /// The type of the register
+    enum class Kind {
+        /// A uninitialized register
+        kUninitialized,
+        /// A temporary allocated register
+        kTemp,
+        /// A f32 register
+        kF32,
+        /// A f16 register
+        kF16,
+        /// An i32 register
+        kI32,
+        /// A u32 register
+        kU32,
+        /// A variable register
+        kVar,
+        /// A boolean register
+        kBool,
+    };
+
+    /// Stores data for a given variable. There will be multiple `VarData` entries for a given `id`.
+    /// The `id` acts like a generation number (although they aren't sequential, they are
+    /// increasing). As the variable is stored too a new register will be created and the the `id`
+    /// will be incremented.
+    struct VarData {
+        /// The symbol for the variable
+        Symbol sym;
+        /// The id for the variable.
+        Id id;
+        // TODO(dsinclair): Should var type data be stored here along side the variable info?
+    };
+
+    /// Constructor
+    /// Creates a uninitialized register
+    Register();
+
+    /// Constructor
+    /// @param id the id for the register
+    explicit Register(Id id);
+
+    /// Constructor
+    /// @param s the symbol for the register
+    /// @param id the id for the register
+    Register(Symbol s, Id id);
+
+    /// Constructor
+    /// @param b the `bool` value to store in the register
+    explicit Register(bool b);
+
+    /// Constructor
+    /// @param f the `f32` value to store in the register
+    explicit Register(f32 f);
+
+    /// Constructor
+    /// @param f the `f16` value to store in the register
+    explicit Register(f16 f);
+
+    /// Constructor
+    /// @param u the `u32` value to store in the register
+    explicit Register(u32 u);
+
+    /// Constructor
+    /// @param i the `i32` value to store in the register
+    explicit Register(i32 i);
+
+    /// Destructor
+    ~Register();
+
+    /// Copy constructor
+    /// @param o the register to copy from
+    Register(const Register& o);
+    /// Move constructor
+    /// @param o the register to move from
+    Register(Register&& o);
+
+    /// Copy assign
+    /// @param o the register to copy from
+    /// @returns this
+    Register& operator=(const Register& o);
+    /// Move assign
+    /// @param o the register to move from
+    /// @returns this
+    Register& operator=(Register&& o);
+
+    /// @returns true if this is a temporary register
+    bool IsTemp() const { return kind_ == Kind::kTemp; }
+    /// @returns true if this is a f32 register
+    bool IsF32() const { return kind_ == Kind::kF32; }
+    /// @returns true if this is a f16 register
+    bool IsF16() const { return kind_ == Kind::kF16; }
+    /// @returns true if this is an i32 register
+    bool IsI32() const { return kind_ == Kind::kI32; }
+    /// @returns true if this is a u32 register
+    bool IsU32() const { return kind_ == Kind::kU32; }
+    /// @returns true if this is a var register
+    bool IsVar() const { return kind_ == Kind::kVar; }
+    /// @returns true if this is a bool register
+    bool IsBool() const { return kind_ == Kind::kBool; }
+
+    /// @returns the kind of register
+    Kind GetKind() const { return kind_; }
+
+    /// @returns the register data as a `f32`.
+    /// @note, must only be called if `IsF32()` is true
+    f32 AsF32() const { return std::get<f32>(data_); }
+    /// @returns the register data as a `f16`.
+    /// @note, must only be called if `IsF16()` is true
+    f16 AsF16() const { return std::get<f16>(data_); }
+    /// @returns the register data as an `i32`.
+    /// @note, must only be called if `IsI32()` is true
+    i32 AsI32() const { return std::get<i32>(data_); }
+    /// @returns the register data as a `u32`.
+    /// @note, must only be called if `IsU32()` is true
+    u32 AsU32() const { return std::get<u32>(data_); }
+    /// @returns the register data as an `Id`.
+    /// @note, must only be called if `IsTemp()` is true
+    Id AsId() const { return std::get<Id>(data_); }
+    /// @returns the register data as a `VarData` structure.
+    /// @note, must only be called if `IsVar()` is true
+    VarData AsVarData() const { return std::get<VarData>(data_); }
+    /// @returns the register data as a `bool`.
+    /// @note, must only be called if `IsBool()` is true
+    bool AsBool() const { return std::get<bool>(data_); }
+
+  private:
+    /// The type of data stored in this register
+    Kind kind_;
+    /// The data stored in the register
+    std::variant<Id, f32, f16, u32, i32, VarData, bool> data_;
+};
+
+std::ostream& operator<<(std::ostream& out, const Register& r);
+
+}  // namespace tint::ir
+
+#endif  // SRC_TINT_IR_REGISTER_H_
diff --git a/src/tint/ir/register_test.cc b/src/tint/ir/register_test.cc
new file mode 100644
index 0000000..fb0194f
--- /dev/null
+++ b/src/tint/ir/register_test.cc
@@ -0,0 +1,183 @@
+// 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 <sstream>
+
+#include "src/tint/ir/register.h"
+#include "src/tint/ir/test_helper.h"
+
+namespace tint::ir {
+namespace {
+
+using namespace tint::number_suffixes;  // NOLINT
+
+using IR_RegisterTest = TestHelper;
+
+TEST_F(IR_RegisterTest, f32) {
+    std::stringstream str;
+
+    Register r(1.2_f);
+    EXPECT_EQ(1.2_f, r.AsF32());
+
+    str << r;
+    EXPECT_EQ("1.200000", str.str());
+
+    EXPECT_TRUE(r.IsF32());
+    EXPECT_FALSE(r.IsF16());
+    EXPECT_FALSE(r.IsI32());
+    EXPECT_FALSE(r.IsU32());
+    EXPECT_FALSE(r.IsTemp());
+    EXPECT_FALSE(r.IsVar());
+    EXPECT_FALSE(r.IsBool());
+}
+
+TEST_F(IR_RegisterTest, f16) {
+    std::stringstream str;
+
+    Register r(1.1_h);
+    EXPECT_EQ(1.1_h, r.AsF16());
+
+    str << r;
+    EXPECT_EQ("1.099609", str.str());
+
+    EXPECT_FALSE(r.IsF32());
+    EXPECT_TRUE(r.IsF16());
+    EXPECT_FALSE(r.IsI32());
+    EXPECT_FALSE(r.IsU32());
+    EXPECT_FALSE(r.IsTemp());
+    EXPECT_FALSE(r.IsVar());
+    EXPECT_FALSE(r.IsBool());
+}
+
+TEST_F(IR_RegisterTest, i32) {
+    std::stringstream str;
+
+    Register r(1_i);
+    EXPECT_EQ(1_i, r.AsI32());
+
+    str << r;
+    EXPECT_EQ("1", str.str());
+
+    EXPECT_FALSE(r.IsF32());
+    EXPECT_FALSE(r.IsF16());
+    EXPECT_TRUE(r.IsI32());
+    EXPECT_FALSE(r.IsU32());
+    EXPECT_FALSE(r.IsTemp());
+    EXPECT_FALSE(r.IsVar());
+    EXPECT_FALSE(r.IsBool());
+}
+
+TEST_F(IR_RegisterTest, u32) {
+    std::stringstream str;
+
+    Register r(2_u);
+    EXPECT_EQ(2_u, r.AsU32());
+
+    str << r;
+    EXPECT_EQ("2", str.str());
+
+    EXPECT_FALSE(r.IsF32());
+    EXPECT_FALSE(r.IsF16());
+    EXPECT_FALSE(r.IsI32());
+    EXPECT_TRUE(r.IsU32());
+    EXPECT_FALSE(r.IsTemp());
+    EXPECT_FALSE(r.IsVar());
+    EXPECT_FALSE(r.IsBool());
+}
+
+TEST_F(IR_RegisterTest, id) {
+    std::stringstream str;
+
+    Register r(Register::Id(4));
+    EXPECT_EQ(4u, r.AsId());
+
+    str << r;
+    EXPECT_EQ("%4", str.str());
+
+    EXPECT_FALSE(r.IsF32());
+    EXPECT_FALSE(r.IsF16());
+    EXPECT_FALSE(r.IsI32());
+    EXPECT_FALSE(r.IsU32());
+    EXPECT_TRUE(r.IsTemp());
+    EXPECT_FALSE(r.IsVar());
+    EXPECT_FALSE(r.IsBool());
+}
+
+TEST_F(IR_RegisterTest, bool) {
+    std::stringstream str;
+
+    Register r(false);
+    EXPECT_FALSE(r.AsBool());
+
+    str << r;
+    EXPECT_EQ("false", str.str());
+
+    str.str("");
+    r = Register(true);
+    EXPECT_TRUE(r.AsBool());
+
+    str << r;
+    EXPECT_EQ("true", str.str());
+
+    EXPECT_FALSE(r.IsF32());
+    EXPECT_FALSE(r.IsF16());
+    EXPECT_FALSE(r.IsI32());
+    EXPECT_FALSE(r.IsU32());
+    EXPECT_FALSE(r.IsTemp());
+    EXPECT_FALSE(r.IsVar());
+    EXPECT_TRUE(r.IsBool());
+}
+
+TEST_F(IR_RegisterTest, var) {
+    std::stringstream str;
+
+    Symbol s;
+    Register r(s, 2);
+    EXPECT_EQ(2u, r.AsVarData().id);
+    EXPECT_EQ(s, r.AsVarData().sym);
+
+    str << r;
+    EXPECT_EQ("%v2", str.str());
+    str.str("");
+
+    r = Register(s, 4);
+    EXPECT_EQ(4u, r.AsVarData().id);
+    EXPECT_EQ(s, r.AsVarData().sym);
+
+    str << r;
+    EXPECT_EQ("%v4", str.str());
+
+    EXPECT_FALSE(r.IsF32());
+    EXPECT_FALSE(r.IsF16());
+    EXPECT_FALSE(r.IsI32());
+    EXPECT_FALSE(r.IsU32());
+    EXPECT_FALSE(r.IsTemp());
+    EXPECT_TRUE(r.IsVar());
+    EXPECT_FALSE(r.IsBool());
+}
+
+TEST_F(IR_RegisterTest, uninitialized) {
+    Register r;
+
+    EXPECT_FALSE(r.IsF32());
+    EXPECT_FALSE(r.IsF16());
+    EXPECT_FALSE(r.IsI32());
+    EXPECT_FALSE(r.IsU32());
+    EXPECT_FALSE(r.IsTemp());
+    EXPECT_FALSE(r.IsVar());
+    EXPECT_FALSE(r.IsBool());
+}
+
+}  // namespace
+}  // namespace tint::ir
diff --git a/src/tint/ir/switch.h b/src/tint/ir/switch.h
index 39d3d06..73284cf 100644
--- a/src/tint/ir/switch.h
+++ b/src/tint/ir/switch.h
@@ -17,6 +17,7 @@
 
 #include "src/tint/ir/block.h"
 #include "src/tint/ir/flow_node.h"
+#include "src/tint/ir/register.h"
 
 // Forward declarations
 namespace tint::ast {
@@ -50,6 +51,9 @@
 
     /// The switch case statements
     utils::Vector<Case, 4> cases;
+
+    /// Register holding the condition result
+    Register condition;
 };
 
 }  // namespace tint::ir
diff --git a/src/tint/ir/test_helper.h b/src/tint/ir/test_helper.h
index fdc4787..10694ee 100644
--- a/src/tint/ir/test_helper.h
+++ b/src/tint/ir/test_helper.h
@@ -20,6 +20,7 @@
 
 #include "gtest/gtest.h"
 #include "src/tint/ir/builder_impl.h"
+#include "src/tint/ir/disassembler.h"
 #include "src/tint/program_builder.h"
 
 namespace tint::ir {
@@ -36,7 +37,7 @@
     /// @note The builder is only created once. Multiple calls to Build() will
     /// return the same builder without rebuilding.
     /// @return the builder
-    BuilderImpl& Build() {
+    BuilderImpl& CreateBuilder() {
         if (gen_) {
             return *gen_;
         }
@@ -47,6 +48,16 @@
         return *gen_;
     }
 
+    /// Creates a BuilderImpl without an originating program. This is used for testing the
+    /// expressions which don't require the full builder implementation. The current flow block
+    /// is initialized with an empty block.
+    /// @returns the BuilderImpl for testing.
+    BuilderImpl& CreateEmptyBuilder() {
+        gen_ = std::make_unique<BuilderImpl>(nullptr);
+        gen_->current_flow_block = gen_->builder.CreateBlock();
+        return *gen_;
+    }
+
     /// The program built with a call to Build()
     std::unique_ptr<Program> program;
 
diff --git a/src/tint/number.cc b/src/tint/number.cc
index 91dbb45..629091b 100644
--- a/src/tint/number.cc
+++ b/src/tint/number.cc
@@ -20,8 +20,35 @@
 #include <ostream>
 
 #include "src/tint/debug.h"
+#include "src/tint/utils/bitcast.h"
 
 namespace tint {
+namespace {
+
+constexpr uint16_t kF16Nan = 0x7e00u;
+constexpr uint16_t kF16PosInf = 0x7c00u;
+constexpr uint16_t kF16NegInf = 0xfc00u;
+
+constexpr uint16_t kF16SignMask = 0x8000u;
+constexpr uint16_t kF16ExponentMask = 0x7c00u;
+constexpr uint16_t kF16MantissaMask = 0x03ffu;
+
+constexpr uint32_t kF16MantissaBits = 10;
+constexpr uint32_t kF16ExponentBias = 15;
+
+constexpr uint32_t kF32SignMask = 0x80000000u;
+constexpr uint32_t kF32ExponentMask = 0x7f800000u;
+constexpr uint32_t kF32MantissaMask = 0x007fffffu;
+
+constexpr uint32_t kF32MantissaBits = 23;
+constexpr uint32_t kF32ExponentBias = 127;
+
+constexpr uint32_t kMaxF32BiasedExpForF16NormalNumber = 142;
+constexpr uint32_t kMinF32BiasedExpForF16NormalNumber = 113;
+constexpr uint32_t kMaxF32BiasedExpForF16SubnormalNumber = 112;
+constexpr uint32_t kMinF32BiasedExpForF16SubnormalNumber = 103;
+
+}  // namespace
 
 std::ostream& operator<<(std::ostream& out, ConversionFailure failure) {
     switch (failure) {
@@ -40,20 +67,17 @@
     if (value < kLowestValue) {
         return -std::numeric_limits<f16::type>::infinity();
     }
+
     // Below value must be within the finite range of a f16.
     // Assert we use binary32 (i.e. float) as underlying type, which has 4 bytes.
     static_assert(std::is_same<f16::type, float>());
-    const uint32_t sign_mask = 0x80000000u;      // Mask for the sign bit
-    const uint32_t exponent_mask = 0x7f800000u;  // Mask for 8 exponent bits
 
-    uint32_t u32;
-    memcpy(&u32, &value, 4);
-
-    if ((u32 & ~sign_mask) == 0) {
-        return value;                // +/- zero
+    uint32_t u32 = utils::Bitcast<uint32_t>(value);
+    if ((u32 & ~kF32SignMask) == 0) {
+        return value;  // +/- zero
     }
-    if ((u32 & exponent_mask) == exponent_mask) {  // exponent all 1's
-        return value;                        // inf or nan
+    if ((u32 & kF32ExponentMask) == kF32ExponentMask) {  // exponent all 1's
+        return value;                                    // inf or nan
     }
 
     // We are now going to quantize a f32 number into subnormal f16 and store the result value back
@@ -62,23 +86,27 @@
     // bits of the original f32 number.
     //
     // Note:
-    // f32 has 1 sign bit, 8 exponent bits for biased exponent (i.e. unbiased exponent + 127), and
-    // 23 mantissa bits. Binary form: s_eeeeeeee_mmmmmmmmmmmmmmmmmmmmmmm
-    // f16 has 1 sign bit, 5 exponent bits for biased exponent (i.e. unbiased exponent + 15), and
-    // 10 mantissa bits. Binary form: s_eeeee_mmmmmmmmmm
+    // * f32 has 1 sign bit, 8 exponent bits for biased exponent (i.e. unbiased exponent + 127), and
+    //   23 mantissa bits. Binary form: s_eeeeeeee_mmmmmmmmmmmmmmmmmmmmmmm
+    //
+    // * f16 has 1 sign bit, 5 exponent bits for biased exponent (i.e. unbiased exponent + 15), and
+    //   10 mantissa bits. Binary form: s_eeeee_mmmmmmmmmm
+    //
     // The largest finite f16 number has a biased exponent of 11110 in binary, or 30 decimal, and so
-    // a unbiased exponent of 30 - 15 = 15.
+    // an unbiased exponent of 30 - 15 = 15.
+    //
     // The smallest finite f16 number has a biased exponent of 00001 in binary, or 1 decimal, and so
     // a unbiased exponent of 1 - 15 = -14.
     //
     // We may follow the argument below:
     // 1. All normal or subnormal f16 values, range from 0x1.p-24 to 0x1.ffcp15, are exactly
-    // representable by normal f32 number.
+    // representable by a normal f32 number.
     //   1.1. We can denote the set of all f32 number that are exact representation of finite f16
     //   values by `R`.
     //   1.2. We can do the quantization by mapping a normal f32 value v (in the f16 finite range)
     //   to a certain f32 number v' in the set R, which is the largest (by the meaning of absolute
     //   value) one among all values in R that are no larger than v.
+    //
     // 2. We can decide whether a given normal f32 number v is in the set R, by looking at its
     // mantissa bits and biased exponent `e`. Recall that biased exponent e is unbiased exponent +
     // 127, and in the range of 1 to 254 for normal f32 number.
@@ -99,100 +127,131 @@
     //   equal to 0.0, thus can not be in set R.
     //   2.5. If abs(v) = 0, v is in set R and is just +-0.0.
     //
-    // Proof for 2.2:
+    // Proof for 2.2
+    // -------------
     // Any normal f16 number, in binary form, s_eeeee_mmmmmmmmmm, has value
-    //      (s==0?1:-1)*(1+uint(mmmmm_mmmmm)*(2^-10))*2^(uint(eeeee)-15)
+    //
+    //      (s == 0 ? 1 : -1) * (1 + uint(mmmmm_mmmmm) * (2^-10)) * 2^(uint(eeeee) - 15)
+    //
     // in which unit(bbbbb) means interprete binary pattern "bbbbb" as unsigned binary number,
     // and we have 1 <= uint(eeeee) <= 30.
+    //
     // This value is equal to a normal f32 number with binary
     //      s_EEEEEEEE_mmmmmmmmmm0000000000000
-    // where uint(EEEEEEEE) = uint(eeeee) + 112, so that unbiased exponent keep unchanged
+    //
+    // where uint(EEEEEEEE) = uint(eeeee) + 112, so that unbiased exponent is kept unchanged
+    //
     //      uint(EEEEEEEE) - 127 = uint(eeeee) - 15
+    //
     // and its value is
-    //         (s==0?1:-1)*(1+uint(mmmmm_mmmmm_00000_00000_000)*(2^-23))*2^(uint(EEEEEEEE)-127)
-    //      == (s==0?1:-1)*(1+uint(mmmmm_mmmmm)*(2^-10))*2^(uint(eeeee)-15)
+    //         (s == 0 ? 1 : -1) *
+    //            (1 + uint(mmmmm_mmmmm_00000_00000_000) * (2^-23)) * 2^(uint(EEEEEEEE) - 127)
+    //      == (s == 0 ? 1 : -1) *
+    //            (1 + uint(mmmmm_mmmmm)                 * (2^-10)) * 2^(uint(eeeee) - 15)
+    //
     // Notice that uint(EEEEEEEE) is in range [113, 142], showing that it is a normal f32 number.
-    // So we proof that any normal f16 number can be exactly representd by a normal f32 number
-    // with biased exponent in range [113,142] and the lowest 13 mantissa bits 0.
+    // So we proved that any normal f16 number can be exactly representd by a normal f32 number
+    // with biased exponent in range [113, 142] and the lowest 13 mantissa bits 0.
+    //
     // On the other hand, since mantissa bits mmmmmmmmmm are arbitrary, the value of any f32
     // that has a biased exponent in range [113, 142] and lowest 13 mantissa bits zero is equal
-    // to a normal f16 value. Hence we proof 2.2.
+    // to a normal f16 value. Hence we prove 2.2.
     //
-    // Proof for 2.3:
+    // Proof for 2.3
+    // -------------
     // Any subnormal f16 number has a binary form of s_00000_mmmmmmmmmm, and its value is
-    // (s==0?1:-1)*uint(mmmmmmmmmm)*(2^-10)*(2^-14) = (s==0?1:-1)*uint(mmmmmmmmmm)*(2^-24).
-    // We discuss on bit pattern of mantissa bits mmmmmmmmmm.
-    //   Case 1: mantissa bits has no leading zero bit, s_00000_1mmmmmmmmm
+    //
+    //    (s == 0 ? 1 : -1) * uint(mmmmmmmmmm) * (2^-10) * (2^-14)
+    // == (s == 0 ? 1 : -1) * uint(mmmmmmmmmm) * (2^-24).
+    //
+    // We discuss the bit pattern of mantissa bits mmmmmmmmmm.
+    //   Case 1: mantissa bits have no leading zero bit, s_00000_1mmmmmmmmm
     //      In this case the value is
-    //              (s==0?1:-1)*uint(1mmmm_mmmmm)*(2^-10)*(2^-14)
-    //          ==  (s==0?1:-1)*(uint(1_mmmmm_mmmm)*(2^-9))*(2^-15)
-    //          ==  (s==0?1:-1)*(1+uint(mmmmm_mmmm)*(2^-9))*(2^-15)
-    //          ==  (s==0?1:-1)*(1+uint(mmmmm_mmmm0_00000_00000_000)*(2^-23))*(2^-15)
-    //      which is equal to the value of normal f32 number
+    //              (s == 0 ? 1 : -1) *      uint(1mmmm_mmmmm)                 * (2^-10)  * (2^-14)
+    //          ==  (s == 0 ? 1 : -1) * (    uint(1_mmmmm_mmmm)                * (2^-9))  * (2^-15)
+    //          ==  (s == 0 ? 1 : -1) * (1 + uint(mmmmm_mmmm)                  * (2^-9))  * (2^-15)
+    //          ==  (s == 0 ? 1 : -1) * (1 + uint(mmmmm_mmmm0_00000_00000_000) * (2^-23)) * (2^-15)
+    //
+    //      which is equal to the value of the normal f32 number
+    //
     //          s_EEEEEEEE_mmmmm_mmmm0_00000_00000_000
-    //      where uint(EEEEEEEE) = -15 + 127 = 112. Hence we proof that any subnormal f16 number
+    //
+    //      where uint(EEEEEEEE) == -15 + 127 = 112. Hence we proved that any subnormal f16 number
     //      with no leading zero mantissa bit can be exactly represented by a f32 number with
     //      biased exponent 112 and the lowest 14 mantissa bits zero, and the value of any f32
-    //      number with biased exponent 112 and the lowest 14 mantissa bits zero are equal to a
+    //      number with biased exponent 112 and the lowest 14 mantissa bits zero is equal to a
     //      subnormal f16 number with no leading zero mantissa bit.
+    //
     //   Case 2: mantissa bits has 1 leading zero bit, s_00000_01mmmmmmmm
     //      In this case the value is
-    //              (s==0?1:-1)*uint(01mmm_mmmmm)*(2^-10)*(2^-14)
-    //          ==  (s==0?1:-1)*(uint(01_mmmmm_mmm)*(2^-8))*(2^-16)
-    //          ==  (s==0?1:-1)*(1+uint(mmmmm_mmm)*(2^-8))*(2^-16)
-    //          ==  (s==0?1:-1)*(1+uint(mmmmm_mmm00_00000_00000_000)*(2^-23))*(2^-16)
+    //              (s == 0 ? 1 : -1) *      uint(01mmm_mmmmm)                 * (2^-10)  * (2^-14)
+    //          ==  (s == 0 ? 1 : -1) * (    uint(01_mmmmm_mmm)                * (2^-8))  * (2^-16)
+    //          ==  (s == 0 ? 1 : -1) * (1 + uint(mmmmm_mmm)                   * (2^-8))  * (2^-16)
+    //          ==  (s == 0 ? 1 : -1) * (1 + uint(mmmmm_mmm00_00000_00000_000) * (2^-23)) * (2^-16)
+    //
     //      which is equal to the value of normal f32 number
+    //
     //          s_EEEEEEEE_mmmmm_mmm00_00000_00000_000
-    //      where uint(EEEEEEEE) = -16 + 127 = 111. Hence we proof that any subnormal f16 number
+    //
+    //      where uint(EEEEEEEE) = -16 + 127 = 111. Hence we proved that any subnormal f16 number
     //      with 1 leading zero mantissa bit can be exactly represented by a f32 number with
     //      biased exponent 111 and the lowest 15 mantissa bits zero, and the value of any f32
-    //      number with biased exponent 111 and the lowest 15 mantissa bits zero are equal to a
+    //      number with biased exponent 111 and the lowest 15 mantissa bits zero is equal to a
     //      subnormal f16 number with 1 leading zero mantissa bit.
+    //
     //   Case 3 to case 8: ......
-    //   Case 9: mantissa bits has 8 leading zero bit, s_00000_000000001m
+    //
+    //   Case 9: mantissa bits has 8 leading zero bits, s_00000_000000001m
     //      In this case the value is
-    //              (s==0?1:-1)*uint(00000_0001m)*(2^-10)*(2^-14)
-    //          ==  (s==0?1:-1)*(uint(000000001_m)*(2^-1))*(2^-23)
-    //          ==  (s==0?1:-1)*(1+uint(m)*(2^-1))*(2^-23)
-    //          ==  (s==0?1:-1)*(1+uint(m0000_00000_00000_00000_000)*(2^-23))*(2^-23)
+    //              (s == 0 ? 1 : -1) *      uint(00000_0001m)                 * (2^-10)  * (2^-14)
+    //          ==  (s == 0 ? 1 : -1) * (    uint(000000001_m)                 * (2^-1))  * (2^-23)
+    //          ==  (s == 0 ? 1 : -1) * (1 + uint(m)                           * (2^-1))  * (2^-23)
+    //          ==  (s == 0 ? 1 : -1) * (1 + uint(m0000_00000_00000_00000_000) * (2^-23)) * (2^-23)
+    //
     //      which is equal to the value of normal f32 number
+    //
     //          s_EEEEEEEE_m0000_00000_00000_00000_000
-    //      where uint(EEEEEEEE) = -23 + 127 = 104. Hence we proof that any subnormal f16 number
+    //
+    //      where uint(EEEEEEEE) = -23 + 127 = 104. Hence we proved that any subnormal f16 number
     //      with 8 leading zero mantissa bit can be exactly represented by a f32 number with
     //      biased exponent 104 and the lowest 22 mantissa bits zero, and the value of any f32
     //      number with biased exponent 104 and the lowest 22 mantissa bits zero are equal to a
     //      subnormal f16 number with 8 leading zero mantissa bit.
-    //   Case 10: mantissa bits has 9 leading zero bit, s_00000_0000000001
-    //      In this case the value is just +-2^-24 = +-0x1.0p-24,
+    //
+    //   Case 10: mantissa bits has 9 leading zero bits, s_00000_0000000001
+    //      In this case the value is just +-2^-24 == +-0x1.0p-24,
     //      the f32 number has biased exponent 103 and all 23 mantissa bits zero.
-    //   Case 11: mantissa bits has 10 leading zero bit, s_00000_0000000000, just 0.0
-    // Concluding all these case, we proof that any subnormal f16 number with N leading zero
-    // mantissa bit can be exactly represented by a f32 number with biased exponent 112-N and the
-    // lowest 14+N mantissa bits zero, and the value of any f32 number with biased exponent 112-N (=
-    // e) and the lowest 14+N (= 126-e) mantissa bits zero are equal to a subnormal f16 number with
-    // N leading zero mantissa bit. N is in range [0, 9], so the f32 number's biased exponent e is
-    // in range [103, 112], or unbiased exponent in [-24, -15].
+    //
+    //   Case 11: mantissa bits has 10 leading zero bits, s_00000_0000000000, just 0.0
+    //
+    // Concluding all these case, we proved that any subnormal f16 number with N leading zero
+    // mantissa bit can be exactly represented by a f32 number with biased exponent 112 - N and the
+    // lowest 14 + N mantissa bits zero, and the value of any f32 number with biased exponent
+    // 112 - N (= e) and the lowest 14 + N (= 126 - e) mantissa bits zero are equal to a subnormal
+    // f16 number with N leading zero mantissa bits. N is in range [0, 9], so the f32 number's
+    // biased exponent e is in range [103, 112], or unbiased exponent in [-24, -15].
 
     float abs_value = std::fabs(value);
     if (abs_value >= kSmallestValue) {
         // Value falls in the normal f16 range, quantize it to a normal f16 value by masking out
         // lowest 13 mantissa bits.
-        u32 = u32 & ~((1u << 13) - 1);
+        u32 = u32 & ~((1u << (kF32MantissaBits - kF16MantissaBits)) - 1);
     } else if (abs_value >= kSmallestSubnormalValue) {
         // Value should be quantized to a subnormal f16 value.
 
         // Get the biased exponent `e` of f32 value, e.g. value 127 representing exponent 2^0.
-        uint32_t biased_exponent_original = (u32 & exponent_mask) >> 23;
+        uint32_t biased_exponent_original = (u32 & kF32ExponentMask) >> kF32MantissaBits;
         // Since we ensure that kSmallestValue = 0x1f-14 > abs(value) >= kSmallestSubnormalValue =
         // 0x1f-24, value will have a unbiased exponent in range -24 to -15 (inclusive), and the
         // corresponding biased exponent in f32 is in range 103 to 112 (inclusive).
         TINT_ASSERT(Semantic,
-                    (103 <= biased_exponent_original) && (biased_exponent_original <= 112));
+                    (kMinF32BiasedExpForF16SubnormalNumber <= biased_exponent_original) &&
+                        (biased_exponent_original <= kMaxF32BiasedExpForF16SubnormalNumber));
 
         // As we have proved, masking out the lowest 126-e mantissa bits of input value will result
         // in a valid subnormal f16 value, which is exactly the required quantization result.
         uint32_t discard_bits = 126 - biased_exponent_original;  // In range 14 to 23 (inclusive)
-        TINT_ASSERT(Semantic, (14 <= discard_bits) && (discard_bits <= 23));
+        TINT_ASSERT(Semantic, (14 <= discard_bits) && (discard_bits <= kF32MantissaBits));
         uint32_t discard_mask = (1u << discard_bits) - 1;
         u32 = u32 & ~discard_mask;
     } else {
@@ -200,15 +259,11 @@
         // to zero.
         return value > 0 ? 0.0 : -0.0;
     }
-    memcpy(&value, &u32, 4);
-    return value;
+
+    return utils::Bitcast<f16::type>(u32);
 }
 
 uint16_t f16::BitsRepresentation() const {
-    constexpr uint16_t f16_nan = 0x7e00u;
-    constexpr uint16_t f16_pos_inf = 0x7c00u;
-    constexpr uint16_t f16_neg_inf = 0xfc00u;
-
     // Assert we use binary32 (i.e. float) as underlying type, which has 4 bytes.
     static_assert(std::is_same<f16::type, float>());
 
@@ -216,11 +271,11 @@
     // Inf, or exactly representable by normal or subnormal f16.
 
     if (std::isnan(value)) {
-        return f16_nan;
+        return kF16Nan;
     }
 
     if (std::isinf(value)) {
-        return value > 0 ? f16_pos_inf : f16_neg_inf;
+        return value > 0 ? kF16PosInf : kF16NegInf;
     }
 
     // Now quantized_value must be a finite f16 exactly-representable value.
@@ -230,56 +285,37 @@
     // |------------------|----------------|------------------|------------------|
     // |     +/- zero     |        \       |         0        |         0        |
     // |  Subnormal f16   |   [-24, -15]   |         0        |    [103, 112]    |
-    // |    Normal f16    |   [-14, 15]    |      [1, 30]     |    [113, 142]    |
+    // |    Normal f16    |   [-14,  15]   |      [1, 30]     |    [113, 142]    |
     // ---------------------------------------------------------------------------
 
-    constexpr uint32_t max_f32_biased_exp_for_f16_normal_number = 142;
-    constexpr uint32_t min_f32_biased_exp_for_f16_normal_number = 113;
-    constexpr uint32_t max_f32_biased_exp_for_f16_subnormal_number = 112;
-    constexpr uint32_t min_f32_biased_exp_for_f16_subnormal_number = 103;
+    uint32_t f32_bit_pattern = utils::Bitcast<uint32_t>(value);
+    uint32_t f32_biased_exponent = (f32_bit_pattern & kF32ExponentMask) >> kF32MantissaBits;
+    uint32_t f32_mantissa = f32_bit_pattern & kF32MantissaMask;
 
-    constexpr uint32_t f32_sign_mask = 0x80000000u;
-    constexpr uint32_t f32_exp_mask = 0x7f800000u;
-    constexpr uint32_t f32_mantissa_mask = 0x007fffffu;
-    constexpr uint32_t f32_mantissa_bis_number = 23;
-    constexpr uint32_t f32_exp_bias = 127;
+    uint16_t f16_sign_part = static_cast<uint16_t>((f32_bit_pattern & kF32SignMask) >> 16);
+    TINT_ASSERT(Semantic, (f16_sign_part & ~kF16SignMask) == 0);
 
-    constexpr uint16_t f16_sign_mask = 0x8000u;
-    constexpr uint16_t f16_exp_mask = 0x7c00u;
-    constexpr uint16_t f16_mantissa_mask = 0x03ffu;
-    constexpr uint32_t f16_mantissa_bis_number = 10;
-    constexpr uint32_t f16_exp_bias = 15;
-
-    uint32_t f32_bit_pattern;
-    memcpy(&f32_bit_pattern, &value, 4);
-    uint32_t f32_biased_exponent = (f32_bit_pattern & f32_exp_mask) >> f32_mantissa_bis_number;
-    uint32_t f32_mantissa = f32_bit_pattern & f32_mantissa_mask;
-
-    uint16_t f16_sign_part = static_cast<uint16_t>((f32_bit_pattern & f32_sign_mask) >> 16);
-    TINT_ASSERT(Semantic, (f16_sign_part & ~f16_sign_mask) == 0);
-
-    if ((f32_bit_pattern & ~f32_sign_mask) == 0) {
+    if ((f32_bit_pattern & ~kF32SignMask) == 0) {
         // +/- zero
         return f16_sign_part;
     }
 
-    if ((min_f32_biased_exp_for_f16_normal_number <= f32_biased_exponent) &&
-        (f32_biased_exponent <= max_f32_biased_exp_for_f16_normal_number)) {
+    if ((kMinF32BiasedExpForF16NormalNumber <= f32_biased_exponent) &&
+        (f32_biased_exponent <= kMaxF32BiasedExpForF16NormalNumber)) {
         // Normal f16
-        uint32_t f16_biased_exponent = f32_biased_exponent - f32_exp_bias + f16_exp_bias;
-        uint16_t f16_exp_part =
-            static_cast<uint16_t>(f16_biased_exponent << f16_mantissa_bis_number);
-        uint16_t f16_mantissa_part = static_cast<uint16_t>(
-            f32_mantissa >> (f32_mantissa_bis_number - f16_mantissa_bis_number));
+        uint32_t f16_biased_exponent = f32_biased_exponent - kF32ExponentBias + kF16ExponentBias;
+        uint16_t f16_exp_part = static_cast<uint16_t>(f16_biased_exponent << kF16MantissaBits);
+        uint16_t f16_mantissa_part =
+            static_cast<uint16_t>(f32_mantissa >> (kF32MantissaBits - kF16MantissaBits));
 
-        TINT_ASSERT(Semantic, (f16_exp_part & ~f16_exp_mask) == 0);
-        TINT_ASSERT(Semantic, (f16_mantissa_part & ~f16_mantissa_mask) == 0);
+        TINT_ASSERT(Semantic, (f16_exp_part & ~kF16ExponentMask) == 0);
+        TINT_ASSERT(Semantic, (f16_mantissa_part & ~kF16MantissaMask) == 0);
 
         return f16_sign_part | f16_exp_part | f16_mantissa_part;
     }
 
-    if ((min_f32_biased_exp_for_f16_subnormal_number <= f32_biased_exponent) &&
-        (f32_biased_exponent <= max_f32_biased_exp_for_f16_subnormal_number)) {
+    if ((kMinF32BiasedExpForF16SubnormalNumber <= f32_biased_exponent) &&
+        (f32_biased_exponent <= kMaxF32BiasedExpForF16SubnormalNumber)) {
         // Subnormal f16
         // The resulting exp bits are always 0, and the mantissa bits should be handled specially.
         uint16_t f16_exp_part = 0;
@@ -287,15 +323,15 @@
         // of value is of the minimum, i.e. -24; and have all 10 mantissa bits valid if the unbiased
         // exponent of value is of the maximum, i.e. -15.
         uint32_t f16_valid_mantissa_bits =
-            f32_biased_exponent - min_f32_biased_exp_for_f16_subnormal_number + 1;
+            f32_biased_exponent - kMinF32BiasedExpForF16SubnormalNumber + 1;
         // The resulting f16 mantissa part comes from right-shifting the f32 mantissa bits with
         // leading 1 added.
         uint16_t f16_mantissa_part =
-            static_cast<uint16_t>((f32_mantissa | (f32_mantissa_mask + 1)) >>
-                                  (f32_mantissa_bis_number + 1 - f16_valid_mantissa_bits));
+            static_cast<uint16_t>((f32_mantissa | (kF32MantissaMask + 1)) >>
+                                  (kF32MantissaBits + 1 - f16_valid_mantissa_bits));
 
         TINT_ASSERT(Semantic, (1 <= f16_valid_mantissa_bits) &&
-                                  (f16_valid_mantissa_bits <= f16_mantissa_bis_number));
+                                  (f16_valid_mantissa_bits <= kF16MantissaBits));
         TINT_ASSERT(Semantic, (f16_mantissa_part & ~((1u << f16_valid_mantissa_bits) - 1)) == 0);
         TINT_ASSERT(Semantic, (f16_mantissa_part != 0));
 
@@ -305,7 +341,76 @@
     // Neither zero, subnormal f16 or normal f16, shall never hit.
     tint::diag::List diag;
     TINT_UNREACHABLE(Semantic, diag);
-    return f16_nan;
+    return kF16Nan;
+}
+
+// static
+Number<detail::NumberKindF16> f16::FromBits(uint16_t bits) {
+    // Assert we use binary32 (i.e. float) as underlying type, which has 4 bytes.
+    static_assert(std::is_same<f16::type, float>());
+
+    if (bits == kF16PosInf) {
+        return f16(std::numeric_limits<f16::type>::infinity());
+    }
+    if (bits == kF16NegInf) {
+        return f16(-std::numeric_limits<f16::type>::infinity());
+    }
+
+    auto f16_sign_bit = uint32_t(bits & kF16SignMask);
+    // If none of the other bits are set we have a 0. If only the sign bit is set we have a -0.
+    if ((bits & ~kF16SignMask) == 0) {
+        return f16(f16_sign_bit > 0 ? -0.f : 0.f);
+    }
+
+    auto f16_mantissa = uint32_t(bits & kF16MantissaMask);
+    auto f16_biased_exponent = uint32_t(bits & kF16ExponentMask);
+
+    // F16 NaN has all expoennt bits set and at least one mantissa bit set
+    if (((f16_biased_exponent & kF16ExponentMask) == kF16ExponentMask) && f16_mantissa != 0) {
+        return f16(std::numeric_limits<f16::type>::quiet_NaN());
+    }
+
+    // Shift the exponent over to be a regular number.
+    f16_biased_exponent >>= kF16MantissaBits;
+
+    // Add the F32 bias and remove the F16 bias.
+    uint32_t f32_biased_exponent = f16_biased_exponent + kF32ExponentBias - kF16ExponentBias;
+
+    if (f16_biased_exponent == 0) {
+        // Subnormal number
+        //
+        // All subnormal F16 values can be represented as normal F32 values. Shift the mantissa and
+        // set the exponent as if this was a normal f16 value.
+
+        // While the first F16 exponent bit is not set
+        constexpr uint32_t kF16FirstExponentBit = 0x0400;
+        while ((f16_mantissa & kF16FirstExponentBit) == 0) {
+            // Shift the mantissa to the left
+            f16_mantissa <<= 1;
+            // Decrease the biased exponent to counter the shift
+            f32_biased_exponent -= 1;
+        }
+
+        // Remove the first exponent bit from the mantissa value
+        f16_mantissa &= ~kF16FirstExponentBit;
+        // Increase the exponent to deal with the masked off value.
+        f32_biased_exponent += 1;
+    }
+
+    // The mantissa bits are shifted over the difference in mantissa size to be in the F32 location.
+    uint32_t f32_mantissa = f16_mantissa << (kF32MantissaBits - kF16MantissaBits);
+
+    // Shift the exponent to the F32 exponent position before the mantissa.
+    f32_biased_exponent <<= kF32MantissaBits;
+
+    // Shift the sign bit over to the f32 sign bit position
+    uint32_t f32_sign_bit = f16_sign_bit << 16;
+
+    // Combine values together into the F32 value as a uint32_t.
+    uint32_t val = f32_sign_bit | f32_biased_exponent | f32_mantissa;
+
+    // Bitcast to a F32 and then store into the F16 Number
+    return f16(utils::Bitcast<f16::type>(val));
 }
 
 }  // namespace tint
diff --git a/src/tint/number.h b/src/tint/number.h
index 821e486..dc66689 100644
--- a/src/tint/number.h
+++ b/src/tint/number.h
@@ -124,6 +124,9 @@
     /// type is the underlying type of the Number
     using type = T;
 
+    /// Number of bits in the number.
+    static constexpr size_t kNumBits = sizeof(T) * 8;
+
     /// Highest finite representable value of this type.
     static constexpr type kHighestValue = std::numeric_limits<type>::max();
 
@@ -187,6 +190,9 @@
     /// C++ does not have a native float16 type, so we use a 32-bit float instead.
     using type = float;
 
+    /// Number of bits in the number.
+    static constexpr size_t kNumBits = 16;
+
     /// Highest finite representable value of this type.
     static constexpr type kHighestValue = 65504.0f;  // 2¹⁵ × (1 + 1023/1024)
 
@@ -237,6 +243,11 @@
     /// will be 0xfc00u.
     uint16_t BitsRepresentation() const;
 
+    /// Creates an f16 value from the uint16_t bit representation.
+    /// @param bits the bits to convert from
+    /// @returns the binary16 value based off the provided bit pattern.
+    static Number<detail::NumberKindF16> FromBits(uint16_t bits);
+
     /// @param value the input float32 value
     /// @returns the float32 value quantized to the smaller float16 value, through truncation of the
     /// mantissa bits (no rounding). If the float32 value is too large (positive or negative) to be
@@ -263,6 +274,9 @@
 /// However since C++ don't have native binary16 type, the value is stored as float.
 using f16 = Number<detail::NumberKindF16>;
 
+template <typename T, traits::EnableIf<IsFloatingPoint<T>>* = nullptr>
+inline const auto kPi = T(UnwrapNumber<T>(3.14159265358979323846));
+
 /// True iff T is an abstract number type
 template <typename T>
 constexpr bool IsAbstract = std::is_same_v<T, AInt> || std::is_same_v<T, AFloat>;
@@ -420,13 +434,14 @@
     return AInt(result);
 }
 
-/// @returns a + b, or an empty optional if the resulting value overflowed the AFloat
-inline std::optional<AFloat> CheckedAdd(AFloat a, AFloat b) {
-    auto result = a.value + b.value;
-    if (!std::isfinite(result)) {
+/// @returns a + b, or an empty optional if the resulting value overflowed the float value
+template <typename FloatingPointT, typename = traits::EnableIf<IsFloatingPoint<FloatingPointT>>>
+inline std::optional<FloatingPointT> CheckedAdd(FloatingPointT a, FloatingPointT b) {
+    auto result = FloatingPointT{a.value + b.value};
+    if (!std::isfinite(result.value)) {
         return {};
     }
-    return AFloat{result};
+    return result;
 }
 
 /// @returns a - b, or an empty optional if the resulting value overflowed the AInt
@@ -451,13 +466,14 @@
     return AInt(result);
 }
 
-/// @returns a + b, or an empty optional if the resulting value overflowed the AFloat
-inline std::optional<AFloat> CheckedSub(AFloat a, AFloat b) {
-    auto result = a.value - b.value;
-    if (!std::isfinite(result)) {
+/// @returns a + b, or an empty optional if the resulting value overflowed the float value
+template <typename FloatingPointT, typename = traits::EnableIf<IsFloatingPoint<FloatingPointT>>>
+inline std::optional<FloatingPointT> CheckedSub(FloatingPointT a, FloatingPointT b) {
+    auto result = FloatingPointT{a.value - b.value};
+    if (!std::isfinite(result.value)) {
         return {};
     }
-    return AFloat{result};
+    return result;
 }
 
 /// @returns a * b, or an empty optional if the resulting value overflowed the AInt
@@ -494,13 +510,14 @@
     return AInt(result);
 }
 
-/// @returns a * b, or an empty optional if the resulting value overflowed the AFloat
-inline std::optional<AFloat> CheckedMul(AFloat a, AFloat b) {
-    auto result = a.value * b.value;
-    if (!std::isfinite(result)) {
+/// @returns a * b, or an empty optional if the resulting value overflowed the float value
+template <typename FloatingPointT, typename = traits::EnableIf<IsFloatingPoint<FloatingPointT>>>
+inline std::optional<FloatingPointT> CheckedMul(FloatingPointT a, FloatingPointT b) {
+    auto result = FloatingPointT{a.value * b.value};
+    if (!std::isfinite(result.value)) {
         return {};
     }
-    return AFloat{result};
+    return result;
 }
 
 /// @returns a / b, or an empty optional if the resulting value overflowed the AInt
@@ -516,13 +533,14 @@
     return AInt{a.value / b.value};
 }
 
-/// @returns a / b, or an empty optional if the resulting value overflowed the AFloat
-inline std::optional<AFloat> CheckedDiv(AFloat a, AFloat b) {
-    auto result = a.value / b.value;
-    if (!std::isfinite(result)) {
+/// @returns a / b, or an empty optional if the resulting value overflowed the float value
+template <typename FloatingPointT, typename = traits::EnableIf<IsFloatingPoint<FloatingPointT>>>
+inline std::optional<FloatingPointT> CheckedDiv(FloatingPointT a, FloatingPointT b) {
+    auto result = FloatingPointT{a.value / b.value};
+    if (!std::isfinite(result.value)) {
         return {};
     }
-    return AFloat{result};
+    return result;
 }
 
 /// @returns a * b + c, or an empty optional if the value overflowed the AInt
diff --git a/src/tint/number_test.cc b/src/tint/number_test.cc
index 15d96e7..f21e531 100644
--- a/src/tint/number_test.cc
+++ b/src/tint/number_test.cc
@@ -14,6 +14,7 @@
 
 #include <cmath>
 #include <tuple>
+#include <variant>
 #include <vector>
 
 #include "src/tint/program_builder.h"
@@ -26,6 +27,15 @@
 namespace tint {
 namespace {
 
+// Concats any number of std::vectors
+template <typename Vec, typename... Vecs>
+[[nodiscard]] inline auto Concat(Vec&& v1, Vecs&&... vs) {
+    auto total_size = v1.size() + (vs.size() + ...);
+    v1.reserve(total_size);
+    (std::move(vs.begin(), vs.end(), std::back_inserter(v1)), ...);
+    return std::move(v1);
+}
+
 // Next ULP up from kHighestF32 for a float64.
 constexpr double kHighestF32NextULP = 0x1.fffffe0000001p+127;
 
@@ -268,6 +278,22 @@
     EXPECT_EQ(f16(input_value).BitsRepresentation(), representation);
 }
 
+TEST_P(NumberF16Test, FromBits) {
+    float input_value = GetParam().quantized_value;
+    uint16_t representation = GetParam().f16_bit_pattern;
+
+    std::stringstream ss;
+    ss << "binary16 bits representation = " << std::hex << std::showbase << representation
+       << " expected value = " << input_value;
+    SCOPED_TRACE(ss.str());
+
+    if (std::isnan(input_value)) {
+        EXPECT_TRUE(std::isnan(f16::FromBits(representation)));
+    } else {
+        EXPECT_EQ(f16::FromBits(representation), f16(input_value));
+    }
+}
+
 INSTANTIATE_TEST_SUITE_P(
     NumberF16Test,
     NumberF16Test,
@@ -362,16 +388,23 @@
 #define OVERFLOW \
     {}
 
-using BinaryCheckedCase_AInt = std::tuple<std::optional<AInt>, AInt, AInt>;
-using BinaryCheckedCase_AFloat = std::tuple<std::optional<AFloat>, AFloat, AFloat>;
+template <typename T>
+auto Overflow = std::optional<T>{};
 
+using BinaryCheckedCase_AInt = std::tuple<std::optional<AInt>, AInt, AInt>;
 using CheckedAddTest_AInt = testing::TestWithParam<BinaryCheckedCase_AInt>;
+
+using FloatInputTypes = std::variant<AFloat, f32, f16>;
+using FloatExpectedTypes =
+    std::variant<std::optional<AFloat>, std::optional<f32>, std::optional<f16>>;
+using BinaryCheckedCase_Float = std::tuple<FloatExpectedTypes, FloatInputTypes, FloatInputTypes>;
+
 TEST_P(CheckedAddTest_AInt, Test) {
     auto expect = std::get<0>(GetParam());
     auto a = std::get<1>(GetParam());
     auto b = std::get<2>(GetParam());
-    EXPECT_EQ(CheckedAdd(a, b), expect) << std::hex << "0x" << a << " + 0x" << b;
-    EXPECT_EQ(CheckedAdd(b, a), expect) << std::hex << "0x" << a << " + 0x" << b;
+    EXPECT_TRUE(CheckedAdd(a, b) == expect) << std::hex << "0x" << a << " + 0x" << b;
+    EXPECT_TRUE(CheckedAdd(b, a) == expect) << std::hex << "0x" << a << " + 0x" << b;
 }
 INSTANTIATE_TEST_SUITE_P(
     CheckedAddTest_AInt,
@@ -401,41 +434,50 @@
         ////////////////////////////////////////////////////////////////////////
     }));
 
-using CheckedAddTest_AFloat = testing::TestWithParam<BinaryCheckedCase_AFloat>;
-TEST_P(CheckedAddTest_AFloat, Test) {
-    auto expect = std::get<0>(GetParam());
-    auto a = std::get<1>(GetParam());
-    auto b = std::get<2>(GetParam());
-    EXPECT_EQ(CheckedAdd(a, b), expect) << std::hex << "0x" << a << " + 0x" << b;
-    EXPECT_EQ(CheckedAdd(b, a), expect) << std::hex << "0x" << a << " + 0x" << b;
+using CheckedAddTest_Float = testing::TestWithParam<BinaryCheckedCase_Float>;
+TEST_P(CheckedAddTest_Float, Test) {
+    auto& p = GetParam();
+    std::visit(
+        [&](auto&& lhs) {
+            using T = std::decay_t<decltype(lhs)>;
+            auto rhs = std::get<T>(std::get<2>(p));
+            auto expect = std::get<std::optional<T>>(std::get<0>(p));
+            EXPECT_TRUE(CheckedAdd(lhs, rhs) == expect)
+                << std::hex << "0x" << lhs << " + 0x" << rhs;
+            EXPECT_TRUE(CheckedAdd(rhs, lhs) == expect)
+                << std::hex << "0x" << lhs << " + 0x" << rhs;
+        },
+        std::get<1>(p));
 }
-INSTANTIATE_TEST_SUITE_P(
-    CheckedAddTest_AFloat,
-    CheckedAddTest_AFloat,
-    testing::ValuesIn(std::vector<BinaryCheckedCase_AFloat>{
-        {AFloat(0), AFloat(0), AFloat(0)},
-        {AFloat(1), AFloat(1), AFloat(0)},
-        {AFloat(2), AFloat(1), AFloat(1)},
-        {AFloat(0), AFloat(-1), AFloat(1)},
-        {AFloat(3), AFloat(2), AFloat(1)},
-        {AFloat(-1), AFloat(-2), AFloat(1)},
-        {AFloat(0x300), AFloat(0x100), AFloat(0x200)},
-        {AFloat(0x100), AFloat(-0x100), AFloat(0x200)},
-        {AFloat::Highest(), AFloat(1), AFloat(AFloat::kHighestValue - 1)},
-        {AFloat::Lowest(), AFloat(-1), AFloat(AFloat::kLowestValue + 1)},
-        {AFloat::Highest(), AFloat::Highest(), AFloat(0)},
-        {AFloat::Lowest(), AFloat::Lowest(), AFloat(0)},
-        {OVERFLOW, AFloat::Highest(), AFloat::Highest()},
-        {OVERFLOW, AFloat::Lowest(), AFloat::Lowest()},
-        ////////////////////////////////////////////////////////////////////////
-    }));
+template <typename T>
+std::vector<BinaryCheckedCase_Float> CheckedAddTest_FloatCases() {
+    return {
+        {T(0), T(0), T(0)},
+        {T(1), T(1), T(0)},
+        {T(2), T(1), T(1)},
+        {T(0), T(-1), T(1)},
+        {T(3), T(2), T(1)},
+        {T(-1), T(-2), T(1)},
+        {T(0x300), T(0x100), T(0x200)},
+        {T(0x100), T(-0x100), T(0x200)},
+        {T::Highest(), T::Highest(), T(0)},
+        {T::Lowest(), T::Lowest(), T(0)},
+        {Overflow<T>, T::Highest(), T::Highest()},
+        {Overflow<T>, T::Lowest(), T::Lowest()},
+    };
+}
+INSTANTIATE_TEST_SUITE_P(CheckedAddTest_Float,
+                         CheckedAddTest_Float,
+                         testing::ValuesIn(Concat(CheckedAddTest_FloatCases<AFloat>(),
+                                                  CheckedAddTest_FloatCases<f32>(),
+                                                  CheckedAddTest_FloatCases<f16>())));
 
 using CheckedSubTest_AInt = testing::TestWithParam<BinaryCheckedCase_AInt>;
 TEST_P(CheckedSubTest_AInt, Test) {
     auto expect = std::get<0>(GetParam());
     auto a = std::get<1>(GetParam());
     auto b = std::get<2>(GetParam());
-    EXPECT_EQ(CheckedSub(a, b), expect) << std::hex << "0x" << a << " - 0x" << b;
+    EXPECT_TRUE(CheckedSub(a, b) == expect) << std::hex << "0x" << a << " - 0x" << b;
 }
 INSTANTIATE_TEST_SUITE_P(
     CheckedSubTest_AInt,
@@ -464,40 +506,48 @@
         ////////////////////////////////////////////////////////////////////////
     }));
 
-using CheckedSubTest_AFloat = testing::TestWithParam<BinaryCheckedCase_AFloat>;
-TEST_P(CheckedSubTest_AFloat, Test) {
-    auto expect = std::get<0>(GetParam());
-    auto a = std::get<1>(GetParam());
-    auto b = std::get<2>(GetParam());
-    EXPECT_EQ(CheckedSub(a, b), expect) << std::hex << "0x" << a << " - 0x" << b;
+using CheckedSubTest_Float = testing::TestWithParam<BinaryCheckedCase_Float>;
+TEST_P(CheckedSubTest_Float, Test) {
+    auto& p = GetParam();
+    std::visit(
+        [&](auto&& lhs) {
+            using T = std::decay_t<decltype(lhs)>;
+            auto rhs = std::get<T>(std::get<2>(p));
+            auto expect = std::get<std::optional<T>>(std::get<0>(p));
+            EXPECT_TRUE(CheckedSub(lhs, rhs) == expect)
+                << std::hex << "0x" << lhs << " - 0x" << rhs;
+        },
+        std::get<1>(p));
 }
-INSTANTIATE_TEST_SUITE_P(
-    CheckedSubTest_AFloat,
-    CheckedSubTest_AFloat,
-    testing::ValuesIn(std::vector<BinaryCheckedCase_AFloat>{
-        {AFloat(0), AFloat(0), AFloat(0)},
-        {AFloat(1), AFloat(1), AFloat(0)},
-        {AFloat(0), AFloat(1), AFloat(1)},
-        {AFloat(-2), AFloat(-1), AFloat(1)},
-        {AFloat(1), AFloat(2), AFloat(1)},
-        {AFloat(-3), AFloat(-2), AFloat(1)},
-        {AFloat(0x100), AFloat(0x300), AFloat(0x200)},
-        {AFloat(-0x300), AFloat(-0x100), AFloat(0x200)},
-        {AFloat::Highest(), AFloat(AFloat::kHighestValue - 1), AFloat(-1)},
-        {AFloat::Lowest(), AFloat(AFloat::kLowestValue + 1), AFloat(1)},
-        {AFloat::Highest(), AFloat::Highest(), AFloat(0)},
-        {AFloat::Lowest(), AFloat::Lowest(), AFloat(0)},
-        {OVERFLOW, AFloat::Lowest(), AFloat::Highest()},
-        ////////////////////////////////////////////////////////////////////////
-    }));
+template <typename T>
+std::vector<BinaryCheckedCase_Float> CheckedSubTest_FloatCases() {
+    return {
+        {T(0), T(0), T(0)},
+        {T(1), T(1), T(0)},
+        {T(0), T(1), T(1)},
+        {T(-2), T(-1), T(1)},
+        {T(1), T(2), T(1)},
+        {T(-3), T(-2), T(1)},
+        {T(0x100), T(0x300), T(0x200)},
+        {T(-0x300), T(-0x100), T(0x200)},
+        {T::Highest(), T::Highest(), T(0)},
+        {T::Lowest(), T::Lowest(), T(0)},
+        {Overflow<T>, T::Lowest(), T::Highest()},
+    };
+}
+INSTANTIATE_TEST_SUITE_P(CheckedSubTest_Float,
+                         CheckedSubTest_Float,
+                         testing::ValuesIn(Concat(CheckedSubTest_FloatCases<AFloat>(),
+                                                  CheckedSubTest_FloatCases<f32>(),
+                                                  CheckedSubTest_FloatCases<f16>())));
 
 using CheckedMulTest_AInt = testing::TestWithParam<BinaryCheckedCase_AInt>;
 TEST_P(CheckedMulTest_AInt, Test) {
     auto expect = std::get<0>(GetParam());
     auto a = std::get<1>(GetParam());
     auto b = std::get<2>(GetParam());
-    EXPECT_EQ(CheckedMul(a, b), expect) << std::hex << "0x" << a << " * 0x" << b;
-    EXPECT_EQ(CheckedMul(b, a), expect) << std::hex << "0x" << a << " * 0x" << b;
+    EXPECT_TRUE(CheckedMul(a, b) == expect) << std::hex << "0x" << a << " * 0x" << b;
+    EXPECT_TRUE(CheckedMul(b, a) == expect) << std::hex << "0x" << a << " * 0x" << b;
 }
 INSTANTIATE_TEST_SUITE_P(
     CheckedMulTest_AInt,
@@ -537,12 +587,48 @@
         ////////////////////////////////////////////////////////////////////////
     }));
 
+using CheckedMulTest_Float = testing::TestWithParam<BinaryCheckedCase_Float>;
+TEST_P(CheckedMulTest_Float, Test) {
+    auto& p = GetParam();
+    std::visit(
+        [&](auto&& lhs) {
+            using T = std::decay_t<decltype(lhs)>;
+            auto rhs = std::get<T>(std::get<2>(p));
+            auto expect = std::get<std::optional<T>>(std::get<0>(p));
+            EXPECT_TRUE(CheckedMul(lhs, rhs) == expect)
+                << std::hex << "0x" << lhs << " * 0x" << rhs;
+            EXPECT_TRUE(CheckedMul(rhs, lhs) == expect)
+                << std::hex << "0x" << lhs << " * 0x" << rhs;
+        },
+        std::get<1>(p));
+}
+template <typename T>
+std::vector<BinaryCheckedCase_Float> CheckedMulTest_FloatCases() {
+    return {
+        {T(0), T(0), T(0)},
+        {T(0), T(1), T(0)},
+        {T(1), T(1), T(1)},
+        {T(-1), T(-1), T(1)},
+        {T(2), T(2), T(1)},
+        {T(-2), T(-2), T(1)},
+        {T(0), T::Highest(), T(0)},
+        {T(0), T::Lowest(), -T(0)},
+        {Overflow<T>, T::Highest(), T::Highest()},
+        {Overflow<T>, T::Lowest(), T::Lowest()},
+    };
+}
+INSTANTIATE_TEST_SUITE_P(CheckedMulTest_Float,
+                         CheckedMulTest_Float,
+                         testing::ValuesIn(Concat(CheckedMulTest_FloatCases<AFloat>(),
+                                                  CheckedMulTest_FloatCases<f32>(),
+                                                  CheckedMulTest_FloatCases<f16>())));
+
 using CheckedDivTest_AInt = testing::TestWithParam<BinaryCheckedCase_AInt>;
 TEST_P(CheckedDivTest_AInt, Test) {
     auto expect = std::get<0>(GetParam());
     auto a = std::get<1>(GetParam());
     auto b = std::get<2>(GetParam());
-    EXPECT_EQ(CheckedDiv(a, b), expect) << std::hex << "0x" << a << " - 0x" << b;
+    EXPECT_TRUE(CheckedDiv(a, b) == expect) << std::hex << "0x" << a << " - 0x" << b;
 }
 INSTANTIATE_TEST_SUITE_P(
     CheckedDivTest_AInt,
@@ -563,33 +649,43 @@
         ////////////////////////////////////////////////////////////////////////
     }));
 
-using CheckedDivTest_AFloat = testing::TestWithParam<BinaryCheckedCase_AFloat>;
-TEST_P(CheckedDivTest_AFloat, Test) {
-    auto expect = std::get<0>(GetParam());
-    auto a = std::get<1>(GetParam());
-    auto b = std::get<2>(GetParam());
-    EXPECT_EQ(CheckedDiv(a, b), expect) << std::hex << "0x" << a << " - 0x" << b;
+using CheckedDivTest_Float = testing::TestWithParam<BinaryCheckedCase_Float>;
+TEST_P(CheckedDivTest_Float, Test) {
+    auto& p = GetParam();
+    std::visit(
+        [&](auto&& lhs) {
+            using T = std::decay_t<decltype(lhs)>;
+            auto rhs = std::get<T>(std::get<2>(p));
+            auto expect = std::get<std::optional<T>>(std::get<0>(p));
+            EXPECT_TRUE(CheckedDiv(lhs, rhs) == expect)
+                << std::hex << "0x" << lhs << " / 0x" << rhs;
+        },
+        std::get<1>(p));
 }
-INSTANTIATE_TEST_SUITE_P(
-    CheckedDivTest_AFloat,
-    CheckedDivTest_AFloat,
-    testing::ValuesIn(std::vector<BinaryCheckedCase_AFloat>{
-        {AFloat(0), AFloat(0), AFloat(1)},
-        {AFloat(1), AFloat(1), AFloat(1)},
-        {AFloat(1), AFloat(1), AFloat(1)},
-        {AFloat(2), AFloat(2), AFloat(1)},
-        {AFloat(2), AFloat(4), AFloat(2)},
-        {AFloat::Highest(), AFloat::Highest(), AFloat(1)},
-        {AFloat::Lowest(), AFloat::Lowest(), AFloat(1)},
-        {AFloat(1), AFloat::Highest(), AFloat::Highest()},
-        {AFloat(0), AFloat(0), AFloat::Highest()},
-        {-AFloat(0), AFloat(0), AFloat::Lowest()},
-        {OVERFLOW, AFloat(123), AFloat(0)},
-        {OVERFLOW, AFloat(123), AFloat(-0)},
-        {OVERFLOW, AFloat(-123), AFloat(0)},
-        {OVERFLOW, AFloat(-123), AFloat(-0)},
-        ////////////////////////////////////////////////////////////////////////
-    }));
+template <typename T>
+std::vector<BinaryCheckedCase_Float> CheckedDivTest_FloatCases() {
+    return {
+        {T(0), T(0), T(1)},
+        {T(1), T(1), T(1)},
+        {T(1), T(1), T(1)},
+        {T(2), T(2), T(1)},
+        {T(2), T(4), T(2)},
+        {T::Highest(), T::Highest(), T(1)},
+        {T::Lowest(), T::Lowest(), T(1)},
+        {T(1), T::Highest(), T::Highest()},
+        {T(0), T(0), T::Highest()},
+        {-T(0), T(0), T::Lowest()},
+        {Overflow<T>, T(123), T(0)},
+        {Overflow<T>, T(123), T(-0)},
+        {Overflow<T>, T(-123), T(0)},
+        {Overflow<T>, T(-123), T(-0)},
+    };
+}
+INSTANTIATE_TEST_SUITE_P(CheckedDivTest_Float,
+                         CheckedDivTest_Float,
+                         testing::ValuesIn(Concat(CheckedDivTest_FloatCases<AFloat>(),
+                                                  CheckedDivTest_FloatCases<f32>(),
+                                                  CheckedDivTest_FloatCases<f16>())));
 
 using TernaryCheckedCase = std::tuple<std::optional<AInt>, AInt, AInt, AInt>;
 
diff --git a/src/tint/program_builder.h b/src/tint/program_builder.h
index 2f5b5e0..d6d1f00 100644
--- a/src/tint/program_builder.h
+++ b/src/tint/program_builder.h
@@ -47,7 +47,6 @@
 #include "src/tint/ast/external_texture.h"
 #include "src/tint/ast/f16.h"
 #include "src/tint/ast/f32.h"
-#include "src/tint/ast/fallthrough_statement.h"
 #include "src/tint/ast/float_literal_expression.h"
 #include "src/tint/ast/for_loop_statement.h"
 #include "src/tint/ast/i32.h"
@@ -2932,17 +2931,6 @@
     /// @returns the selector pointer
     const ast::CaseSelector* DefaultCaseSelector() { return create<ast::CaseSelector>(nullptr); }
 
-    /// Creates an ast::FallthroughStatement
-    /// @param source the source information
-    /// @returns the fallthrough statement pointer
-    const ast::FallthroughStatement* Fallthrough(const Source& source) {
-        return create<ast::FallthroughStatement>(source);
-    }
-
-    /// Creates an ast::FallthroughStatement
-    /// @returns the fallthrough statement pointer
-    const ast::FallthroughStatement* Fallthrough() { return create<ast::FallthroughStatement>(); }
-
     /// Creates an ast::BuiltinAttribute
     /// @param source the source information
     /// @param builtin the builtin value
@@ -3267,6 +3255,14 @@
 //! @cond Doxygen_Suppress
 // Various template specializations for ProgramBuilder::TypesBuilder::CToAST.
 template <>
+struct ProgramBuilder::TypesBuilder::CToAST<AInt> {
+    static const ast::Type* get(const ProgramBuilder::TypesBuilder*) { return nullptr; }
+};
+template <>
+struct ProgramBuilder::TypesBuilder::CToAST<AFloat> {
+    static const ast::Type* get(const ProgramBuilder::TypesBuilder*) { return nullptr; }
+};
+template <>
 struct ProgramBuilder::TypesBuilder::CToAST<i32> {
     static const ast::Type* get(const ProgramBuilder::TypesBuilder* t) { return t->i32(); }
 };
diff --git a/src/tint/reader/spirv/enum_converter.cc b/src/tint/reader/spirv/enum_converter.cc
index d590507..8c70156 100644
--- a/src/tint/reader/spirv/enum_converter.cc
+++ b/src/tint/reader/spirv/enum_converter.cc
@@ -82,6 +82,8 @@
             return ast::BuiltinValue::kLocalInvocationIndex;
         case spv::BuiltIn::GlobalInvocationId:
             return ast::BuiltinValue::kGlobalInvocationId;
+        case spv::BuiltIn::NumWorkgroups:
+            return ast::BuiltinValue::kNumWorkgroups;
         case spv::BuiltIn::WorkgroupId:
             return ast::BuiltinValue::kWorkgroupId;
         case spv::BuiltIn::SampleId:
diff --git a/src/tint/reader/spirv/enum_converter_test.cc b/src/tint/reader/spirv/enum_converter_test.cc
index b366ebd..bdb0247 100644
--- a/src/tint/reader/spirv/enum_converter_test.cc
+++ b/src/tint/reader/spirv/enum_converter_test.cc
@@ -192,6 +192,7 @@
         BuiltinCase{spv::BuiltIn::LocalInvocationIndex, true,
                     ast::BuiltinValue::kLocalInvocationIndex},
         BuiltinCase{spv::BuiltIn::GlobalInvocationId, true, ast::BuiltinValue::kGlobalInvocationId},
+        BuiltinCase{spv::BuiltIn::NumWorkgroups, true, ast::BuiltinValue::kNumWorkgroups},
         BuiltinCase{spv::BuiltIn::WorkgroupId, true, ast::BuiltinValue::kWorkgroupId},
         BuiltinCase{spv::BuiltIn::SampleId, true, ast::BuiltinValue::kSampleIndex},
         BuiltinCase{spv::BuiltIn::SampleMask, true, ast::BuiltinValue::kSampleMask}));
@@ -208,8 +209,6 @@
                          testing::Values(BuiltinCase{static_cast<spv::BuiltIn>(9999), false,
                                                      ast::BuiltinValue::kUndefined},
                                          BuiltinCase{static_cast<spv::BuiltIn>(9999), false,
-                                                     ast::BuiltinValue::kUndefined},
-                                         BuiltinCase{spv::BuiltIn::NumWorkgroups, false,
                                                      ast::BuiltinValue::kUndefined}));
 
 // Dim
diff --git a/src/tint/reader/spirv/function.cc b/src/tint/reader/spirv/function.cc
index 0aafdf9..f97d169 100644
--- a/src/tint/reader/spirv/function.cc
+++ b/src/tint/reader/spirv/function.cc
@@ -25,7 +25,6 @@
 #include "src/tint/ast/call_statement.h"
 #include "src/tint/ast/continue_statement.h"
 #include "src/tint/ast/discard_statement.h"
-#include "src/tint/ast/fallthrough_statement.h"
 #include "src/tint/ast/if_statement.h"
 #include "src/tint/ast/loop_statement.h"
 #include "src/tint/ast/return_statement.h"
@@ -1514,7 +1513,13 @@
 
     ParameterList ast_params;
     function_.ForEachParam([this, &ast_params](const spvtools::opt::Instruction* param) {
-        auto* type = parser_impl_.ConvertType(param->type_id());
+        // Valid SPIR-V requires function call parameters to be non-null
+        // instructions.
+        TINT_ASSERT(Reader, param != nullptr);
+        const Type* const type = IsHandleObj(*param)
+                                     ? parser_impl_.GetHandleTypeForSpirvHandle(*param)
+                                     : parser_impl_.ConvertType(param->type_id());
+
         if (type != nullptr) {
             auto* ast_param = parser_impl_.MakeParameter(param->result_id(), type, AttributeList{});
             // Parameters are treated as const declarations.
@@ -1537,6 +1542,20 @@
     return success();
 }
 
+bool FunctionEmitter::IsHandleObj(const spvtools::opt::Instruction& obj) {
+    TINT_ASSERT(Reader, obj.type_id() != 0u);
+    auto* spirv_type = type_mgr_->GetType(obj.type_id());
+    TINT_ASSERT(Reader, spirv_type);
+    return spirv_type->AsImage() || spirv_type->AsSampler() ||
+           (spirv_type->AsPointer() &&
+            (static_cast<spv::StorageClass>(spirv_type->AsPointer()->storage_class()) ==
+             spv::StorageClass::UniformConstant));
+}
+
+bool FunctionEmitter::IsHandleObj(const spvtools::opt::Instruction* obj) {
+    return (obj != nullptr) && IsHandleObj(*obj);
+}
+
 const Type* FunctionEmitter::GetVariableStoreType(const spvtools::opt::Instruction& var_decl_inst) {
     const auto type_id = var_decl_inst.type_id();
     // Normally we use the SPIRV-Tools optimizer to manage types.
@@ -2139,8 +2158,7 @@
     // For each branch encountered, classify each edge (S,T) as:
     //    - a back-edge
     //    - a structured exit (specific ways of branching to enclosing construct)
-    //    - a normal (forward) edge, either natural control flow or a case
-    //    fallthrough
+    //    - a normal (forward) edge, either natural control flow or a case fallthrough
     //
     // If more than one block is targeted by a normal edge, then S must be a
     // structured header.
@@ -2174,11 +2192,10 @@
         // There should only be one backedge per backedge block.
         uint32_t num_backedges = 0;
 
-        // Track destinations for normal forward edges, either kForward
-        // or kCaseFallThrough. These count toward the need
-        // to have a merge instruction.  We also track kIfBreak edges
-        // because when used with normal forward edges, we'll need
-        // to generate a flow guard variable.
+        // Track destinations for normal forward edges, either kForward or kCaseFallThrough.
+        // These count toward the need to have a merge instruction.  We also track kIfBreak edges
+        // because when used with normal forward edges, we'll need to generate a flow guard
+        // variable.
         utils::Vector<uint32_t, 4> normal_forward_edges;
         utils::Vector<uint32_t, 4> if_break_edges;
 
@@ -2356,6 +2373,12 @@
                                << dest_construct.begin_id << " (dominance rule violated)";
                     }
                 }
+
+                // Error on the fallthrough at the end in order to allow the better error messages
+                // from the above checks to happen.
+                if (edge_kind == EdgeKind::kCaseFallThrough) {
+                    return Fail() << "Fallthrough not permitted in WGSL";
+                }
             }  // end forward edge
         }      // end successor
 
@@ -3187,8 +3210,8 @@
                 return false;
             }
             AddStatement(create<ast::ReturnStatement>(Source{}, value.expr));
-        }
             return true;
+        }
         case spv::Op::OpKill:
             // For now, assume SPIR-V OpKill has same semantics as WGSL discard.
             // TODO(dneto): https://github.com/gpuweb/gpuweb/issues/676
@@ -3238,31 +3261,63 @@
             // start of an if-selection or a switch-selection.  So at most one branch
             // is a kForward, kCaseFallThrough, or kIfBreak.
 
-            // The fallthrough case is special because WGSL requires the fallthrough
-            // statement to be last in the case clause.
-            if (true_kind == EdgeKind::kCaseFallThrough) {
-                return EmitConditionalCaseFallThrough(block_info, cond, false_kind, *false_info,
-                                                      true);
-            } else if (false_kind == EdgeKind::kCaseFallThrough) {
-                return EmitConditionalCaseFallThrough(block_info, cond, true_kind, *true_info,
-                                                      false);
+            if (true_kind == EdgeKind::kCaseFallThrough ||
+                false_kind == EdgeKind::kCaseFallThrough) {
+                return Fail() << "Fallthrough not supported in WGSL";
+            }
+
+            // In the case of a continuing block a `break-if` needs to be emitted for either an
+            // if-break or an if-else-break statement. This only happens inside the continue block.
+            // It's possible for a continue block to also be the loop block, so checks are needed
+            // that this is a continue construct and the header construct will cause a continuing
+            // construct to be emitted. (i.e. the header is not `continue is entire loop`.
+            bool needs_break_if = false;
+            if ((true_kind == EdgeKind::kLoopBreak || false_kind == EdgeKind::kLoopBreak) &&
+                block_info.construct && block_info.construct->kind == Construct::Kind::kContinue) {
+                auto* header = GetBlockInfo(block_info.construct->begin_id);
+
+                TINT_ASSERT(Reader, header->construct &&
+                                        header->construct->kind == Construct::Kind::kContinue);
+                if (!header->is_continue_entire_loop) {
+                    needs_break_if = true;
+                }
             }
 
             // At this point, at most one edge is kForward or kIfBreak.
 
-            // Emit an 'if' statement to express the *other* branch as a conditional
-            // break or continue.  Either or both of these could be nullptr.
-            // (A nullptr is generated for kIfBreak, kForward, or kBack.)
-            // Also if one of the branches is an if-break out of an if-selection
-            // requiring a flow guard, then get that flow guard name too.  It will
-            // come from at most one of these two branches.
-            std::string flow_guard;
-            auto* true_branch = MakeBranchDetailed(block_info, *true_info, false, &flow_guard);
-            auto* false_branch = MakeBranchDetailed(block_info, *false_info, false, &flow_guard);
+            // If this is a continuing block and a `break` is to be emitted, then this needs to be
+            // converted to a `break-if`. This may involve inverting the condition if this was a
+            // `break-unless`.
+            if (needs_break_if) {
+                if (true_kind == EdgeKind::kLoopBreak && false_kind == EdgeKind::kLoopBreak) {
+                    // Both branches break ... ?
+                    return Fail() << "Both branches of if inside continuing break.";
+                }
 
-            AddStatement(MakeSimpleIf(cond, true_branch, false_branch));
-            if (!flow_guard.empty()) {
-                PushGuard(flow_guard, statements_stack_.Back().GetEndId());
+                if (true_kind == EdgeKind::kLoopBreak) {
+                    AddStatement(create<ast::BreakIfStatement>(Source{}, cond));
+                } else {
+                    AddStatement(create<ast::BreakIfStatement>(
+                        Source{},
+                        create<ast::UnaryOpExpression>(Source{}, ast::UnaryOp::kNot, cond)));
+                }
+                return true;
+
+            } else {
+                // Emit an 'if' statement to express the *other* branch as a conditional
+                // break or continue.  Either or both of these could be nullptr.
+                // (A nullptr is generated for kIfBreak, kForward, or kBack.)
+                // Also if one of the branches is an if-break out of an if-selection
+                // requiring a flow guard, then get that flow guard name too.  It will
+                // come from at most one of these two branches.
+                std::string flow_guard;
+                auto* true_branch = MakeBranchDetailed(block_info, *true_info, &flow_guard);
+                auto* false_branch = MakeBranchDetailed(block_info, *false_info, &flow_guard);
+
+                AddStatement(MakeSimpleIf(cond, true_branch, false_branch));
+                if (!flow_guard.empty()) {
+                    PushGuard(flow_guard, statements_stack_.Back().GetEndId());
+                }
             }
             return true;
         }
@@ -3283,19 +3338,14 @@
 
 const ast::Statement* FunctionEmitter::MakeBranchDetailed(const BlockInfo& src_info,
                                                           const BlockInfo& dest_info,
-                                                          bool forced,
-                                                          std::string* flow_guard_name_ptr) const {
+                                                          std::string* flow_guard_name_ptr) {
     auto kind = src_info.succ_edge.find(dest_info.id)->second;
     switch (kind) {
         case EdgeKind::kBack:
             // Nothing to do. The loop backedge is implicit.
             break;
         case EdgeKind::kSwitchBreak: {
-            if (forced) {
-                return create<ast::BreakStatement>(Source{});
-            }
-            // Unless forced, don't bother with a break at the end of a case/default
-            // clause.
+            // Don't bother with a break at the end of a case/default clause.
             const auto header = dest_info.header_for_merge;
             TINT_ASSERT(Reader, header != 0);
             const auto* exiting_construct = GetBlockInfo(header)->construct;
@@ -3347,8 +3397,10 @@
             // merge block is implicit.
             break;
         }
-        case EdgeKind::kCaseFallThrough:
-            return create<ast::FallthroughStatement>(Source{});
+        case EdgeKind::kCaseFallThrough: {
+            Fail() << "Fallthrough not supported in WGSL";
+            return nullptr;
+        }
         case EdgeKind::kForward:
             // Unconditional forward branch is implicit.
             break;
@@ -3378,45 +3430,6 @@
     return if_stmt;
 }
 
-bool FunctionEmitter::EmitConditionalCaseFallThrough(const BlockInfo& src_info,
-                                                     const ast::Expression* cond,
-                                                     EdgeKind other_edge_kind,
-                                                     const BlockInfo& other_dest,
-                                                     bool fall_through_is_true_branch) {
-    // In WGSL, the fallthrough statement must come last in the case clause.
-    // So we'll emit an if statement for the other branch, and then emit
-    // the fallthrough.
-
-    // We have two distinct destinations. But we only get here if this
-    // is a normal terminator; in particular the source block is *not* the
-    // start of an if-selection.  So at most one branch is a kForward or
-    // kCaseFallThrough.
-    if (other_edge_kind == EdgeKind::kForward) {
-        return Fail() << "internal error: normal terminator OpBranchConditional has "
-                         "both forward and fallthrough edges";
-    }
-    if (other_edge_kind == EdgeKind::kIfBreak) {
-        return Fail() << "internal error: normal terminator OpBranchConditional has "
-                         "both IfBreak and fallthrough edges.  Violates nesting rule";
-    }
-    if (other_edge_kind == EdgeKind::kBack) {
-        return Fail() << "internal error: normal terminator OpBranchConditional has "
-                         "both backedge and fallthrough edges.  Violates nesting rule";
-    }
-    auto* other_branch = MakeForcedBranch(src_info, other_dest);
-    if (other_branch == nullptr) {
-        return Fail() << "internal error: expected a branch for edge-kind " << int(other_edge_kind);
-    }
-    if (fall_through_is_true_branch) {
-        AddStatement(MakeSimpleIf(cond, nullptr, other_branch));
-    } else {
-        AddStatement(MakeSimpleIf(cond, other_branch, nullptr));
-    }
-    AddStatement(create<ast::FallthroughStatement>(Source{}));
-
-    return success();
-}
-
 bool FunctionEmitter::EmitStatementsInBasicBlock(const BlockInfo& block_info,
                                                  bool* already_emitted) {
     if (*already_emitted) {
@@ -3982,7 +3995,11 @@
     if (op == spv::Op::OpCompositeConstruct) {
         ExpressionList operands;
         for (uint32_t iarg = 0; iarg < inst.NumInOperands(); ++iarg) {
-            operands.Push(MakeOperand(inst, iarg).expr);
+            auto operand = MakeOperand(inst, iarg);
+            if (!operand) {
+                return {};
+            }
+            operands.Push(operand.expr);
         }
         return {ast_type,
                 builder_.Construct(Source{}, ast_type->Build(builder_), std::move(operands))};
@@ -5265,7 +5282,21 @@
 
     ExpressionList args;
     for (uint32_t iarg = 1; iarg < inst.NumInOperands(); ++iarg) {
-        auto expr = MakeOperand(inst, iarg);
+        uint32_t arg_id = inst.GetSingleWordInOperand(iarg);
+        TypedExpression expr;
+
+        if (IsHandleObj(def_use_mgr_->GetDef(arg_id))) {
+            // For textures and samplers, use the memory object declaration
+            // instead.
+            const auto usage = parser_impl_.GetHandleUsage(arg_id);
+            const auto* mem_obj_decl =
+                parser_impl_.GetMemoryObjectDeclarationForHandle(arg_id, usage.IsTexture());
+            expr = MakeExpression(mem_obj_decl->result_id());
+            // Pass the handle through instead of a pointer to the handle.
+            expr.type = parser_impl_.GetHandleTypeForSpirvHandle(*mem_obj_decl);
+        } else {
+            expr = MakeOperand(inst, iarg);
+        }
         if (!expr) {
             return false;
         }
@@ -5407,21 +5438,17 @@
 }
 
 const Texture* FunctionEmitter::GetImageType(const spvtools::opt::Instruction& image) {
-    const Pointer* ptr_type = parser_impl_.GetTypeForHandleVar(image);
+    const Type* type = parser_impl_.GetHandleTypeForSpirvHandle(image);
     if (!parser_impl_.success()) {
         Fail();
         return {};
     }
-    if (!ptr_type) {
-        Fail() << "invalid texture type for " << image.PrettyPrint();
-        return {};
+    TINT_ASSERT(Reader, type != nullptr);
+    if (auto* result = type->UnwrapAll()->As<Texture>()) {
+        return result;
     }
-    auto* result = ptr_type->type->UnwrapAll()->As<Texture>();
-    if (!result) {
-        Fail() << "invalid texture type for " << image.PrettyPrint();
-        return {};
-    }
-    return result;
+    Fail() << "invalid texture type for " << image.PrettyPrint();
+    return {};
 }
 
 const ast::Expression* FunctionEmitter::GetImageExpression(const spvtools::opt::Instruction& inst) {
@@ -5471,12 +5498,7 @@
     }
 
     // Find the texture type.
-    const Pointer* texture_ptr_type = parser_impl_.GetTypeForHandleVar(*image);
-    if (!texture_ptr_type) {
-        return Fail();
-    }
-    const Texture* texture_type = texture_ptr_type->type->UnwrapAll()->As<Texture>();
-
+    const auto* texture_type = parser_impl_.GetHandleTypeForSpirvHandle(*image)->As<Texture>();
     if (!texture_type) {
         return Fail();
     }
@@ -5736,7 +5758,8 @@
         // If necessary, convert the result to the signedness of the instruction
         // result type. Compare the SPIR-V image's sampled component type with the
         // component of the result type of the SPIR-V instruction.
-        auto* spirv_image_type = parser_impl_.GetSpirvTypeForHandleMemoryObjectDeclaration(*image);
+        auto* spirv_image_type =
+            parser_impl_.GetSpirvTypeForHandleOrHandleMemoryObjectDeclaration(*image);
         if (!spirv_image_type || (opcode(spirv_image_type) != spv::Op::OpTypeImage)) {
             return Fail() << "invalid image type for image memory object declaration "
                           << image->PrettyPrint();
diff --git a/src/tint/reader/spirv/function.h b/src/tint/reader/spirv/function.h
index 755ecc9..65a1e3b 100644
--- a/src/tint/reader/spirv/function.h
+++ b/src/tint/reader/spirv/function.h
@@ -34,15 +34,13 @@
 //
 // The edge kinds are used in many ways.
 //
-// For example, consider the edges leaving a basic block and going to distinct
-// targets. If the total number of kForward + kIfBreak + kCaseFallThrough edges
-// is more than 1, then the block must be a structured header, i.e. it needs
-// a merge instruction to declare the control flow divergence and associated
-// reconvergence point.  Those those edge kinds count toward divergence
-// because SPIR-v is designed to easily map back to structured control flow
-// in GLSL (and C).  In GLSL and C, those forward-flow edges don't have a
-// special statement to express them.  The other forward edges: kSwitchBreak,
-// kLoopBreak, and kLoopContinue directly map to 'break', 'break', and
+// For example, consider the edges leaving a basic block and going to distinct targets. If the
+// total number of kForward + kIfBreak + kCaseFallThrough edges is more than 1, then the block must
+// be a structured header, i.e. it needs a merge instruction to declare the control flow divergence
+// and associated reconvergence point.  Those those edge kinds count toward divergence because
+// SPIR-V is designed to easily map back to structured control flow in GLSL (and C).  In GLSL and C,
+// those forward-flow edges don't have a special statement to express them.  The other forward
+// edges: kSwitchBreak, kLoopBreak, and kLoopContinue directly map to 'break', 'break', and
 // 'continue', respectively.
 enum class EdgeKind {
     // A back-edge: An edge from a node to one of its ancestors in a depth-first
@@ -64,7 +62,8 @@
     // This can only occur for an "if" selection, i.e. where the selection
     // header ends in OpBranchConditional.
     kIfBreak,
-    // An edge from one switch case to the next sibling switch case.
+    // An edge from one switch case to the next sibling switch case. Note, this is not valid in WGSL
+    // at the moment and will trigger an ICE if encountered. It is here for completeness.
     kCaseFallThrough,
     // None of the above.
     kForward
@@ -708,8 +707,7 @@
 
     /// Emits code for terminators, but that aren't part of entering or
     /// resolving structured control flow. That is, if the basic block
-    /// terminator calls for it, emit the fallthrough, break, continue, return,
-    /// or kill commands.
+    /// terminator calls for it, emit the fallthrough break, continue, return, or kill commands.
     /// @param block_info the block with the terminator to emit (if any)
     /// @returns false if emission failed
     bool EmitNormalTerminator(const BlockInfo& block_info);
@@ -722,39 +720,24 @@
     /// @param src_info the source block
     /// @param dest_info the destination block
     /// @returns the new statement, or a null statement
-    const ast::Statement* MakeBranch(const BlockInfo& src_info, const BlockInfo& dest_info) const {
-        return MakeBranchDetailed(src_info, dest_info, false, nullptr);
+    const ast::Statement* MakeBranch(const BlockInfo& src_info, const BlockInfo& dest_info) {
+        return MakeBranchDetailed(src_info, dest_info, nullptr);
     }
 
     /// Returns a new statement to represent the given branch representing a
     /// "normal" terminator, as in the sense of EmitNormalTerminator.  If no
-    /// WGSL statement is required, the statement will be nullptr.
-    /// @param src_info the source block
-    /// @param dest_info the destination block
-    /// @returns the new statement, or a null statement
-    const ast::Statement* MakeForcedBranch(const BlockInfo& src_info,
-                                           const BlockInfo& dest_info) const {
-        return MakeBranchDetailed(src_info, dest_info, true, nullptr);
-    }
-
-    /// Returns a new statement to represent the given branch representing a
-    /// "normal" terminator, as in the sense of EmitNormalTerminator.  If no
-    /// WGSL statement is required, the statement will be nullptr. When `forced`
-    /// is false, this method tries to avoid emitting a 'break' statement when
-    /// that would be redundant in WGSL due to implicit breaking out of a switch.
-    /// When `forced` is true, the method won't try to avoid emitting that break.
-    /// If the control flow edge is an if-break for an if-selection with a
+    /// WGSL statement is required, the statement will be nullptr. This method tries to avoid
+    /// emitting a 'break' statement when that would be redundant in WGSL due to implicit breaking
+    /// out of a switch. If the control flow edge is an if-break for an if-selection with a
     /// control flow guard, then return that guard name via `flow_guard_name_ptr`
     /// when that parameter is not null.
     /// @param src_info the source block
     /// @param dest_info the destination block
-    /// @param forced if true, always emit the branch (if it exists in WGSL)
     /// @param flow_guard_name_ptr return parameter for control flow guard name
     /// @returns the new statement, or a null statement
     const ast::Statement* MakeBranchDetailed(const BlockInfo& src_info,
                                              const BlockInfo& dest_info,
-                                             bool forced,
-                                             std::string* flow_guard_name_ptr) const;
+                                             std::string* flow_guard_name_ptr);
 
     /// Returns a new if statement with the given statements as the then-clause
     /// and the else-clause.  Either or both clauses might be nullptr. If both
@@ -1002,6 +985,16 @@
     /// @returns true if emission has not yet failed.
     bool ParseFunctionDeclaration(FunctionDeclaration* decl);
 
+    /// @param obj a SPIR-V instruction with a result ID and a type ID
+    /// @returns true if the object is an image, a sampler, or a pointer to
+    /// an image or a sampler
+    bool IsHandleObj(const spvtools::opt::Instruction& obj);
+
+    /// @param obj a SPIR-V instruction with a result ID and a type ID
+    /// @returns true if the object is an image, a sampler, or a pointer to
+    /// an image or a sampler
+    bool IsHandleObj(const spvtools::opt::Instruction* obj);
+
     /// @returns the store type for the OpVariable instruction, or
     /// null on failure.
     const Type* GetVariableStoreType(const spvtools::opt::Instruction& var_decl_inst);
diff --git a/src/tint/reader/spirv/function_call_test.cc b/src/tint/reader/spirv/function_call_test.cc
index 6b12ece..145a6fb 100644
--- a/src/tint/reader/spirv/function_call_test.cc
+++ b/src/tint/reader/spirv/function_call_test.cc
@@ -32,6 +32,42 @@
 )";
 }
 
+std::string CommonTypes() {
+    return R"(
+    %void = OpTypeVoid
+    %voidfn = OpTypeFunction %void
+    %float = OpTypeFloat 32
+    %uint = OpTypeInt 32 0
+    %int = OpTypeInt 32 1
+    %float_0 = OpConstant %float 0.0
+  )";
+}
+
+std::string CommonHandleTypes() {
+    return R"(
+    OpName %t "t"
+    OpName %s "s"
+    OpDecorate %t DescriptorSet 0
+    OpDecorate %t Binding 0
+    OpDecorate %s DescriptorSet 0
+    OpDecorate %s Binding 1
+    )" + CommonTypes() +
+           R"(
+
+    %v2float = OpTypeVector %float 2
+    %v4float = OpTypeVector %float 4
+    %v2_0 = OpConstantNull %v2float
+    %sampler = OpTypeSampler
+    %tex2d_f32 = OpTypeImage %float 2D 0 0 0 1 Unknown
+    %sampled_image_2d_f32 = OpTypeSampledImage %tex2d_f32
+    %ptr_sampler = OpTypePointer UniformConstant %sampler
+    %ptr_tex2d_f32 = OpTypePointer UniformConstant %tex2d_f32
+
+    %t = OpVariable %ptr_tex2d_f32 UniformConstant
+    %s = OpVariable %ptr_sampler UniformConstant
+  )";
+}
+
 TEST_F(SpvParserTest, EmitStatement_VoidCallNoParams) {
     auto p = parser(test::Assemble(Preamble() + R"(
      %void = OpTypeVoid
@@ -193,5 +229,142 @@
     EXPECT_EQ(program_ast_str, expected);
 }
 
+std::string HelperFunctionPtrHandle() {
+    return R"(
+     ; This is how Glslang generates functions that take texture and sampler arguments.
+     ; It passes them by pointer.
+     %fn_ty = OpTypeFunction %void %ptr_tex2d_f32 %ptr_sampler
+
+     %200 = OpFunction %void None %fn_ty
+     %14 = OpFunctionParameter %ptr_tex2d_f32
+     %15 = OpFunctionParameter %ptr_sampler
+     %helper_entry = OpLabel
+     ; access the texture, to give the handles usages.
+     %helper_im = OpLoad %tex2d_f32 %14
+     %helper_sam = OpLoad %sampler %15
+     %helper_imsam = OpSampledImage %sampled_image_2d_f32 %helper_im %helper_sam
+     %20 = OpImageSampleImplicitLod %v4float %helper_imsam %v2_0
+     OpReturn
+     OpFunctionEnd
+     )";
+}
+
+std::string HelperFunctionHandle() {
+    return R"(
+     ; It is valid in SPIR-V to pass textures and samplers by value.
+     %fn_ty = OpTypeFunction %void %tex2d_f32 %sampler
+
+     %200 = OpFunction %void None %fn_ty
+     %14 = OpFunctionParameter %tex2d_f32
+     %15 = OpFunctionParameter %sampler
+     %helper_entry = OpLabel
+     ; access the texture, to give the handles usages.
+     %helper_imsam = OpSampledImage %sampled_image_2d_f32 %14 %15
+     %20 = OpImageSampleImplicitLod %v4float %helper_imsam %v2_0
+     OpReturn
+     OpFunctionEnd
+     )";
+}
+
+TEST_F(SpvParserTest, Emit_FunctionCall_HandlePtrParams_Direct) {
+    auto assembly = Preamble() + CommonHandleTypes() + HelperFunctionPtrHandle() + R"(
+
+     %100 = OpFunction %void None %voidfn
+     %entry = OpLabel
+     %1 = OpFunctionCall %void %200 %t %s
+     OpReturn
+     OpFunctionEnd
+  )";
+
+    auto p = parser(test::Assemble(assembly));
+    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
+    auto fe = p->function_emitter(100);
+    EXPECT_TRUE(fe.EmitBody()) << p->error();
+    const auto got = test::ToString(p->program(), fe.ast_body());
+
+    const std::string expect = R"(x_200(t, s);
+return;
+)";
+    EXPECT_EQ(got, expect);
+}
+
+TEST_F(SpvParserTest, Emit_FunctionCall_HandlePtrParams_CopyObject) {
+    auto assembly = Preamble() + CommonHandleTypes() + HelperFunctionPtrHandle() + R"(
+
+     %100 = OpFunction %void None %voidfn
+     %entry = OpLabel
+
+     %copy_t = OpCopyObject %ptr_tex2d_f32 %t
+     %copy_s = OpCopyObject %ptr_sampler %s
+     %1 = OpFunctionCall %void %200 %copy_t %copy_s
+     OpReturn
+     OpFunctionEnd
+  )";
+
+    auto p = parser(test::Assemble(assembly));
+    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+    auto fe = p->function_emitter(100);
+    EXPECT_TRUE(fe.EmitBody()) << p->error();
+    const auto got = test::ToString(p->program(), fe.ast_body());
+
+    const std::string expect = R"(x_200(t, s);
+return;
+)";
+    EXPECT_EQ(got, expect);
+}
+
+TEST_F(SpvParserTest, Emit_FunctionCall_HandleParams_Load) {
+    auto assembly = Preamble() + CommonHandleTypes() + HelperFunctionHandle() + R"(
+
+     %100 = OpFunction %void None %voidfn
+     %entry = OpLabel
+     %im = OpLoad %tex2d_f32 %t
+     %sam = OpLoad %sampler %s
+     %1 = OpFunctionCall %void %200 %im %sam
+     OpReturn
+     OpFunctionEnd
+  )";
+
+    auto p = parser(test::Assemble(assembly));
+    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
+    auto fe = p->function_emitter(100);
+    EXPECT_TRUE(fe.EmitBody()) << p->error();
+    const auto got = test::ToString(p->program(), fe.ast_body());
+
+    const std::string expect = R"(x_200(t, s);
+return;
+)";
+    EXPECT_EQ(got, expect);
+}
+
+TEST_F(SpvParserTest, Emit_FunctionCall_HandleParams_LoadsAndCopyObject) {
+    auto assembly = Preamble() + CommonHandleTypes() + HelperFunctionHandle() + R"(
+
+     %100 = OpFunction %void None %voidfn
+     %entry = OpLabel
+
+     %copy_t = OpCopyObject %ptr_tex2d_f32 %t
+     %copy_s = OpCopyObject %ptr_sampler %s
+     %im = OpLoad %tex2d_f32 %copy_t
+     %sam = OpLoad %sampler %copy_s
+     %copy_im = OpCopyObject %tex2d_f32 %im
+     %copy_sam = OpCopyObject %sampler %sam
+     %1 = OpFunctionCall %void %200 %copy_im %copy_sam
+     OpReturn
+     OpFunctionEnd
+  )";
+
+    auto p = parser(test::Assemble(assembly));
+    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
+    auto fe = p->function_emitter(100);
+    EXPECT_TRUE(fe.EmitBody()) << p->error();
+    const auto got = test::ToString(p->program(), fe.ast_body());
+
+    const std::string expect = R"(x_200(t, s);
+return;
+)";
+    EXPECT_EQ(got, expect);
+}
+
 }  // namespace
 }  // namespace tint::reader::spirv
diff --git a/src/tint/reader/spirv/function_cfg_test.cc b/src/tint/reader/spirv/function_cfg_test.cc
index 538657b..c5800fb 100644
--- a/src/tint/reader/spirv/function_cfg_test.cc
+++ b/src/tint/reader/spirv/function_cfg_test.cc
@@ -638,9 +638,8 @@
     EXPECT_FALSE(bi99->is_continue_entire_loop);
 }
 
-TEST_F(
-    SpvParserCFGTest,
-    RegisterMerges_GoodLoopMerge_MultiBlockLoop_ContinueIsNotHeader_BranchConditional) {  // NOLINT
+TEST_F(SpvParserCFGTest,
+       RegisterMerges_GoodLoopMerge_MultiBlockLoop_ContinueIsNotHeader_BranchConditional) {
     auto p = parser(test::Assemble(CommonTypes() + R"(
      %100 = OpFunction %void None %voidfn
 
@@ -1288,43 +1287,7 @@
     EXPECT_THAT(fe.block_order(), ElementsAre(10, 40, 20, 30, 99));
 }
 
-TEST_F(SpvParserCFGTest, ComputeBlockOrder_RespectSwitchCaseFallthrough) {
-    auto assembly = CommonTypes() + R"(
-     %100 = OpFunction %void None %voidfn
-
-     %10 = OpLabel
-     OpSelectionMerge %99 None
-     ; SPIR-V validation requires a fallthrough destination to immediately
-     ; follow the source. So %20 -> %40, %30 -> %50
-     OpSwitch %selector %99 20 %20 40 %40 30 %30 50 %50
-
-     %50 = OpLabel
-     OpBranch %99
-
-     %99 = OpLabel
-     OpReturn
-
-     %40 = OpLabel
-     OpBranch %99
-
-     %30 = OpLabel
-     OpBranch %50 ; fallthrough
-
-     %20 = OpLabel
-     OpBranch %40 ; fallthrough
-
-     OpFunctionEnd
-  )";
-    auto p = parser(test::Assemble(assembly));
-    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
-    auto fe = p->function_emitter(100);
-    fe.RegisterBasicBlocks();
-    fe.ComputeBlockOrderAndPositions();
-
-    EXPECT_THAT(fe.block_order(), ElementsAre(10, 30, 50, 20, 40, 99)) << assembly;
-}
-
-TEST_F(SpvParserCFGTest, ComputeBlockOrder_RespectSwitchCaseFallthrough_FromDefault) {
+TEST_F(SpvParserCFGTest, ClassifyCFGEdges_Fallthrough_IsError) {
     auto assembly = CommonTypes() + R"(
      %100 = OpFunction %void None %voidfn
 
@@ -1352,122 +1315,9 @@
     auto p = parser(test::Assemble(assembly));
     ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
     auto fe = p->function_emitter(100);
-    fe.RegisterBasicBlocks();
-    fe.ComputeBlockOrderAndPositions();
-
-    EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 80, 30, 40, 99)) << assembly;
-}
-
-TEST_F(SpvParserCFGTest, ComputeBlockOrder_RespectSwitchCaseFallthrough_FromCaseToDefaultToCase) {
-    auto assembly = CommonTypes() + R"(
-     %100 = OpFunction %void None %voidfn
-
-     %10 = OpLabel
-     OpSelectionMerge %99 None
-     OpSwitch %selector %80 20 %20 30 %30
-
-     %20 = OpLabel
-     OpBranch %80 ; fallthrough to default
-
-     %80 = OpLabel ; the default case
-     OpBranch %30 ; fallthrough to 30
-
-     %30 = OpLabel
-     OpBranch %99
-
-     %99 = OpLabel ; dominated by %30, so follow %30
-     OpReturn
-
-     OpFunctionEnd
-  )";
-    auto p = parser(test::Assemble(assembly));
-    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
-    auto fe = p->function_emitter(100);
-    fe.RegisterBasicBlocks();
-    fe.ComputeBlockOrderAndPositions();
-
-    EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 80, 30, 99)) << assembly;
-}
-
-TEST_F(SpvParserCFGTest, ComputeBlockOrder_SwitchCasesFallthrough_OppositeDirections) {
-    auto assembly = CommonTypes() + R"(
-     %100 = OpFunction %void None %voidfn
-
-     %10 = OpLabel
-     OpSelectionMerge %99 None
-     OpSwitch %selector %99 20 %20 30 %30 40 %40 50 %50
-
-     %99 = OpLabel
-     OpReturn
-
-     %20 = OpLabel
-     OpBranch %30 ; forward
-
-     %40 = OpLabel
-     OpBranch %99
-
-     %30 = OpLabel
-     OpBranch %99
-
-     ; SPIR-V doesn't actually allow a fall-through that goes backward in the
-     ; module. But the block ordering algorithm tolerates it.
-     %50 = OpLabel
-     OpBranch %40 ; backward
-
-     OpFunctionEnd
-  )";
-    auto p = parser(test::Assemble(assembly));
-    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
-    auto fe = p->function_emitter(100);
-    fe.RegisterBasicBlocks();
-    fe.ComputeBlockOrderAndPositions();
-
-    EXPECT_THAT(fe.block_order(), ElementsAre(10, 50, 40, 20, 30, 99)) << assembly;
-
-    // We're deliberately testing a case that SPIR-V doesn't allow.
-    p->DeliberatelyInvalidSpirv();
-}
-
-TEST_F(SpvParserCFGTest, ComputeBlockOrder_RespectSwitchCaseFallthrough_Interleaved) {
-    auto assembly = CommonTypes() + R"(
-     %100 = OpFunction %void None %voidfn
-
-     %10 = OpLabel
-     OpSelectionMerge %99 None
-     ; SPIR-V validation requires a fallthrough destination to immediately
-     ; follow the source. So %20 -> %40
-     OpSwitch %selector %99 20 %20 40 %40 30 %30 50 %50
-
-     %99 = OpLabel
-     OpReturn
-
-     %20 = OpLabel
-     OpBranch %40
-
-     %30 = OpLabel
-     OpBranch %50
-
-     %40 = OpLabel
-     OpBranch %60
-
-     %50 = OpLabel
-     OpBranch %70
-
-     %60 = OpLabel
-     OpBranch %99
-
-     %70 = OpLabel
-     OpBranch %99
-
-     OpFunctionEnd
-  )";
-    auto p = parser(test::Assemble(assembly));
-    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
-    auto fe = p->function_emitter(100);
-    fe.RegisterBasicBlocks();
-    fe.ComputeBlockOrderAndPositions();
-
-    EXPECT_THAT(fe.block_order(), ElementsAre(10, 30, 50, 70, 20, 40, 60, 99)) << assembly;
+    EXPECT_FALSE(FlowClassifyCFGEdges(&fe)) << p->error();
+    // Some further processing
+    EXPECT_THAT(p->error(), Eq("Fallthrough not permitted in WGSL"));
 }
 
 TEST_F(SpvParserCFGTest, ComputeBlockOrder_Nest_If_Contains_If) {
@@ -1566,54 +1416,6 @@
     EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 40, 49, 50, 60, 70, 79, 99)) << assembly;
 }
 
-TEST_F(SpvParserCFGTest, ComputeBlockOrder_Nest_IfFallthrough_In_SwitchCase) {
-    auto assembly = CommonTypes() + R"(
-     %100 = OpFunction %void None %voidfn
-
-     %10 = OpLabel
-     OpSelectionMerge %99 None
-     OpSwitch %selector %50 20 %20 50 %50
-
-     %99 = OpLabel
-     OpReturn
-
-     %20 = OpLabel
-     OpSelectionMerge %49 None
-     OpBranchConditional %cond %30 %40
-
-     %49 = OpLabel
-     OpBranchConditional %cond %99 %50 ; fallthrough
-
-     %30 = OpLabel
-     OpBranch %49
-
-     %40 = OpLabel
-     OpBranch %49
-
-     %50 = OpLabel
-     OpSelectionMerge %79 None
-     OpBranchConditional %cond %60 %70
-
-     %79 = OpLabel
-     OpBranch %99
-
-     %60 = OpLabel
-     OpBranch %79
-
-     %70 = OpLabel
-     OpBranch %79
-
-     OpFunctionEnd
-  )";
-    auto p = parser(test::Assemble(assembly));
-    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
-    auto fe = p->function_emitter(100);
-    fe.RegisterBasicBlocks();
-    fe.ComputeBlockOrderAndPositions();
-
-    EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 40, 49, 50, 60, 70, 79, 99)) << assembly;
-}
-
 TEST_F(SpvParserCFGTest, ComputeBlockOrder_Nest_IfBreak_In_SwitchCase) {
     auto assembly = CommonTypes() + R"(
      %100 = OpFunction %void None %voidfn
@@ -2802,7 +2604,7 @@
 }
 
 TEST_F(SpvParserCFGTest,
-       VerifyHeaderContinueMergeOrder_HeaderDoesNotStrictlyDominateContinueTarget) {  // NOLINT
+       VerifyHeaderContinueMergeOrder_HeaderDoesNotStrictlyDominateContinueTarget) {
     auto assembly = CommonTypes() + R"(
      %100 = OpFunction %void None %voidfn
 
@@ -3904,7 +3706,7 @@
      OpSwitch %selector %80 60 %60
 
      %60 = OpLabel
-     OpBranch %89 ; fallthrough
+     OpBranch %89
 
      %80 = OpLabel ; default for both switches
      OpBranch %89
@@ -4648,9 +4450,8 @@
     EXPECT_EQ(bi40->succ_edge[20], EdgeKind::kBack);
 }
 
-TEST_F(
-    SpvParserCFGTest,
-    ClassifyCFGEdges_BackEdge_MultiBlockLoop_MultiBlockContinueConstruct_ContinueIsNotHeader) {  // NOLINT
+TEST_F(SpvParserCFGTest,
+       ClassifyCFGEdges_BackEdge_MultiBlockLoop_MultiBlockContinueConstruct_ContinueIsNotHeader) {
     auto assembly = CommonTypes() + R"(
      %100 = OpFunction %void None %voidfn
 
@@ -4686,9 +4487,8 @@
     EXPECT_EQ(bi50->succ_edge[20], EdgeKind::kBack);
 }
 
-TEST_F(
-    SpvParserCFGTest,
-    ClassifyCFGEdges_BackEdge_MultiBlockLoop_MultiBlockContinueConstruct_ContinueIsHeader) {  // NOLINT
+TEST_F(SpvParserCFGTest,
+       ClassifyCFGEdges_BackEdge_MultiBlockLoop_MultiBlockContinueConstruct_ContinueIsHeader) {
     auto assembly = CommonTypes() + R"(
      %100 = OpFunction %void None %voidfn
 
@@ -4974,8 +4774,6 @@
 }
 
 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_IfBreak_EscapeSwitchCase_IsError) {
-    // Code generation assumes that you can't have kCaseFallThrough and kIfBreak
-    // from the same OpBranchConditional.
     // This checks one direction of that, where the IfBreak is shown it can't
     // escape a switch case.
     auto assembly = CommonTypes() + R"(
@@ -5936,440 +5734,6 @@
                                "starting at block 30; branch bypasses merge block 59"));
 }
 
-TEST_F(SpvParserCFGTest, ClassifyCFGEdges_Fallthrough_CaseTailToCase) {
-    auto assembly = CommonTypes() + R"(
-     %100 = OpFunction %void None %voidfn
-
-     %10 = OpLabel
-     OpSelectionMerge %99 None
-     OpSwitch %selector %99 20 %20 40 %40
-
-     %20 = OpLabel ; case 20
-     OpBranch %30
-
-     %30 = OpLabel
-     OpBranch %40 ; fallthrough
-
-     %40 = OpLabel ; case 40
-     OpBranch %99
-
-     %99 = OpLabel
-     OpReturn
-
-     OpFunctionEnd
-)";
-    auto p = parser(test::Assemble(assembly));
-    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
-    auto fe = p->function_emitter(100);
-    EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
-
-    auto* bi = fe.GetBlockInfo(30);
-    ASSERT_NE(bi, nullptr);
-    EXPECT_EQ(bi->succ_edge.count(40), 1u);
-    EXPECT_EQ(bi->succ_edge[40], EdgeKind::kCaseFallThrough);
-}
-
-TEST_F(SpvParserCFGTest, ClassifyCFGEdges_Fallthrough_CaseTailToDefaultNotMerge) {
-    auto assembly = CommonTypes() + R"(
-     %100 = OpFunction %void None %voidfn
-
-     %10 = OpLabel
-     OpSelectionMerge %99 None
-     OpSwitch %selector %40 20 %20
-
-     %20 = OpLabel ; case 20
-     OpBranch %30
-
-     %30 = OpLabel
-     OpBranch %40 ; fallthrough
-
-     %40 = OpLabel ; case 40
-     OpBranch %99
-
-     %99 = OpLabel
-     OpReturn
-
-     OpFunctionEnd
-)";
-    auto p = parser(test::Assemble(assembly));
-    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
-    auto fe = p->function_emitter(100);
-    EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
-
-    auto* bi = fe.GetBlockInfo(30);
-    ASSERT_NE(bi, nullptr);
-    EXPECT_EQ(bi->succ_edge.count(40), 1u);
-    EXPECT_EQ(bi->succ_edge[40], EdgeKind::kCaseFallThrough);
-}
-
-TEST_F(SpvParserCFGTest, ClassifyCFGEdges_Fallthrough_DefaultToCase) {
-    auto assembly = CommonTypes() + R"(
-     %100 = OpFunction %void None %voidfn
-
-     %10 = OpLabel
-     OpSelectionMerge %99 None
-     OpSwitch %selector %20 40 %40
-
-     %20 = OpLabel ; default
-     OpBranch %30
-
-     %30 = OpLabel
-     OpBranch %40 ; fallthrough
-
-     %40 = OpLabel ; case 40
-     OpBranch %99
-
-     %99 = OpLabel
-     OpReturn
-
-     OpFunctionEnd
-)";
-    auto p = parser(test::Assemble(assembly));
-    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
-    auto fe = p->function_emitter(100);
-    EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
-
-    auto* bi = fe.GetBlockInfo(30);
-    ASSERT_NE(bi, nullptr);
-    EXPECT_EQ(bi->succ_edge.count(40), 1u);
-    EXPECT_EQ(bi->succ_edge[40], EdgeKind::kCaseFallThrough);
-}
-
-TEST_F(SpvParserCFGTest, ClassifyCFGEdges_Fallthrough_BranchConditionalWith_IfBreak_IsError) {
-    // Code generation assumes OpBranchConditional can't have kCaseFallThrough
-    // with kIfBreak.
-    auto assembly = CommonTypes() + R"(
-     %100 = OpFunction %void None %voidfn
-
-     %10 = OpLabel
-     OpSelectionMerge %99 None ; Set up if-break to %99
-     OpBranchConditional %cond %20 %99
-
-     %20 = OpLabel
-     OpSelectionMerge %80 None ; switch-selection
-     OpSwitch %selector %80 30 %30 40 %40
-
-     %30 = OpLabel ; first case
-        ; branch to %99 would be an if-break, but it bypasess the switch merge
-        ; Also has case fall-through
-     OpBranchConditional %cond2 %99 %40
-
-     %40 = OpLabel ; second case
-     OpBranch %80
-
-     %80 = OpLabel ; switch-selection's merge
-     OpBranch %99
-
-     %99 = OpLabel ; if-selection's merge
-     OpReturn
-
-     OpFunctionEnd
-)";
-    auto p = parser(test::Assemble(assembly));
-    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
-    auto fe = p->function_emitter(100);
-    EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
-    EXPECT_THAT(p->error(), Eq("Branch from block 30 to block 99 is an invalid exit from "
-                               "construct starting at block 20; branch bypasses merge block 80"));
-}
-
-TEST_F(SpvParserCFGTest, ClassifyCFGEdges_Fallthrough_BranchConditionalWith_Forward_IsError) {
-    // Code generation assumes OpBranchConditional can't have kCaseFallThrough
-    // with kForward.
-    auto assembly = CommonTypes() + R"(
-     %100 = OpFunction %void None %voidfn
-
-     %10 = OpLabel
-     OpSelectionMerge %99 None ; switch-selection
-     OpSwitch %selector %99 20 %20 30 %30
-
-     ; Try to make branch to 35 a kForward branch
-     %20 = OpLabel ; first case
-     OpBranchConditional %cond2 %25 %30
-
-     %25 = OpLabel
-     OpBranch %99
-
-     %30 = OpLabel ; second case
-     OpBranch %99
-
-     %99 = OpLabel ; if-selection's merge
-     OpReturn
-
-     OpFunctionEnd
-)";
-    auto p = parser(test::Assemble(assembly));
-    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
-    auto fe = p->function_emitter(100);
-    EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
-    EXPECT_THAT(p->error(), Eq("Control flow diverges at block 20 (to 25, 30) but it is not "
-                               "a structured header (it has no merge instruction)"));
-}
-
-TEST_F(SpvParserCFGTest,
-       ClassifyCFGEdges_Fallthrough_BranchConditionalWith_Back_LoopOnOutside_IsError) {  // NOLINT
-    // Code generation assumes OpBranchConditional can't have kCaseFallThrough
-    // with kBack.
-    //
-    // This test has the loop on the outside. The backedge coming from a case
-    // clause means the switch is inside the continue construct, and the nesting
-    // of the switch's merge means the backedge is coming from a block that is not
-    // at the end of the continue construct.
-    auto assembly = CommonTypes() + R"(
-     %100 = OpFunction %void None %voidfn
-
-     %10 = OpLabel
-     OpBranch %20
-
-     %20 = OpLabel
-     OpLoopMerge %99 %30 None
-     OpBranch %30
-
-     %30 = OpLabel  ; continue target and
-     OpSelectionMerge %80 None ; switch-selection
-     OpSwitch %selector %80 40 %40 50 %50
-
-     ; try to make a back edge with a fallthrough
-     %40 = OpLabel ; first case
-     OpBranchConditional %cond2 %20 %50
-
-     %50 = OpLabel ; second case
-     OpBranch %80
-
-     %80 = OpLabel ; switch merge
-     OpBranch %20  ; also backedge
-
-     %99 = OpLabel ; loop merge
-     OpReturn
-
-     OpFunctionEnd
-)";
-    auto p = parser(test::Assemble(assembly));
-    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
-    auto fe = p->function_emitter(100);
-    EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
-    EXPECT_THAT(p->error(), Eq("Invalid exit (40->20) from continue construct: 40 is not the "
-                               "last block in the continue construct starting at 30 "
-                               "(violates post-dominance rule)"));
-}
-
-TEST_F(
-    SpvParserCFGTest,
-    FindSwitchCaseSelectionHeaders_Fallthrough_BranchConditionalWith_Back_LoopOnInside_FallthroughIsMerge_IsError) {  // NOLINT
-    // Code generation assumes OpBranchConditional can't have kCaseFallThrough
-    // with kBack.
-    //
-    // This test has the loop on the inside. The merge block is also the
-    // fallthrough target.
-    auto assembly = CommonTypes() + R"(
-     %100 = OpFunction %void None %voidfn
-
-     %10 = OpLabel  ; continue target and
-     OpSelectionMerge %99 None ; switch-selection
-     OpSwitch %selector %99 20 %20 50 %50
-
-     %20 = OpLabel ; first case, and loop header
-     OpLoopMerge %50 %40 None
-     OpBranch %40
-
-     ; try to make a back edge with a fallthrough
-     %40 = OpLabel
-     OpBranchConditional %cond2 %20 %50
-
-     %50 = OpLabel ; second case.  also the loop merge ; header must dominate its merge block !
-     OpBranch %99
-
-     %99 = OpLabel ; switch merge
-     OpReturn
-
-     OpFunctionEnd
-)";
-    auto p = parser(test::Assemble(assembly));
-    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
-    auto fe = p->function_emitter(100);
-    EXPECT_FALSE(FlowFindSwitchCaseHeaders(&fe));
-    EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 40, 50, 99));
-    EXPECT_THAT(p->error(), Eq("Block 50 is a case block for switch-selection header 10 and "
-                               "also the merge block for 20 (violates dominance rule)"));
-}
-
-TEST_F(
-    SpvParserCFGTest,
-    ClassifyCFGEdges_Fallthrough_BranchConditionalWith_Back_LoopOnInside_FallthroughIsNotMerge_IsError) {  // NOLINT
-    // Code generation assumes OpBranchConditional can't have kCaseFallThrough
-    // with kBack.
-    //
-    // This test has the loop on the inside. The merge block is not the merge
-    // target But the block order gets messed up because of the weird
-    // connectivity.
-    auto assembly = CommonTypes() + R"(
-     %100 = OpFunction %void None %voidfn
-
-     %10 = OpLabel  ; continue target and
-     OpSelectionMerge %99 None ; switch-selection
-     OpSwitch %selector %99 20 %20 50 %50
-
-     %20 = OpLabel ; first case, and loop header
-     OpLoopMerge %45 %40 None  ; move the merge to an unreachable block
-     OpBranch %40
-
-     ; try to make a back edge with a fallthrough
-     %40 = OpLabel
-     OpBranchConditional %cond2 %20 %50
-
-     %45 = OpLabel ; merge for the loop
-     OpUnreachable
-
-     %50 = OpLabel ; second case. target of fallthrough
-     OpBranch %99
-
-     %99 = OpLabel ; switch merge
-     OpReturn
-
-     OpFunctionEnd
-)";
-    auto p = parser(test::Assemble(assembly));
-    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
-    auto fe = p->function_emitter(100);
-    EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
-    EXPECT_THAT(p->error(), Eq("Branch from 10 to 50 bypasses continue target 40 "
-                               "(dominance rule violated)"));
-}
-
-TEST_F(
-    SpvParserCFGTest,
-    ClassifyCFGEdges_Fallthrough_BranchConditionalWith_Back_LoopOnInside_NestedMerge_IsError) {  // NOLINT
-    // Code generation assumes OpBranchConditional can't have kCaseFallThrough
-    // with kBack.
-    //
-    // This test has the loop on the inside. The fallthrough is an invalid exit
-    // from the loop. However, the block order gets all messed up because going
-    // from 40 to 50 ends up pulling in 99
-    auto assembly = CommonTypes() + R"(
-     %100 = OpFunction %void None %voidfn
-
-     %10 = OpLabel  ; continue target and
-     OpSelectionMerge %99 None ; switch-selection
-     OpSwitch %selector %99 20 %20 50 %50
-
-       %20 = OpLabel ; first case, and loop header
-       OpLoopMerge %49 %40 None
-       OpBranch %40
-
-       ; try to make a back edge with a fallthrough
-       %40 = OpLabel
-       OpBranchConditional %cond2 %20 %50
-
-       %49 = OpLabel ; loop merge
-       OpBranch %99
-
-     %50 = OpLabel ; second case
-     OpBranch %99
-
-     %99 = OpLabel ; switch merge
-     OpReturn
-
-     OpFunctionEnd
-)";
-    auto p = parser(test::Assemble(assembly));
-    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
-    auto fe = p->function_emitter(100);
-    EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
-    EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 40, 50, 49, 99));
-    EXPECT_THAT(p->error(), Eq("Branch from 10 to 50 bypasses continue target 40 "
-                               "(dominance rule violated)"));
-}
-
-TEST_F(SpvParserCFGTest, ClassifyCFGEdges_Fallthrough_CaseNonTailToCase_TrueBranch) {
-    // This is an unusual one, and is an error. Structurally it looks like this:
-    //   switch (val) {
-    //   case 0: {
-    //        if (cond) {
-    //          fallthrough;
-    //        }
-    //        something = 1;
-    //      }
-    //   case 1: { }
-    //   }
-    auto assembly = CommonTypes() + R"(
-     %100 = OpFunction %void None %voidfn
-
-     %10 = OpLabel
-     OpSelectionMerge %99 None
-     OpSwitch %selector %99 20 %20 50 %50
-
-     %20 = OpLabel
-     OpSelectionMerge %49 None
-     OpBranchConditional %cond %30 %49
-
-     %30 = OpLabel
-     OpBranch %50 ; attempt to fallthrough
-
-     %49 = OpLabel
-     OpBranch %99
-
-     %50 = OpLabel
-     OpBranch %99
-
-     %99 = OpLabel
-     OpReturn
-
-     OpFunctionEnd
-)";
-    auto p = parser(test::Assemble(assembly));
-    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
-    auto fe = p->function_emitter(100);
-    EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
-    EXPECT_THAT(p->error(),
-                Eq("Branch from 10 to 50 bypasses header 20 (dominance rule violated)"));
-}
-
-TEST_F(SpvParserCFGTest, ClassifyCFGEdges_Fallthrough_CaseNonTailToCase_FalseBranch) {
-    // Like previous test, but taking the false branch.
-
-    // This is an unusual one, and is an error. Structurally it looks like this:
-    //   switch (val) {
-    //   case 0: {
-    //        if (cond) {
-    //          fallthrough;
-    //        }
-    //        something = 1;
-    //      }
-    //   case 1: { }
-    //   }
-    auto assembly = CommonTypes() + R"(
-     %100 = OpFunction %void None %voidfn
-
-     %10 = OpLabel
-     OpSelectionMerge %99 None
-     OpSwitch %selector %99 20 %20 50 %50
-
-     %20 = OpLabel
-     OpSelectionMerge %49 None
-     OpBranchConditional %cond %49 %30 ;; this is the difference
-
-     %30 = OpLabel
-     OpBranch %50 ; attempt to fallthrough
-
-     %49 = OpLabel
-     OpBranch %99
-
-     %50 = OpLabel
-     OpBranch %99
-
-     %99 = OpLabel
-     OpReturn
-
-     OpFunctionEnd
-)";
-    auto p = parser(test::Assemble(assembly));
-    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
-    auto fe = p->function_emitter(100);
-    EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
-    EXPECT_THAT(p->error(),
-                Eq("Branch from 10 to 50 bypasses header 20 (dominance rule violated)"));
-}
-
 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_Forward_IfToThen) {
     auto assembly = CommonTypes() + R"(
      %100 = OpFunction %void None %voidfn
@@ -6684,7 +6048,7 @@
 }
 
 TEST_F(SpvParserCFGTest,
-       FindSwitchCaseHeaders_DomViolation_SwitchCase_CantBeMergeForOtherConstruct) {  // NOLINT
+       FindSwitchCaseHeaders_DomViolation_SwitchCase_CantBeMergeForOtherConstruct) {
     auto assembly = CommonTypes() + R"(
      %100 = OpFunction %void None %voidfn
 
@@ -7395,8 +6759,7 @@
 }
 
 TEST_F(SpvParserCFGTest,
-       FindIfSelectionInternalHeaders_DomViolation_InteriorMerge_CantBeTrueHeader) {  // NOLINT -
-                                                                                      // line length
+       FindIfSelectionInternalHeaders_DomViolation_InteriorMerge_CantBeTrueHeader) {
     auto assembly = CommonTypes() + R"(
      %100 = OpFunction %void None %voidfn
 
@@ -7428,10 +6791,8 @@
                    "merge block for header block 20 (violates dominance rule)"));
 }
 
-TEST_F(
-    SpvParserCFGTest,
-    FindIfSelectionInternalHeaders_DomViolation_InteriorMerge_CantBeFalseHeader) {  // NOLINT - line
-                                                                                    // length
+TEST_F(SpvParserCFGTest,
+       FindIfSelectionInternalHeaders_DomViolation_InteriorMerge_CantBeFalseHeader) {
     auto assembly = CommonTypes() + R"(
      %100 = OpFunction %void None %voidfn
 
@@ -9120,7 +8481,7 @@
     ASSERT_EQ(expect, got);
 }
 
-// First do no special control flow: no fallthroughs, breaks, continues.
+// First do no special control flow: no breaks, continues.
 TEST_F(SpvParserCFGTest, EmitBody_Switch_DefaultIsMerge_OneCase) {
     auto p = parser(test::Assemble(CommonTypes() + R"(
      %100 = OpFunction %void None %voidfn
@@ -9307,8 +8668,7 @@
 
 TEST_F(SpvParserCFGTest, EmitBody_Switch_DefaultIsCase_WithDupCase) {
     // The default block is not the merge block and is the same as a case.
-    // We emit the default case separately, but just before the labeled
-    // case, and with a fallthrough.
+    // We emit the default case as part of the labeled case.
     auto p = parser(test::Assemble(CommonTypes() + R"(
      %100 = OpFunction %void None %voidfn
 
@@ -10108,9 +9468,7 @@
 }
 
 TEST_F(SpvParserCFGTest,
-       EmitBody_Branch_LoopBreak_MultiBlockLoop_FromContinueConstructEnd_Unconditional) {  // NOLINT
-                                                                                           // - line
-                                                                                           // length
+       EmitBody_Branch_LoopBreak_MultiBlockLoop_FromContinueConstructEnd_Unconditional) {
     auto p = parser(test::Assemble(CommonTypes() + R"(
      %100 = OpFunction %void None %voidfn
 
@@ -10153,10 +9511,46 @@
     ASSERT_EQ(expect, got);
 }
 
-TEST_F(
-    SpvParserCFGTest,
-    EmitBody_Branch_LoopBreak_MultiBlockLoop_FromContinueConstructEnd_Conditional) {  // NOLINT -
-                                                                                      // line length
+TEST_F(SpvParserCFGTest,
+       EmitBody_Branch_LoopBreak_MultiBlockLoop_FromContinueConstructEnd_Conditional_BreakIf) {
+    auto p = parser(test::Assemble(CommonTypes() + R"(
+     %100 = OpFunction %void None %voidfn
+
+     %10 = OpLabel
+     OpBranch %20
+
+     %20 = OpLabel
+     OpLoopMerge %99 %80 None
+     OpBranch %80
+
+     %80 = OpLabel ; continue target
+     OpStore %var %uint_1
+     OpBranchConditional %cond %99 %20  ; exit, and backedge
+
+     %99 = OpLabel
+     OpReturn
+
+     OpFunctionEnd
+  )"));
+    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
+    auto fe = p->function_emitter(100);
+    EXPECT_TRUE(fe.EmitBody()) << p->error();
+    auto ast_body = fe.ast_body();
+    auto got = test::ToString(p->program(), ast_body);
+    auto* expect = R"(loop {
+
+  continuing {
+    var_1 = 1u;
+    break if false;
+  }
+}
+return;
+)";
+    ASSERT_EQ(expect, got);
+}
+
+TEST_F(SpvParserCFGTest,
+       EmitBody_Branch_LoopBreak_MultiBlockLoop_FromContinueConstructEnd_Conditional) {
     auto p = parser(test::Assemble(CommonTypes() + R"(
      %100 = OpFunction %void None %voidfn
 
@@ -10185,10 +9579,48 @@
 
   continuing {
     var_1 = 1u;
-    if (false) {
-    } else {
-      break;
-    }
+    break if !(false);
+  }
+}
+return;
+)";
+    ASSERT_EQ(expect, got);
+}
+
+TEST_F(SpvParserCFGTest, EmitBody_Branch_LoopBreak_FromContinueConstructTail) {
+    auto p = parser(test::Assemble(CommonTypes() + R"(
+     %100 = OpFunction %void None %voidfn
+
+     %10 = OpLabel
+     OpBranch %20
+
+     %20 = OpLabel
+     OpLoopMerge %99 %50 None
+     OpBranchConditional %cond %30 %99
+     %30 = OpLabel
+     OpBranch %50
+     %50 = OpLabel
+     OpBranch %60
+     %60 = OpLabel
+     OpBranchConditional %cond %20 %99
+     %99 = OpLabel
+     OpReturn
+
+     OpFunctionEnd
+  )"));
+    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
+    auto fe = p->function_emitter(100);
+    EXPECT_TRUE(fe.EmitBody()) << p->error();
+    auto ast_body = fe.ast_body();
+    auto got = test::ToString(p->program(), ast_body);
+    auto* expect = R"(loop {
+  if (false) {
+  } else {
+    break;
+  }
+
+  continuing {
+    break if !(false);
   }
 }
 return;
@@ -10423,53 +9855,6 @@
     ASSERT_EQ(expect, got);
 }
 
-TEST_F(SpvParserCFGTest, EmitBody_Branch_Fallthrough) {
-    auto p = parser(test::Assemble(CommonTypes() + R"(
-     %100 = OpFunction %void None %voidfn
-
-     %10 = OpLabel
-     OpStore %var %uint_1
-     OpSelectionMerge %99 None
-     OpSwitch %selector %99 20 %20 30 %30
-
-     %20 = OpLabel
-     OpStore %var %uint_20
-     OpBranch %30 ; uncondtional fallthrough
-
-     %30 = OpLabel
-     OpStore %var %uint_30
-     OpBranch %99
-
-     %99 = OpLabel
-     OpStore %var %uint_7
-     OpReturn
-
-     OpFunctionEnd
-  )"));
-    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
-    auto fe = p->function_emitter(100);
-    EXPECT_TRUE(fe.EmitBody()) << p->error();
-
-    auto ast_body = fe.ast_body();
-    auto got = test::ToString(p->program(), ast_body);
-    auto* expect = R"(var_1 = 1u;
-switch(42u) {
-  case 20u: {
-    var_1 = 20u;
-    fallthrough;
-  }
-  case 30u: {
-    var_1 = 30u;
-  }
-  default: {
-  }
-}
-var_1 = 7u;
-return;
-)";
-    ASSERT_EQ(expect, got);
-}
-
 TEST_F(SpvParserCFGTest, EmitBody_Branch_Forward) {
     auto p = parser(test::Assemble(CommonTypes() + R"(
      %100 = OpFunction %void None %voidfn
@@ -10507,7 +9892,6 @@
 //                      If continue is forward, then it's a continue from a
 //                      continue which is also invalid.
 //      kIfBreak: invalid: loop and if must have distinct merge blocks
-//      kCaseFallThrough: invalid: loop header must dominate its merge
 //      kForward: impossible; would be a loop break
 //
 //    kSwitchBreak with:
@@ -10516,7 +9900,6 @@
 //      kLoopBreak: invalid; only one kind of break allowed
 //      kLoopContinue: TESTED
 //      kIfBreak: invalid: switch and if must have distinct merge blocks
-//      kCaseFallThrough: TESTED
 //      kForward: TESTED
 //
 //    kLoopBreak with:
@@ -10525,7 +9908,6 @@
 //      kLoopBreak: dup general case
 //      kLoopContinue: TESTED
 //      kIfBreak: invalid: switch and if must have distinct merge blocks
-//      kCaseFallThrough: not possible, because switch break conflicts with loop
 //      break kForward: TESTED
 //
 //    kLoopContinue with:
@@ -10534,7 +9916,6 @@
 //      kLoopBreak: symmetry
 //      kLoopContinue: dup general case
 //      kIfBreak: TESTED
-//      kCaseFallThrough: TESTED
 //      kForward: TESTED
 //
 //    kIfBreak with:
@@ -10543,25 +9924,14 @@
 //      kLoopBreak: symmetry
 //      kLoopContinue: symmetry
 //      kIfBreak: dup general case
-//      kCaseFallThrough: invalid; violates nesting or unique merges
 //      kForward: invalid: needs a merge instruction
 //
-//    kCaseFallThrough with:
-//      kBack : symmetry
-//      kSwitchBreak: symmetry
-//      kLoopBreak: symmetry
-//      kLoopContinue: symmetry
-//      kIfBreak: symmetry
-//      kCaseFallThrough: dup general case
-//      kForward: invalid (tested)
-//
 //    kForward with:
 //      kBack : symmetry
 //      kSwitchBreak: symmetry
 //      kLoopBreak: symmetry
 //      kLoopContinue: symmetry
 //      kIfBreak: symmetry
-//      kCaseFallThrough: symmetry
 //      kForward: dup general case
 
 TEST_F(SpvParserCFGTest, EmitBody_BranchConditional_Back_SingleBlock_Back) {
@@ -10705,9 +10075,7 @@
   var_1 = 1u;
 
   continuing {
-    if (false) {
-      break;
-    }
+    break if false;
   }
 }
 var_1 = 5u;
@@ -10748,10 +10116,7 @@
   var_1 = 1u;
 
   continuing {
-    if (false) {
-    } else {
-      break;
-    }
+    break if !(false);
   }
 }
 var_1 = 5u;
@@ -11085,107 +10450,6 @@
     ASSERT_EQ(expect, got);
 }
 
-TEST_F(SpvParserCFGTest, EmitBody_BranchConditional_SwitchBreak_Fallthrough_OnTrue) {
-    auto p = parser(test::Assemble(CommonTypes() + R"(
-     %100 = OpFunction %void None %voidfn
-
-     %10 = OpLabel
-     OpStore %var %uint_1
-     OpSelectionMerge %99 None
-     OpSwitch %selector %99 20 %20 30 %30
-
-     %20 = OpLabel
-     OpStore %var %uint_20
-     OpBranchConditional %cond %30 %99; fallthrough on true
-
-     %30 = OpLabel
-     OpStore %var %uint_30
-     OpBranch %99
-
-     %99 = OpLabel
-     OpStore %var %uint_7
-     OpReturn
-
-     OpFunctionEnd
-  )"));
-    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
-    auto fe = p->function_emitter(100);
-    EXPECT_TRUE(fe.EmitBody()) << p->error();
-
-    auto ast_body = fe.ast_body();
-    auto got = test::ToString(p->program(), ast_body);
-    auto* expect = R"(var_1 = 1u;
-switch(42u) {
-  case 20u: {
-    var_1 = 20u;
-    if (false) {
-    } else {
-      break;
-    }
-    fallthrough;
-  }
-  case 30u: {
-    var_1 = 30u;
-  }
-  default: {
-  }
-}
-var_1 = 7u;
-return;
-)";
-    ASSERT_EQ(expect, got);
-}
-
-TEST_F(SpvParserCFGTest, EmitBody_BranchConditional_SwitchBreak_Fallthrough_OnFalse) {
-    auto p = parser(test::Assemble(CommonTypes() + R"(
-     %100 = OpFunction %void None %voidfn
-
-     %10 = OpLabel
-     OpStore %var %uint_1
-     OpSelectionMerge %99 None
-     OpSwitch %selector %99 20 %20 30 %30
-
-     %20 = OpLabel
-     OpStore %var %uint_20
-     OpBranchConditional %cond %99 %30; fallthrough on false
-
-     %30 = OpLabel
-     OpStore %var %uint_30
-     OpBranch %99
-
-     %99 = OpLabel
-     OpStore %var %uint_7
-     OpReturn
-
-     OpFunctionEnd
-  )"));
-    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
-    auto fe = p->function_emitter(100);
-    EXPECT_TRUE(fe.EmitBody()) << p->error();
-
-    auto ast_body = fe.ast_body();
-    auto got = test::ToString(p->program(), ast_body);
-    auto* expect = R"(var_1 = 1u;
-switch(42u) {
-  case 20u: {
-    var_1 = 20u;
-    if (false) {
-      break;
-    }
-    fallthrough;
-  }
-  case 30u: {
-    var_1 = 30u;
-  }
-  default: {
-  }
-}
-var_1 = 7u;
-return;
-)";
-    ASSERT_EQ(expect, got);
-}
-
 TEST_F(SpvParserCFGTest, EmitBody_BranchConditional_LoopBreak_SingleBlock_LoopBreak) {
     auto p = parser(test::Assemble(CommonTypes() + R"(
      %100 = OpFunction %void None %voidfn
@@ -11411,53 +10675,6 @@
     ASSERT_EQ(expect, got);
 }
 
-TEST_F(SpvParserCFGTest, EmitBody_BranchConditional_LoopBreak_Fallthrough_IsError) {
-    // It's an error because switch break conflicts with loop break.
-    auto p = parser(test::Assemble(CommonTypes() + R"(
-     %100 = OpFunction %void None %voidfn
-
-     %10 = OpLabel
-     OpStore %var %uint_0
-     OpBranch %20
-
-     %20 = OpLabel
-     OpStore %var %uint_1
-     OpLoopMerge %99 %80 None
-     OpBranch %30
-
-     %30 = OpLabel
-     OpSelectionMerge %79 None
-     OpSwitch %selector %79 40 %40 50 %50
-
-     %40 = OpLabel
-     OpStore %var %uint_40
-     ; error: branch to 99 bypasses switch's merge
-     OpBranchConditional %cond %99 %50 ; loop break; fall through
-
-     %50 = OpLabel
-     OpStore %var %uint_50
-     OpBranch %79
-
-     %79 = OpLabel ; switch merge
-     OpBranch %80
-
-     %80 = OpLabel ; continue target
-     OpStore %var %uint_4
-     OpBranch %20
-
-     %99 = OpLabel
-     OpStore %var %uint_5
-     OpReturn
-
-     OpFunctionEnd
-  )"));
-    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
-    auto fe = p->function_emitter(100);
-    EXPECT_FALSE(fe.EmitBody()) << p->error();
-    EXPECT_THAT(p->error(), Eq("Branch from block 40 to block 99 is an invalid exit from construct "
-                               "starting at block 30; branch bypasses merge block 79"));
-}
-
 TEST_F(SpvParserCFGTest, EmitBody_BranchConditional_LoopBreak_Forward_OnTrue) {
     auto p = parser(test::Assemble(CommonTypes() + R"(
      %100 = OpFunction %void None %voidfn
@@ -11662,7 +10879,6 @@
 
 TEST_F(SpvParserCFGTest, EmitBody_BranchConditional_Continue_Continue_AfterHeader_Conditional) {
     // Create an intervening block so we actually require a "continue" statement
-    // instead of just an adjacent fallthrough to the continue target.
     auto p = parser(test::Assemble(CommonTypes() + R"(
      %100 = OpFunction %void None %voidfn
 
@@ -11723,9 +10939,8 @@
     ASSERT_EQ(expect, got);
 }
 
-TEST_F(
-    SpvParserCFGTest,
-    EmitBody_BranchConditional_Continue_Continue_AfterHeader_Conditional_EmptyContinuing) {  // NOLINT
+TEST_F(SpvParserCFGTest,
+       EmitBody_BranchConditional_Continue_Continue_AfterHeader_Conditional_EmptyContinuing) {
     // Like the previous tests, but with an empty continuing clause.
     auto p = parser(test::Assemble(CommonTypes() + R"(
      %100 = OpFunction %void None %voidfn
@@ -11977,157 +11192,6 @@
     ASSERT_EQ(expect, got);
 }
 
-TEST_F(SpvParserCFGTest, EmitBody_BranchConditional_Continue_Fallthrough_OnTrue) {
-    auto p = parser(test::Assemble(CommonTypes() + R"(
-     %100 = OpFunction %void None %voidfn
-
-     %10 = OpLabel
-     OpStore %var %uint_0
-     OpBranch %20
-
-     %20 = OpLabel
-     OpStore %var %uint_1
-     OpLoopMerge %99 %80 None
-     OpBranch %30
-
-     %30 = OpLabel
-     OpStore %var %uint_2
-     OpSelectionMerge %79 None
-     OpSwitch %selector %79 40 %40 50 %50
-
-     %40 = OpLabel
-     OpStore %var %uint_40
-     OpBranchConditional %cond %50 %80 ; loop continue; fall through on true
-
-     %50 = OpLabel
-     OpStore %var %uint_50
-     OpBranch %79
-
-     %79 = OpLabel ; switch merge
-     OpStore %var %uint_3
-     OpBranch %80
-
-     %80 = OpLabel ; continue target
-     OpStore %var %uint_4
-     OpBranch %20
-
-     %99 = OpLabel
-     OpStore %var %uint_5
-     OpReturn
-
-     OpFunctionEnd
-  )"));
-    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
-    auto fe = p->function_emitter(100);
-    EXPECT_TRUE(fe.EmitBody()) << p->error();
-    auto ast_body = fe.ast_body();
-    auto got = test::ToString(p->program(), ast_body);
-    auto* expect = R"(var_1 = 0u;
-loop {
-  var_1 = 1u;
-  var_1 = 2u;
-  switch(42u) {
-    case 40u: {
-      var_1 = 40u;
-      if (false) {
-      } else {
-        continue;
-      }
-      fallthrough;
-    }
-    case 50u: {
-      var_1 = 50u;
-    }
-    default: {
-    }
-  }
-  var_1 = 3u;
-
-  continuing {
-    var_1 = 4u;
-  }
-}
-var_1 = 5u;
-return;
-)";
-    ASSERT_EQ(expect, got);
-}
-
-TEST_F(SpvParserCFGTest, EmitBody_BranchConditional_Continue_Fallthrough_OnFalse) {
-    auto p = parser(test::Assemble(CommonTypes() + R"(
-     %100 = OpFunction %void None %voidfn
-
-     %10 = OpLabel
-     OpStore %var %uint_0
-     OpBranch %20
-
-     %20 = OpLabel
-     OpStore %var %uint_1
-     OpLoopMerge %99 %80 None
-     OpBranch %30
-
-     %30 = OpLabel
-     OpStore %var %uint_2
-     OpSelectionMerge %79 None
-     OpSwitch %selector %79 40 %40 50 %50
-
-     %40 = OpLabel
-     OpStore %var %uint_40
-     OpBranchConditional %cond %80 %50 ; loop continue; fall through on false
-
-     %50 = OpLabel
-     OpStore %var %uint_50
-     OpBranch %79
-
-     %79 = OpLabel ; switch merge
-     OpStore %var %uint_3
-     OpBranch %80
-
-     %80 = OpLabel ; continue target
-     OpStore %var %uint_4
-     OpBranch %20
-
-     %99 = OpLabel
-     OpStore %var %uint_5
-     OpReturn
-
-     OpFunctionEnd
-  )"));
-    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
-    auto fe = p->function_emitter(100);
-    EXPECT_TRUE(fe.EmitBody()) << p->error();
-    auto ast_body = fe.ast_body();
-    auto got = test::ToString(p->program(), ast_body);
-    auto* expect = R"(var_1 = 0u;
-loop {
-  var_1 = 1u;
-  var_1 = 2u;
-  switch(42u) {
-    case 40u: {
-      var_1 = 40u;
-      if (false) {
-        continue;
-      }
-      fallthrough;
-    }
-    case 50u: {
-      var_1 = 50u;
-    }
-    default: {
-    }
-  }
-  var_1 = 3u;
-
-  continuing {
-    var_1 = 4u;
-  }
-}
-var_1 = 5u;
-return;
-)";
-    ASSERT_EQ(expect, got);
-}
-
 TEST_F(SpvParserCFGTest, EmitBody_BranchConditional_Continue_Forward_OnTrue) {
     auto p = parser(test::Assemble(CommonTypes() + R"(
      %100 = OpFunction %void None %voidfn
@@ -12308,91 +11372,6 @@
                                "starting at block 20; branch bypasses merge block 89"));
 }
 
-TEST_F(SpvParserCFGTest, EmitBody_BranchConditional_Fallthrough_Fallthrough_Same) {
-    auto p = parser(test::Assemble(CommonTypes() + R"(
-     %100 = OpFunction %void None %voidfn
-
-     %10 = OpLabel
-     OpStore %var %uint_1
-     OpSelectionMerge %99 None
-     OpSwitch %selector %99 20 %20 30 %30
-
-     %20 = OpLabel
-     OpStore %var %uint_20
-     OpBranchConditional %cond %30 %30 ; fallthrough fallthrough
-
-     %30 = OpLabel
-     OpStore %var %uint_30
-     OpBranch %99
-
-     %99 = OpLabel
-     OpStore %var %uint_7
-     OpReturn
-
-     OpFunctionEnd
-  )"));
-    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
-    auto fe = p->function_emitter(100);
-    EXPECT_TRUE(fe.EmitBody()) << p->error();
-
-    auto ast_body = fe.ast_body();
-    auto got = test::ToString(p->program(), ast_body);
-    auto* expect = R"(var_1 = 1u;
-switch(42u) {
-  case 20u: {
-    var_1 = 20u;
-    fallthrough;
-  }
-  case 30u: {
-    var_1 = 30u;
-  }
-  default: {
-  }
-}
-var_1 = 7u;
-return;
-)";
-    ASSERT_EQ(expect, got);
-}
-
-TEST_F(SpvParserCFGTest, EmitBody_BranchConditional_Fallthrough_NotLastInCase_IsError) {
-    // See also
-    // ClassifyCFGEdges_Fallthrough_BranchConditionalWith_Forward_IsError.
-    auto p = parser(test::Assemble(CommonTypes() + R"(
-     %100 = OpFunction %void None %voidfn
-
-     %10 = OpLabel
-     OpSelectionMerge %99 None
-     OpSwitch %selector %99 20 %20 40 %40
-
-     %20 = OpLabel ; case 30
-     OpSelectionMerge %39 None
-     OpBranchConditional %cond %40 %30 ; fallthrough and forward
-
-     %30 = OpLabel
-     OpBranch %39
-
-     %39 = OpLabel
-     OpBranch %99
-
-     %40 = OpLabel  ; case 40
-     OpBranch %99
-
-     %99 = OpLabel
-     OpReturn
-
-     OpFunctionEnd
-  )"));
-    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
-    auto fe = p->function_emitter(100);
-    EXPECT_FALSE(fe.EmitBody());
-    // The weird forward branch pulls in 40 as part of the selection rather than
-    // as a case.
-    EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 40, 30, 39, 99));
-    EXPECT_THAT(p->error(),
-                Eq("Branch from 10 to 40 bypasses header 20 (dominance rule violated)"));
-}
-
 TEST_F(SpvParserCFGTest, EmitBody_BranchConditional_Forward_Forward_Same) {
     auto p = parser(test::Assemble(CommonTypes() + R"(
      %100 = OpFunction %void None %voidfn
diff --git a/src/tint/reader/spirv/function_decl_test.cc b/src/tint/reader/spirv/function_decl_test.cc
index 8af9da2..0c4834b 100644
--- a/src/tint/reader/spirv/function_decl_test.cc
+++ b/src/tint/reader/spirv/function_decl_test.cc
@@ -52,6 +52,19 @@
   )";
 }
 
+std::string CommonHandleTypes() {
+    return CommonTypes() + R"(
+    %v2float = OpTypeVector %float 2
+    %v4float = OpTypeVector %float 4
+    %v2_0 = OpConstantNull %v2float
+    %sampler = OpTypeSampler
+    %tex2d_f32 = OpTypeImage %float 2D 0 0 0 1 Unknown
+    %sampled_image_2d_f32 = OpTypeSampledImage %tex2d_f32
+    %ptr_sampler = OpTypePointer UniformConstant %sampler
+    %ptr_tex2d_f32 = OpTypePointer UniformConstant %tex2d_f32
+  )";
+}
+
 std::string MainBody() {
     return R"(
     %100 = OpFunction %void None %voidfn
@@ -147,5 +160,67 @@
     EXPECT_THAT(got, HasSubstr(expect));
 }
 
+TEST_F(SpvParserTest, Emit_FunctionDecl_ParamPtrTexture_ParamPtrSampler) {
+    auto p = parser(test::Assemble(Preamble() + CommonHandleTypes() + R"(
+
+     ; This is how Glslang generates functions that take texture and sampler arguments.
+     ; It passes them by pointer.
+     %fn_ty = OpTypeFunction %void %ptr_tex2d_f32 %ptr_sampler
+
+     %200 = OpFunction %void None %fn_ty
+     %14 = OpFunctionParameter %ptr_tex2d_f32
+     %15 = OpFunctionParameter %ptr_sampler
+     %mixed_entry = OpLabel
+     ; access the texture, to give the handles usages.
+     %im = OpLoad %tex2d_f32 %14
+     %sam = OpLoad %sampler %15
+     %imsam = OpSampledImage %sampled_image_2d_f32 %im %sam
+     %20 = OpImageSampleImplicitLod %v4float %imsam %v2_0
+     OpReturn
+     OpFunctionEnd
+  )" + MainBody()));
+    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+    auto fe = p->function_emitter(200);
+    EXPECT_TRUE(fe.Emit());
+
+    auto got = test::ToString(p->program());
+    std::string expect = R"(fn x_200(x_14 : texture_2d<f32>, x_15 : sampler) {
+  let x_20 : vec4<f32> = textureSample(x_14, x_15, vec2<f32>());
+  return;
+}
+)";
+    EXPECT_EQ(got, expect);
+}
+
+TEST_F(SpvParserTest, Emit_FunctionDecl_ParamTexture_ParamSampler) {
+    auto assembly = Preamble() + CommonHandleTypes() + R"(
+
+     ; It is valid in SPIR-V to pass textures and samplers by value.
+     %fn_ty = OpTypeFunction %void %tex2d_f32 %sampler
+
+     %200 = OpFunction %void None %fn_ty
+     %14 = OpFunctionParameter %tex2d_f32
+     %15 = OpFunctionParameter %sampler
+     %mixed_entry = OpLabel
+     ; access the texture, to give the handles usages.
+     %imsam = OpSampledImage %sampled_image_2d_f32 %14 %15
+     %20 = OpImageSampleImplicitLod %v4float %imsam %v2_0
+     OpReturn
+     OpFunctionEnd
+  )" + MainBody();
+    auto p = parser(test::Assemble(assembly));
+    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error() << assembly;
+    auto fe = p->function_emitter(200);
+    EXPECT_TRUE(fe.Emit());
+
+    auto got = test::ToString(p->program());
+    std::string expect = R"(fn x_200(x_14 : texture_2d<f32>, x_15 : sampler) {
+  let x_20 : vec4<f32> = textureSample(x_14, x_15, vec2<f32>());
+  return;
+}
+)";
+    EXPECT_EQ(got, expect);
+}
+
 }  // namespace
 }  // namespace tint::reader::spirv
diff --git a/src/tint/reader/spirv/function_var_test.cc b/src/tint/reader/spirv/function_var_test.cc
index e3d2acf..1119667 100644
--- a/src/tint/reader/spirv/function_var_test.cc
+++ b/src/tint/reader/spirv/function_var_test.cc
@@ -689,9 +689,7 @@
 
   continuing {
     x_1 = 4u;
-    if (false) {
-      break;
-    }
+    break if false;
   }
 }
 x_1 = 5u;
@@ -1344,73 +1342,6 @@
     EXPECT_EQ(expect, got) << got;
 }
 
-TEST_F(SpvParserFunctionVarTest, EmitStatement_Phi_InMerge_PredecessorsDominatdByNestedSwitchCase) {
-    // This is the essence of the bug report from crbug.com/tint/495
-    auto assembly = Preamble() + R"(
-     %cond = OpConstantTrue %bool
-     %pty = OpTypePointer Private %uint
-     %1 = OpVariable %pty Private
-     %boolpty = OpTypePointer Private %bool
-     %7 = OpVariable %boolpty Private
-     %8 = OpVariable %boolpty Private
-
-     %100 = OpFunction %void None %voidfn
-
-     %10 = OpLabel
-     OpSelectionMerge %99 None
-     OpSwitch %uint_1 %20 0 %20 1 %30
-
-       %20 = OpLabel ; case 0
-       OpBranch %30 ;; fall through
-
-       %30 = OpLabel ; case 1
-       OpSelectionMerge %50 None
-       OpBranchConditional %true %40 %45
-
-         %40 = OpLabel
-         OpBranch %50
-
-         %45 = OpLabel
-         OpBranch %99 ; break
-
-       %50 = OpLabel ; end the case
-       OpBranch %99
-
-     %99 = OpLabel
-     ; predecessors are all dominated by case construct head at %30
-     %41 = OpPhi %uint %uint_0 %45 %uint_1 %50
-     %101 = OpCopyObject %uint %41 ; give it a use so it's emitted
-     OpReturn
-
-     OpFunctionEnd
-  )";
-    auto p = parser(test::Assemble(assembly));
-    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
-    auto fe = p->function_emitter(100);
-    EXPECT_TRUE(fe.EmitBody()) << p->error();
-
-    auto ast_body = fe.ast_body();
-    auto got = test::ToString(p->program(), ast_body);
-    auto* expect = R"(var x_41 : u32;
-switch(1u) {
-  case 0u, default: {
-    fallthrough;
-  }
-  case 1u: {
-    if (true) {
-    } else {
-      x_41 = 0u;
-      break;
-    }
-    x_41 = 1u;
-  }
-}
-let x_101 : u32 = x_41;
-return;
-)";
-    EXPECT_EQ(expect, got) << got << assembly;
-}
-
 TEST_F(SpvParserFunctionVarTest, EmitStatement_Phi_UseInPhiCountsAsUse) {
     // From crbug.com/215
     // If the only use of a combinatorially computed ID is as the value
@@ -1677,9 +1608,7 @@
     x_999 = false;
 
     continuing {
-      if (true) {
-        break;
-      }
+      break if true;
     }
   }
 }
@@ -1761,9 +1690,7 @@
     x_999 = false;
 
     continuing {
-      if (true) {
-        break;
-      }
+      break if true;
     }
   }
 
diff --git a/src/tint/reader/spirv/parser_impl.cc b/src/tint/reader/spirv/parser_impl.cc
index f055f4c..600e570 100644
--- a/src/tint/reader/spirv/parser_impl.cc
+++ b/src/tint/reader/spirv/parser_impl.cc
@@ -1183,7 +1183,7 @@
                         break;
                     case spv::BuiltIn::ClipDistance:  // not supported in WGSL
                     case spv::BuiltIn::CullDistance:  // not supported in WGSL
-                        create_ast_member = false;  // Not part of the WGSL structure.
+                        create_ast_member = false;    // Not part of the WGSL structure.
                         break;
                     default:
                         Fail() << "unrecognized builtin " << decoration[1];
@@ -1535,28 +1535,33 @@
         if (!success_) {
             return false;
         }
-        const Type* ast_type = nullptr;
+        const Type* ast_store_type = nullptr;
+        ast::AddressSpace ast_address_space = ast::AddressSpace::kNone;
         if (spirv_storage_class == spv::StorageClass::UniformConstant) {
             // These are opaque handles: samplers or textures
-            ast_type = GetTypeForHandleVar(var);
-            if (!ast_type) {
+            ast_store_type = GetHandleTypeForSpirvHandle(var);
+            if (!ast_store_type) {
                 return false;
             }
+            // ast_storage_class should remain kNone because handle variables
+            // are never declared with an explicit address space.
         } else {
-            ast_type = ConvertType(type_id);
+            const Type* ast_type = ConvertType(type_id);
             if (ast_type == nullptr) {
                 return Fail() << "internal error: failed to register Tint AST type for "
                                  "SPIR-V type with ID: "
                               << var.type_id();
             }
-            if (!ast_type->Is<Pointer>()) {
+            if (auto* ast_ptr_type = ast_type->As<Pointer>()) {
+                ast_store_type = ast_ptr_type->type;
+                ast_address_space = ast_ptr_type->address_space;
+            } else {
                 return Fail() << "variable with ID " << var.result_id() << " has non-pointer type "
                               << var.type_id();
             }
         }
+        TINT_ASSERT(Reader, ast_store_type != nullptr);
 
-        auto* ast_store_type = ast_type->As<Pointer>()->type;
-        auto ast_address_space = ast_type->As<Pointer>()->address_space;
         const ast::Expression* ast_initializer = nullptr;
         if (var.NumInOperands() > 1) {
             // SPIR-V initializers are always constants.
@@ -2020,10 +2025,15 @@
                                        ast::IntLiteralExpression::Suffix::kU)};
         },
         [&](const F32*) {
-            return TypedExpression{ty_.F32(),
-                                   create<ast::FloatLiteralExpression>(
-                                       source, static_cast<double>(spirv_const->GetFloat()),
-                                       ast::FloatLiteralExpression::Suffix::kF)};
+            if (auto f = CheckedConvert<f32>(AFloat(spirv_const->GetFloat()))) {
+                return TypedExpression{ty_.F32(),
+                                       create<ast::FloatLiteralExpression>(
+                                           source, static_cast<double>(spirv_const->GetFloat()),
+                                           ast::FloatLiteralExpression::Suffix::kF)};
+            } else {
+                Fail() << "value cannot be represented as 'f32': " << spirv_const->GetFloat();
+                return TypedExpression{};
+            }
         },
         [&](const Bool*) {
             const bool value =
@@ -2355,8 +2365,8 @@
     }
 }
 
-const spvtools::opt::Instruction* ParserImpl::GetSpirvTypeForHandleMemoryObjectDeclaration(
-    const spvtools::opt::Instruction& var) {
+const spvtools::opt::Instruction* ParserImpl::GetSpirvTypeForHandleOrHandleMemoryObjectDeclaration(
+    const spvtools::opt::Instruction& obj) {
     if (!success()) {
         return nullptr;
     }
@@ -2371,14 +2381,31 @@
     // are the only SPIR-V handles supported by WGSL.
 
     // Get the SPIR-V handle type.
-    const auto* ptr_type = def_use_mgr_->GetDef(var.type_id());
-    if (!ptr_type || (opcode(ptr_type) != spv::Op::OpTypePointer)) {
-        Fail() << "Invalid type for variable or function parameter " << var.PrettyPrint();
+    const auto* type = def_use_mgr_->GetDef(obj.type_id());
+    if (!type) {
+        Fail() << "Invalid type for image, sampler, variable or function parameter to image or "
+                  "sampler "
+               << obj.PrettyPrint();
         return nullptr;
     }
-    const auto* raw_handle_type = def_use_mgr_->GetDef(ptr_type->GetSingleWordInOperand(1));
+    switch (opcode(type)) {
+        case spv::Op::OpTypeSampler:
+        case spv::Op::OpTypeImage:
+            return type;
+        case spv::Op::OpTypePointer:
+            // The remaining cases.
+            break;
+        default:
+            Fail() << "Invalid type for image, sampler, variable or function parameter to image or "
+                      "sampler "
+                   << obj.PrettyPrint();
+            return nullptr;
+    }
+
+    // Look at the pointee type instead.
+    const auto* raw_handle_type = def_use_mgr_->GetDef(type->GetSingleWordInOperand(1));
     if (!raw_handle_type) {
-        Fail() << "Invalid pointer type for variable or function parameter " << var.PrettyPrint();
+        Fail() << "Invalid pointer type for variable or function parameter " << obj.PrettyPrint();
         return nullptr;
     }
     switch (opcode(raw_handle_type)) {
@@ -2390,38 +2417,39 @@
         case spv::Op::OpTypeRuntimeArray:
             Fail() << "arrays of textures or samplers are not supported in WGSL; can't "
                       "translate variable or function parameter: "
-                   << var.PrettyPrint();
+                   << obj.PrettyPrint();
             return nullptr;
         case spv::Op::OpTypeSampledImage:
-            Fail() << "WGSL does not support combined image-samplers: " << var.PrettyPrint();
+            Fail() << "WGSL does not support combined image-samplers: " << obj.PrettyPrint();
             return nullptr;
         default:
             Fail() << "invalid type for image or sampler variable or function "
                       "parameter: "
-                   << var.PrettyPrint();
+                   << obj.PrettyPrint();
             return nullptr;
     }
     return raw_handle_type;
 }
 
-const Pointer* ParserImpl::GetTypeForHandleVar(const spvtools::opt::Instruction& var) {
-    auto where = handle_type_.find(&var);
+const Type* ParserImpl::GetHandleTypeForSpirvHandle(const spvtools::opt::Instruction& obj) {
+    auto where = handle_type_.find(&obj);
     if (where != handle_type_.end()) {
         return where->second;
     }
 
     const spvtools::opt::Instruction* raw_handle_type =
-        GetSpirvTypeForHandleMemoryObjectDeclaration(var);
+        GetSpirvTypeForHandleOrHandleMemoryObjectDeclaration(obj);
     if (!raw_handle_type) {
         return nullptr;
     }
 
-    // The variable could be a sampler or image.
+    // The memory object declaration could be a sampler or image.
     // Where possible, determine which one it is from the usage inferred
     // for the variable.
-    Usage usage = handle_usage_[&var];
+    Usage usage = handle_usage_[&obj];
     if (!usage.IsValid()) {
-        Fail() << "Invalid sampler or texture usage for variable " << var.PrettyPrint() << "\n"
+        Fail() << "Invalid sampler or texture usage for variable or function parameter "
+               << obj.PrettyPrint() << "\n"
                << usage;
         return nullptr;
     }
@@ -2447,7 +2475,7 @@
                 // Get NonWritable and NonReadable attributes of the variable.
                 bool is_nonwritable = false;
                 bool is_nonreadable = false;
-                for (const auto& deco : GetDecorationsFor(var.result_id())) {
+                for (const auto& deco : GetDecorationsFor(obj.result_id())) {
                     if (deco.size() != 1) {
                         continue;
                     }
@@ -2460,11 +2488,11 @@
                 }
                 if (is_nonwritable && is_nonreadable) {
                     Fail() << "storage image variable is both NonWritable and NonReadable"
-                           << var.PrettyPrint();
+                           << obj.PrettyPrint();
                 }
                 if (!is_nonwritable && !is_nonreadable) {
                     Fail() << "storage image variable is neither NonWritable nor NonReadable"
-                           << var.PrettyPrint();
+                           << obj.PrettyPrint();
                 }
                 // Let's make it one of the storage textures.
                 if (is_nonwritable) {
@@ -2484,9 +2512,9 @@
     }
 
     // Construct the Tint handle type.
-    const Type* ast_store_type = nullptr;
+    const Type* ast_handle_type = nullptr;
     if (usage.IsSampler()) {
-        ast_store_type =
+        ast_handle_type =
             ty_.Sampler(usage.IsComparisonSampler() ? ast::SamplerKind::kComparisonSampler
                                                     : ast::SamplerKind::kSampler);
     } else if (usage.IsTexture()) {
@@ -2507,8 +2535,8 @@
                     break;
                 default:
                     Fail() << "WGSL arrayed textures must be 2d_array or cube_array: "
-                              "invalid multisampled texture variable "
-                           << namer_.Name(var.result_id()) << ": " << var.PrettyPrint();
+                              "invalid multisampled texture variable or function parameter "
+                           << namer_.Name(obj.result_id()) << ": " << obj.PrettyPrint();
                     return nullptr;
             }
         }
@@ -2533,20 +2561,20 @@
             // treat that as a depth texture.
             if (image_type->depth() || usage.IsDepthTexture()) {
                 if (image_type->is_multisampled()) {
-                    ast_store_type = ty_.DepthMultisampledTexture(dim);
+                    ast_handle_type = ty_.DepthMultisampledTexture(dim);
                 } else {
-                    ast_store_type = ty_.DepthTexture(dim);
+                    ast_handle_type = ty_.DepthTexture(dim);
                 }
             } else if (image_type->is_multisampled()) {
                 if (dim != ast::TextureDimension::k2d) {
                     Fail() << "WGSL multisampled textures must be 2d and non-arrayed: "
-                              "invalid multisampled texture variable "
-                           << namer_.Name(var.result_id()) << ": " << var.PrettyPrint();
+                              "invalid multisampled texture variable or function parameter "
+                           << namer_.Name(obj.result_id()) << ": " << obj.PrettyPrint();
                 }
                 // Multisampled textures are never depth textures.
-                ast_store_type = ty_.MultisampledTexture(dim, ast_sampled_component_type);
+                ast_handle_type = ty_.MultisampledTexture(dim, ast_sampled_component_type);
             } else {
-                ast_store_type = ty_.SampledTexture(dim, ast_sampled_component_type);
+                ast_handle_type = ty_.SampledTexture(dim, ast_sampled_component_type);
             }
         } else {
             const auto access = ast::Access::kWrite;
@@ -2554,20 +2582,18 @@
             if (format == ast::TexelFormat::kUndefined) {
                 return nullptr;
             }
-            ast_store_type = ty_.StorageTexture(dim, format, access);
+            ast_handle_type = ty_.StorageTexture(dim, format, access);
         }
     } else {
-        Fail() << "unsupported: UniformConstant variable is not a recognized "
-                  "sampler or texture"
-               << var.PrettyPrint();
+        Fail() << "unsupported: UniformConstant variable or function parameter is not a recognized "
+                  "sampler or texture "
+               << obj.PrettyPrint();
         return nullptr;
     }
 
-    // Form the pointer type.
-    auto* result = ty_.Pointer(ast_store_type, ast::AddressSpace::kHandle);
     // Remember it for later.
-    handle_type_[&var] = result;
-    return result;
+    handle_type_[&obj] = ast_handle_type;
+    return ast_handle_type;
 }
 
 const Type* ParserImpl::GetComponentTypeForFormat(ast::TexelFormat format) {
diff --git a/src/tint/reader/spirv/parser_impl.h b/src/tint/reader/spirv/parser_impl.h
index 6e79ecc..1780ca3 100644
--- a/src/tint/reader/spirv/parser_impl.h
+++ b/src/tint/reader/spirv/parser_impl.h
@@ -640,22 +640,26 @@
     /// @returns the handle usage, or an empty usage object.
     Usage GetHandleUsage(uint32_t id) const;
 
-    /// Returns the SPIR-V type for the sampler or image type for the given
-    /// variable in UniformConstant address space, or function parameter pointing
-    /// into the UniformConstant address space .  Returns null and emits an
-    /// error on failure.
-    /// @param var the OpVariable instruction or OpFunctionParameter
-    /// @returns the Tint AST type for the sampler or texture, or null on error
-    const spvtools::opt::Instruction* GetSpirvTypeForHandleMemoryObjectDeclaration(
-        const spvtools::opt::Instruction& var);
+    /// Returns the SPIR-V OpTypeImage or OpTypeSampler for the given:
+    ///   image object,
+    ///   sampler object,
+    ///   memory object declaration image or sampler (i.e. a variable or
+    ///      function parameter with type being a pointer to UniformConstant)
+    /// Returns null and emits an error on failure.
+    /// @param obj the given image, sampler, or memory object declaration for an
+    /// image or sampler
+    /// @returns the SPIR-V instruction declaring the corresponding OpTypeImage
+    /// or OpTypeSampler
+    const spvtools::opt::Instruction* GetSpirvTypeForHandleOrHandleMemoryObjectDeclaration(
+        const spvtools::opt::Instruction& obj);
 
-    /// Returns the AST type for the pointer-to-sampler or pointer-to-texture type
-    /// for the given variable in UniformConstant address space.  Returns null and
-    /// emits an error on failure.
-    /// @param var the OpVariable instruction
+    /// Returns the AST type for the texture or sampler type for the given
+    /// SPIR-V image, sampler, or memory object declaration for an image or
+    /// sampler. Returns null and emits an error on failure.
+    /// @param obj the OpVariable instruction
     /// @returns the Tint AST type for the poiner-to-{sampler|texture} or null on
     /// error
-    const Pointer* GetTypeForHandleVar(const spvtools::opt::Instruction& var);
+    const Type* GetHandleTypeForSpirvHandle(const spvtools::opt::Instruction& obj);
 
     /// Returns the AST variable for the SPIR-V ID of a module-scope variable,
     /// or null if there isn't one.
@@ -878,8 +882,9 @@
     // Maps a memory-object-declaration instruction to any sampler or texture
     // usages implied by usages of the memory-object-declaration.
     std::unordered_map<const spvtools::opt::Instruction*, Usage> handle_usage_;
-    // The inferred pointer type for the given handle variable.
-    std::unordered_map<const spvtools::opt::Instruction*, const Pointer*> handle_type_;
+    // The inferred WGSL handle type for the given SPIR-V image, sampler, or
+    // memory object declaration for an image or sampler.
+    std::unordered_map<const spvtools::opt::Instruction*, const Type*> handle_type_;
 
     /// Maps the SPIR-V ID of a module-scope variable to its AST variable.
     utils::Hashmap<uint32_t, ast::Var*, 16> module_variable_;
diff --git a/src/tint/reader/spirv/parser_impl_module_var_test.cc b/src/tint/reader/spirv/parser_impl_module_var_test.cc
index 44fc4b3..be931a6 100644
--- a/src/tint/reader/spirv/parser_impl_module_var_test.cc
+++ b/src/tint/reader/spirv/parser_impl_module_var_test.cc
@@ -3106,6 +3106,14 @@
 // Returns the start of a shader for testing LocalInvocationIndex,
 // parameterized by store type of %int or %uint
 std::string ComputeBuiltinInputPreamble(std::string builtin, std::string store_type) {
+    std::string ptr_component_type;
+    if (store_type == "%v3int") {
+        ptr_component_type = " %ptr_comp_ty = OpTypePointer Input %int\n";
+    }
+    if (store_type == "%v3uint") {
+        ptr_component_type = " %ptr_comp_ty = OpTypePointer Input %uint\n";
+    }
+
     return R"(
     OpCapability Shader
     OpMemoryModel Logical Simple
@@ -3118,10 +3126,11 @@
     %float = OpTypeFloat 32
     %uint = OpTypeInt 32 0
     %int = OpTypeInt 32 1
+    %int_1 = OpConstant %int 1
     %v3uint = OpTypeVector %uint 3
     %v3int = OpTypeVector %int 3
     %ptr_ty = OpTypePointer Input )" +
-           store_type + R"(
+           store_type + ptr_component_type + R"(
     %1 = OpVariable %ptr_ty Input
 )";
 }
@@ -3331,14 +3340,84 @@
                              {"LocalInvocationId", "%v3int", "local_invocation_id"},
                              {"GlobalInvocationId", "%v3uint", "global_invocation_id"},
                              {"GlobalInvocationId", "%v3int", "global_invocation_id"},
+                             {"NumWorkgroups", "%v3uint", "num_workgroups"},
+                             {"NumWorkgroups", "%v3int", "num_workgroups"},
                              {"WorkgroupId", "%v3uint", "workgroup_id"},
                              {"WorkgroupId", "%v3int", "workgroup_id"}}));
 
-// TODO(dneto): crbug.com/tint/752
-// NumWorkgroups support is blocked by crbug.com/tint/752
-// When the AST supports NumWorkgroups, add these cases:
-//        {"NumWorkgroups", "%uint", "num_workgroups"}
-//        {"NumWorkgroups", "%int", "num_workgroups"}
+// For compute shader builtins that are vectors, test loading one component.
+struct ComputeBuiltinInputVectorCase {
+    std::string spirv_builtin;
+    std::string spirv_store_type;
+    std::string spirv_component_store_type;
+    std::string wgsl_builtin;
+};
+inline std::ostream& operator<<(std::ostream& o, ComputeBuiltinInputVectorCase c) {
+    return o << "ComputeBuiltinInputVectorCase(" << c.spirv_builtin << " " << c.spirv_store_type
+             << " " << c.spirv_component_store_type << " " << c.wgsl_builtin << ")";
+}
+
+using SpvModuleScopeVarParserTest_ComputeBuiltinVector =
+    SpvParserTestBase<::testing::TestWithParam<ComputeBuiltinInputVectorCase>>;
+
+TEST_P(SpvModuleScopeVarParserTest_ComputeBuiltinVector, Load_Component_Direct) {
+    const auto wgsl_type = WgslType(GetParam().spirv_store_type);
+    const auto wgsl_component_type = WgslType(GetParam().spirv_component_store_type);
+    const auto wgsl_builtin = GetParam().wgsl_builtin;
+    const auto unsigned_wgsl_type = UnsignedWgslType(wgsl_type);
+    const auto signed_wgsl_type = SignedWgslType(wgsl_type);
+    const std::string assembly =
+        ComputeBuiltinInputPreamble(GetParam().spirv_builtin, GetParam().spirv_store_type) +
+        R"(
+    %main = OpFunction %void None %voidfn
+    %entry = OpLabel
+    %3 = OpAccessChain %ptr_comp_ty %1 %int_1
+    %2 = OpLoad )" +
+        GetParam().spirv_component_store_type + R"( %3
+    OpReturn
+    OpFunctionEnd
+ )";
+    auto p = parser(test::Assemble(assembly));
+    ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
+    EXPECT_TRUE(p->error().empty());
+    const auto module_str = test::ToString(p->program());
+    std::string expected = R"(var<private> x_1 : ${wgsl_type};
+
+fn main_1() {
+  let x_2 : ${wgsl_component_type} = x_1.y;
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn main(@builtin(${wgsl_builtin}) x_1_param : ${unsigned_wgsl_type}) {
+  x_1 = ${assignment_value};
+  main_1();
+}
+)";
+
+    expected = utils::ReplaceAll(expected, "${wgsl_type}", wgsl_type);
+    expected = utils::ReplaceAll(expected, "${wgsl_component_type}", wgsl_component_type);
+    expected = utils::ReplaceAll(expected, "${unsigned_wgsl_type}", unsigned_wgsl_type);
+    expected = utils::ReplaceAll(expected, "${wgsl_builtin}", wgsl_builtin);
+    expected = utils::ReplaceAll(expected, "${assignment_value}",
+                                 (wgsl_type == unsigned_wgsl_type)
+                                     ? "x_1_param"
+                                     : "bitcast<" + signed_wgsl_type + ">(x_1_param)");
+
+    EXPECT_EQ(module_str, expected) << module_str;
+}
+
+INSTANTIATE_TEST_SUITE_P(Samples,
+                         SpvModuleScopeVarParserTest_ComputeBuiltinVector,
+                         ::testing::ValuesIn(std::vector<ComputeBuiltinInputVectorCase>{
+                             {"LocalInvocationId", "%v3uint", "%uint", "local_invocation_id"},
+                             {"LocalInvocationId", "%v3int", "%int", "local_invocation_id"},
+                             {"GlobalInvocationId", "%v3uint", "%uint", "global_invocation_id"},
+                             {"GlobalInvocationId", "%v3int", "%int", "global_invocation_id"},
+                             {"NumWorkgroups", "%v3uint", "%uint", "num_workgroups"},
+                             {"NumWorkgroups", "%v3int", "%int", "num_workgroups"},
+                             {"WorkgroupId", "%v3uint", "%uint", "workgroup_id"},
+                             {"WorkgroupId", "%v3int", "%int", "workgroup_id"}}));
 
 TEST_F(SpvModuleScopeVarParserTest, RegisterInputOutputVars) {
     const std::string assembly =
diff --git a/src/tint/reader/spirv/parser_impl_test.cc b/src/tint/reader/spirv/parser_impl_test.cc
index 42c0098..908d369 100644
--- a/src/tint/reader/spirv/parser_impl_test.cc
+++ b/src/tint/reader/spirv/parser_impl_test.cc
@@ -217,5 +217,35 @@
     EXPECT_TRUE(ParserImpl::IsValidIdentifier("x_"));           // has underscore
 }
 
+TEST_F(SpvParserTest, Impl_FailOnNonFiniteLiteral) {
+    auto spv = test::Assemble(R"(
+                       OpCapability Shader
+                       OpMemoryModel Logical GLSL450
+                       OpEntryPoint Fragment %main "main" %out_var_SV_TARGET
+                       OpExecutionMode %main OriginUpperLeft
+                       OpSource HLSL 600
+                       OpName %out_var_SV_TARGET "out.var.SV_TARGET"
+                       OpName %main "main"
+                       OpDecorate %out_var_SV_TARGET Location 0
+              %float = OpTypeFloat 32
+     %float_0x1p_128 = OpConstant %float -0x1p+128
+            %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+               %void = OpTypeVoid
+                  %9 = OpTypeFunction %void
+  %out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output
+               %main = OpFunction %void None %9
+                 %10 = OpLabel
+                 %12 = OpCompositeConstruct %v4float %float_0x1p_128 %float_0x1p_128 %float_0x1p_128 %float_0x1p_128
+                       OpStore %out_var_SV_TARGET %12
+                       OpReturn
+                       OpFunctionEnd
+
+)");
+    auto p = parser(spv);
+    EXPECT_FALSE(p->Parse());
+    EXPECT_THAT(p->error(), HasSubstr("value cannot be represented as 'f32': -inf"));
+}
+
 }  // namespace
 }  // namespace tint::reader::spirv
diff --git a/src/tint/reader/wgsl/parser_impl.cc b/src/tint/reader/wgsl/parser_impl.cc
index 398c123..6314133 100644
--- a/src/tint/reader/wgsl/parser_impl.cc
+++ b/src/tint/reader/wgsl/parser_impl.cc
@@ -25,7 +25,6 @@
 #include "src/tint/ast/continue_statement.h"
 #include "src/tint/ast/discard_statement.h"
 #include "src/tint/ast/external_texture.h"
-#include "src/tint/ast/fallthrough_statement.h"
 #include "src/tint/ast/id_attribute.h"
 #include "src/tint/ast/if_statement.h"
 #include "src/tint/ast/increment_decrement_statement.h"
@@ -2189,24 +2188,17 @@
 // case_body
 //   :
 //   | statement case_body
-//   | FALLTHROUGH SEMICOLON
 Maybe<const ast::BlockStatement*> ParserImpl::case_body() {
     StatementList stmts;
     while (continue_parsing()) {
         Source source;
         if (match(Token::Type::kFallthrough, &source)) {
-            if (!expect("fallthrough statement", Token::Type::kSemicolon)) {
-                return Failure::kErrored;
-            }
-
-            deprecated(source,
-                       "fallthrough is set to be removed from WGSL. "
-                       "Case can accept multiple selectors if the existing case bodies are empty. "
-                       "(e.g. `case 1, 2, 3:`) "
-                       "`default` is a valid case selector value. (e.g. `case 1, default:`)");
-
-            stmts.Push(create<ast::FallthroughStatement>(source));
-            break;
+            return add_error(
+                source,
+                "fallthrough is not premitted in WGSL. "
+                "Case can accept multiple selectors if the existing case bodies are empty. "
+                "(e.g. `case 1, 2, 3:`) "
+                "`default` is a valid case selector value. (e.g. `case 1, default:`)");
         }
 
         auto stmt = statement();
diff --git a/src/tint/reader/wgsl/parser_impl_case_body_test.cc b/src/tint/reader/wgsl/parser_impl_case_body_test.cc
index c162be3..4d60e23 100644
--- a/src/tint/reader/wgsl/parser_impl_case_body_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_case_body_test.cc
@@ -12,7 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "src/tint/ast/fallthrough_statement.h"
 #include "src/tint/reader/wgsl/parser_impl_test_helper.h"
 
 namespace tint::reader::wgsl {
@@ -50,25 +49,5 @@
     EXPECT_EQ(e.value, nullptr);
 }
 
-TEST_F(ParserImplTest, CaseBody_Fallthrough) {
-    auto p = parser("fallthrough;");
-    auto e = p->case_body();
-    ASSERT_FALSE(p->has_error()) << p->error();
-    EXPECT_FALSE(e.errored);
-    EXPECT_TRUE(e.matched);
-    ASSERT_EQ(e->statements.Length(), 1u);
-    EXPECT_TRUE(e->statements[0]->Is<ast::FallthroughStatement>());
-}
-
-TEST_F(ParserImplTest, CaseBody_Fallthrough_MissingSemicolon) {
-    auto p = parser("fallthrough");
-    auto e = p->case_body();
-    EXPECT_TRUE(p->has_error());
-    EXPECT_TRUE(e.errored);
-    EXPECT_FALSE(e.matched);
-    EXPECT_EQ(e.value, nullptr);
-    EXPECT_EQ(p->error(), "1:12: expected ';' for fallthrough statement");
-}
-
 }  // namespace
 }  // namespace tint::reader::wgsl
diff --git a/src/tint/reader/wgsl/parser_impl_error_msg_test.cc b/src/tint/reader/wgsl/parser_impl_error_msg_test.cc
index 6c4f14a..822be9b 100644
--- a/src/tint/reader/wgsl/parser_impl_error_msg_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_error_msg_test.cc
@@ -1230,14 +1230,6 @@
 )");
 }
 
-TEST_F(ParserImplErrorTest, SwitchStmtCaseFallthroughMissingSemicolon) {
-    EXPECT("fn f() { switch(1) { case 1: { fallthrough } case 2: {} } }",
-           R"(test.wgsl:1:44 error: expected ';' for fallthrough statement
-fn f() { switch(1) { case 1: { fallthrough } case 2: {} } }
-                                           ^
-)");
-}
-
 TEST_F(ParserImplErrorTest, VarStmtMissingSemicolon) {
     EXPECT("fn f() { var a : u32 }",
            R"(test.wgsl:1:22 error: expected ';' for variable declaration
diff --git a/src/tint/resolver/alias_analysis_test.cc b/src/tint/resolver/alias_analysis_test.cc
new file mode 100644
index 0000000..38710b6
--- /dev/null
+++ b/src/tint/resolver/alias_analysis_test.cc
@@ -0,0 +1,897 @@
+// 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/resolver/resolver.h"
+#include "src/tint/resolver/resolver_test_helper.h"
+
+#include "gmock/gmock.h"
+
+using namespace tint::number_suffixes;  // NOLINT
+
+namespace tint::resolver {
+namespace {
+
+struct ResolverAliasAnalysisTest : public resolver::TestHelper, public testing::Test {};
+
+// Base test harness for tests that pass two pointers to a function.
+//
+// fn target(p1 : ptr<function, i32>, p2 : ptr<function, i32>) {
+//   <test statements>
+// }
+// fn caller() {
+//   var v1 : i32;
+//   var v2 : i32;
+//   target(&v1, aliased ? &v1 : &v2);
+// }
+struct TwoPointerConfig {
+    ast::AddressSpace address_space;  // The address space for the pointers.
+    bool aliased;                     // Whether the pointers alias or not.
+};
+class TwoPointers : public ResolverTestWithParam<TwoPointerConfig> {
+  protected:
+    void SetUp() override {
+        utils::Vector<const ast::Statement*, 4> body;
+        if (GetParam().address_space == ast::AddressSpace::kFunction) {
+            body.Push(Decl(Var("v1", ty.i32())));
+            body.Push(Decl(Var("v2", ty.i32())));
+        } else {
+            GlobalVar("v1", ast::AddressSpace::kPrivate, ty.i32());
+            GlobalVar("v2", ast::AddressSpace::kPrivate, ty.i32());
+        }
+        body.Push(CallStmt(Call("target", AddressOf(Source{{12, 34}}, "v1"),
+                                AddressOf(Source{{56, 78}}, GetParam().aliased ? "v1" : "v2"))));
+        Func("caller", utils::Empty, ty.void_(), body);
+    }
+
+    void Run(utils::Vector<const ast::Statement*, 4>&& body, const char* err = nullptr) {
+        auto addrspace = GetParam().address_space;
+        Func("target",
+             utils::Vector{
+                 Param("p1", ty.pointer<i32>(addrspace)),
+                 Param("p2", ty.pointer<i32>(addrspace)),
+             },
+             ty.void_(), std::move(body));
+        if (GetParam().aliased && err) {
+            EXPECT_TRUE(r()->Resolve());
+            EXPECT_EQ(r()->error(), err);
+        } else {
+            EXPECT_TRUE(r()->Resolve()) << r()->error();
+        }
+    }
+};
+
+TEST_P(TwoPointers, ReadRead) {
+    // _ = *p1;
+    // _ = *p2;
+    Run({
+        Assign(Phony(), Deref("p1")),
+        Assign(Phony(), Deref("p2")),
+    });
+}
+
+TEST_P(TwoPointers, ReadWrite) {
+    // _ = *p1;
+    // *p2 = 42;
+    Run(
+        {
+            Assign(Phony(), Deref("p1")),
+            Assign(Deref("p2"), 42_a),
+        },
+        R"(56:78 warning: invalid aliased pointer argument
+12:34 note: aliases with another argument passed here)");
+}
+
+TEST_P(TwoPointers, WriteRead) {
+    // *p1 = 42;
+    // _ = *p2;
+    Run(
+        {
+            Assign(Deref("p1"), 42_a),
+            Assign(Phony(), Deref("p2")),
+        },
+        R"(56:78 warning: invalid aliased pointer argument
+12:34 note: aliases with another argument passed here)");
+}
+
+TEST_P(TwoPointers, WriteWrite) {
+    // *p1 = 42;
+    // *p2 = 42;
+    Run(
+        {
+            Assign(Deref("p1"), 42_a),
+            Assign(Deref("p2"), 42_a),
+        },
+        R"(56:78 warning: invalid aliased pointer argument
+12:34 note: aliases with another argument passed here)");
+}
+
+TEST_P(TwoPointers, ReadWriteThroughChain) {
+    // fn f2(p1 : ptr<function, i32>, p2 : ptr<function, i32>) {
+    //   _ = *p1;
+    //   *p2 = 42;
+    // }
+    // fn f1(p1 : ptr<function, i32>, p2 : ptr<function, i32>) {
+    //   f2(p1, p2);
+    // }
+    //
+    // f1(p1, p2);
+    Func("f2",
+         utils::Vector{
+             Param("p1", ty.pointer<i32>(GetParam().address_space)),
+             Param("p2", ty.pointer<i32>(GetParam().address_space)),
+         },
+         ty.void_(),
+         utils::Vector{
+             Assign(Phony(), Deref("p1")),
+             Assign(Deref("p2"), 42_a),
+         });
+    Func("f1",
+         utils::Vector{
+             Param("p1", ty.pointer<i32>(GetParam().address_space)),
+             Param("p2", ty.pointer<i32>(GetParam().address_space)),
+         },
+         ty.void_(),
+         utils::Vector{
+             CallStmt(Call("f2", "p1", "p2")),
+         });
+    Run(
+        {
+            CallStmt(Call("f1", "p1", "p2")),
+        },
+        R"(56:78 warning: invalid aliased pointer argument
+12:34 note: aliases with another argument passed here)");
+}
+
+TEST_P(TwoPointers, ReadWriteAcrossDifferentFunctions) {
+    // fn f1(p1 : ptr<function, i32>) {
+    //   _ = *p1;
+    // }
+    // fn f2(p2 : ptr<function, i32>) {
+    //   *p2 = 42;
+    // }
+    //
+    // f1(p1);
+    // f2(p2);
+    Func("f1",
+         utils::Vector<const ast::Parameter*, 4>{
+             Param("p1", ty.pointer<i32>(GetParam().address_space)),
+         },
+         ty.void_(),
+         utils::Vector{
+             Assign(Phony(), Deref("p1")),
+         });
+    Func("f2",
+         utils::Vector<const ast::Parameter*, 4>{
+             Param("p2", ty.pointer<i32>(GetParam().address_space)),
+         },
+         ty.void_(),
+         utils::Vector{
+             Assign(Deref("p2"), 42_a),
+         });
+    Run(
+        {
+            CallStmt(Call("f1", "p1")),
+            CallStmt(Call("f2", "p2")),
+        },
+        R"(56:78 warning: invalid aliased pointer argument
+12:34 note: aliases with another argument passed here)");
+}
+
+INSTANTIATE_TEST_SUITE_P(ResolverAliasAnalysisTest,
+                         TwoPointers,
+                         ::testing::Values(TwoPointerConfig{ast::AddressSpace::kFunction, false},
+                                           TwoPointerConfig{ast::AddressSpace::kFunction, true},
+                                           TwoPointerConfig{ast::AddressSpace::kPrivate, false},
+                                           TwoPointerConfig{ast::AddressSpace::kPrivate, true}),
+                         [](const ::testing::TestParamInfo<TwoPointers::ParamType>& p) {
+                             std::stringstream ss;
+                             ss << (p.param.aliased ? "Aliased" : "Unaliased") << "_"
+                                << p.param.address_space;
+                             return ss.str();
+                         });
+
+// Base test harness for tests that pass a pointer to a function that references a module-scope var.
+//
+// var<private> global_1 : i32;
+// var<private> global_2 : i32;
+// fn target(p1 : ptr<private, i32>) {
+//   <test statements>
+// }
+// fn caller() {
+//   target(aliased ? &global_1 : &global_2);
+// }
+class OnePointerOneModuleScope : public ResolverTestWithParam<bool> {
+  protected:
+    void SetUp() override {
+        GlobalVar("global_1", ast::AddressSpace::kPrivate, ty.i32());
+        GlobalVar("global_2", ast::AddressSpace::kPrivate, ty.i32());
+        Func("caller", utils::Empty, ty.void_(),
+             utils::Vector{
+                 CallStmt(Call("target",
+                               AddressOf(Source{{12, 34}}, GetParam() ? "global_1" : "global_2"))),
+             });
+    }
+
+    void Run(utils::Vector<const ast::Statement*, 4>&& body, const char* err = nullptr) {
+        Func("target",
+             utils::Vector<const ast::Parameter*, 4>{
+                 Param("p1", ty.pointer<i32>(ast::AddressSpace::kPrivate)),
+             },
+             ty.void_(), std::move(body));
+        if (GetParam() && err) {
+            EXPECT_TRUE(r()->Resolve());
+            EXPECT_EQ(r()->error(), err);
+        } else {
+            EXPECT_TRUE(r()->Resolve()) << r()->error();
+        }
+    }
+};
+
+TEST_P(OnePointerOneModuleScope, ReadRead) {
+    // _ = *p1;
+    // _ = global_1;
+    Run({
+        Assign(Phony(), Deref("p1")),
+        Assign(Phony(), "global_1"),
+    });
+}
+
+TEST_P(OnePointerOneModuleScope, ReadWrite) {
+    // _ = *p1;
+    // global_1 = 42;
+    Run(
+        {
+            Assign(Phony(), Deref("p1")),
+            Assign(Expr(Source{{56, 78}}, "global_1"), 42_a),
+        },
+        R"(12:34 warning: invalid aliased pointer argument
+56:78 note: aliases with module-scope variable write in 'target')");
+}
+
+TEST_P(OnePointerOneModuleScope, WriteRead) {
+    // *p1 = 42;
+    // _ = global_1;
+    Run(
+        {
+            Assign(Deref("p1"), 42_a),
+            Assign(Phony(), Expr(Source{{56, 78}}, "global_1")),
+        },
+        R"(12:34 warning: invalid aliased pointer argument
+56:78 note: aliases with module-scope variable read in 'target')");
+}
+
+TEST_P(OnePointerOneModuleScope, WriteWrite) {
+    // *p1 = 42;
+    // global_1 = 42;
+    Run(
+        {
+            Assign(Deref("p1"), 42_a),
+            Assign(Expr(Source{{56, 78}}, "global_1"), 42_a),
+        },
+        R"(12:34 warning: invalid aliased pointer argument
+56:78 note: aliases with module-scope variable write in 'target')");
+}
+
+TEST_P(OnePointerOneModuleScope, ReadWriteThroughChain_GlobalViaArg) {
+    // fn f2(p1 : ptr<private, i32>) {
+    //   *p1 = 42;
+    // }
+    // fn f1(p1 : ptr<private, i32>) {
+    //   _ = *p1;
+    //   f2(&global_1);
+    // }
+    //
+    // f1(p1);
+    Func("f2",
+         utils::Vector<const ast::Parameter*, 4>{
+             Param("p1", ty.pointer<i32>(ast::AddressSpace::kPrivate)),
+         },
+         ty.void_(),
+         utils::Vector{
+             Assign(Deref("p1"), 42_a),
+         });
+    Func("f1",
+         utils::Vector<const ast::Parameter*, 4>{
+             Param("p1", ty.pointer<i32>(ast::AddressSpace::kPrivate)),
+         },
+         ty.void_(),
+         utils::Vector{
+             Assign(Phony(), Deref("p1")),
+             CallStmt(Call("f2", AddressOf(Source{{56, 78}}, "global_1"))),
+         });
+    Run(
+        {
+            CallStmt(Call("f1", "p1")),
+        },
+        R"(12:34 warning: invalid aliased pointer argument
+56:78 note: aliases with module-scope variable write in 'f1')");
+}
+
+TEST_P(OnePointerOneModuleScope, ReadWriteThroughChain_Both) {
+    // fn f2(p1 : ptr<private, i32>) {
+    //   _ = *p1;
+    //   global_1 = 42;
+    // }
+    // fn f1(p1 : ptr<private, i32>) {
+    //   f2(p1);
+    // }
+    //
+    // f1(p1);
+    Func("f2",
+         utils::Vector<const ast::Parameter*, 4>{
+             Param("p1", ty.pointer<i32>(ast::AddressSpace::kPrivate)),
+         },
+         ty.void_(),
+         utils::Vector{
+             Assign(Phony(), Deref("p1")),
+             Assign(Expr(Source{{56, 78}}, "global_1"), 42_a),
+         });
+    Func("f1",
+         utils::Vector<const ast::Parameter*, 4>{
+             Param("p1", ty.pointer<i32>(ast::AddressSpace::kPrivate)),
+         },
+         ty.void_(),
+         utils::Vector{
+             CallStmt(Call("f2", "p1")),
+         });
+    Run(
+        {
+            CallStmt(Call("f1", "p1")),
+        },
+        R"(12:34 warning: invalid aliased pointer argument
+56:78 note: aliases with module-scope variable write in 'f2')");
+}
+
+TEST_P(OnePointerOneModuleScope, WriteReadThroughChain_GlobalViaArg) {
+    // fn f2(p1 : ptr<private, i32>) {
+    //   _ = *p1;
+    // }
+    // fn f1(p1 : ptr<private, i32>) {
+    //   *p1 = 42;
+    //   f2(&global_1);
+    // }
+    //
+    // f1(p1);
+    Func("f2",
+         utils::Vector<const ast::Parameter*, 4>{
+             Param("p1", ty.pointer<i32>(ast::AddressSpace::kPrivate)),
+         },
+         ty.void_(),
+         utils::Vector{
+             Assign(Phony(), Deref("p1")),
+         });
+    Func("f1",
+         utils::Vector<const ast::Parameter*, 4>{
+             Param("p1", ty.pointer<i32>(ast::AddressSpace::kPrivate)),
+         },
+         ty.void_(),
+         utils::Vector{
+             Assign(Deref("p1"), 42_a),
+             CallStmt(Call("f2", AddressOf(Source{{56, 78}}, "global_1"))),
+         });
+    Run(
+        {
+            CallStmt(Call("f1", "p1")),
+        },
+        R"(12:34 warning: invalid aliased pointer argument
+56:78 note: aliases with module-scope variable read in 'f1')");
+}
+
+TEST_P(OnePointerOneModuleScope, WriteReadThroughChain_Both) {
+    // fn f2(p1 : ptr<private, i32>) {
+    //   *p1 = 42;
+    //   _ = global_1;
+    // }
+    // fn f1(p1 : ptr<private, i32>) {
+    //   f2(p1);
+    // }
+    //
+    // f1(p1);
+    Func("f2",
+         utils::Vector{
+             Param("p1", ty.pointer<i32>(ast::AddressSpace::kPrivate)),
+         },
+         ty.void_(),
+         utils::Vector{
+             Assign(Deref("p1"), 42_a),
+             Assign(Phony(), Expr(Source{{56, 78}}, "global_1")),
+         });
+    Func("f1",
+         utils::Vector{
+             Param("p1", ty.pointer<i32>(ast::AddressSpace::kPrivate)),
+         },
+         ty.void_(),
+         utils::Vector{
+             CallStmt(Call("f2", "p1")),
+         });
+    Run(
+        {
+            CallStmt(Call("f1", "p1")),
+        },
+        R"(12:34 warning: invalid aliased pointer argument
+56:78 note: aliases with module-scope variable read in 'f2')");
+}
+
+TEST_P(OnePointerOneModuleScope, ReadWriteAcrossDifferentFunctions) {
+    // fn f1(p1 : ptr<private, i32>) {
+    //   _ = *p1;
+    // }
+    // fn f2() {
+    //   global_1 = 42;
+    // }
+    //
+    // f1(p1);
+    // f2();
+    Func("f1",
+         utils::Vector{
+             Param("p1", ty.pointer<i32>(ast::AddressSpace::kPrivate)),
+         },
+         ty.void_(),
+         utils::Vector{
+             Assign(Phony(), Deref("p1")),
+         });
+    Func("f2", utils::Empty, ty.void_(),
+         utils::Vector{
+             Assign(Expr(Source{{56, 78}}, "global_1"), 42_a),
+         });
+    Run(
+        {
+            CallStmt(Call("f1", "p1")),
+            CallStmt(Call("f2")),
+        },
+        R"(12:34 warning: invalid aliased pointer argument
+56:78 note: aliases with module-scope variable write in 'f2')");
+}
+
+INSTANTIATE_TEST_SUITE_P(ResolverAliasAnalysisTest,
+                         OnePointerOneModuleScope,
+                         ::testing::Values(false, true),
+                         [](const ::testing::TestParamInfo<bool>& p) {
+                             return p.param ? "Aliased" : "Unaliased";
+                         });
+
+// Base test harness for tests that use a potentially aliased pointer in a variety of expressions.
+//
+// fn target(p1 : ptr<function, i32>, p2 : ptr<function, i32>) {
+//   *p1 = 42;
+//   <test statements>
+// }
+// fn caller() {
+//   var v1 : i32;
+//   var v2 : i32;
+//   target(&v1, aliased ? &v1 : &v2);
+// }
+class Use : public ResolverTestWithParam<bool> {
+  protected:
+    void SetUp() override {
+        Func("caller", utils::Empty, ty.void_(),
+             utils::Vector{
+                 Decl(Var("v1", ty.i32())),
+                 Decl(Var("v2", ty.i32())),
+                 CallStmt(Call("target", AddressOf(Source{{12, 34}}, "v1"),
+                               AddressOf(Source{{56, 78}}, GetParam() ? "v1" : "v2"))),
+             });
+    }
+
+    void Run(const ast::Statement* stmt, const char* err = nullptr) {
+        Func("target",
+             utils::Vector{
+                 Param("p1", ty.pointer<i32>(ast::AddressSpace::kFunction)),
+                 Param("p2", ty.pointer<i32>(ast::AddressSpace::kFunction)),
+             },
+             ty.void_(),
+             utils::Vector{
+                 Assign(Deref("p1"), 42_a),
+                 stmt,
+             });
+        if (GetParam() && err) {
+            EXPECT_TRUE(r()->Resolve());
+            EXPECT_EQ(r()->error(), err);
+        } else {
+            EXPECT_TRUE(r()->Resolve()) << r()->error();
+        }
+    }
+};
+
+TEST_P(Use, NoAccess) {
+    // Expect no errors even when aliasing occurs.
+    Run(Assign(Phony(), 42_a));
+}
+
+TEST_P(Use, Write_Increment) {
+    // (*p2)++;
+    Run(Increment(Deref("p2")), R"(56:78 warning: invalid aliased pointer argument
+12:34 note: aliases with another argument passed here)");
+}
+
+TEST_P(Use, Write_Decrement) {
+    // (*p2)--;
+    Run(Decrement(Deref("p2")), R"(56:78 warning: invalid aliased pointer argument
+12:34 note: aliases with another argument passed here)");
+}
+
+TEST_P(Use, Write_CompoundAssignment_LHS) {
+    // *p2 += 42;
+    Run(CompoundAssign(Deref("p2"), 42_a, ast::BinaryOp::kAdd),
+        R"(56:78 warning: invalid aliased pointer argument
+12:34 note: aliases with another argument passed here)");
+}
+
+TEST_P(Use, Read_CompoundAssignment_RHS) {
+    // var<private> global : i32;
+    // global += *p2;
+    GlobalVar("global", ast::AddressSpace::kPrivate, ty.i32());
+    Run(CompoundAssign("global", Deref("p2"), ast::BinaryOp::kAdd),
+        R"(56:78 warning: invalid aliased pointer argument
+12:34 note: aliases with another argument passed here)");
+}
+
+TEST_P(Use, Read_BinaryOp_LHS) {
+    // _ = (*p2) + 1;
+    Run(Assign(Phony(), Add(Deref("p2"), 1_a)), R"(56:78 warning: invalid aliased pointer argument
+12:34 note: aliases with another argument passed here)");
+}
+
+TEST_P(Use, Read_BinaryOp_RHS) {
+    // _ = 1 + (*p2);
+    Run(Assign(Phony(), Add(1_a, Deref("p2"))), R"(56:78 warning: invalid aliased pointer argument
+12:34 note: aliases with another argument passed here)");
+}
+
+TEST_P(Use, Read_UnaryMinus) {
+    // _ = -(*p2);
+    Run(Assign(Phony(), Negation(Deref("p2"))), R"(56:78 warning: invalid aliased pointer argument
+12:34 note: aliases with another argument passed here)");
+}
+
+TEST_P(Use, Read_FunctionCallArg) {
+    // abs(*p2);
+    Run(CallStmt(Call("abs", Deref("p2"))), R"(56:78 warning: invalid aliased pointer argument
+12:34 note: aliases with another argument passed here)");
+}
+
+TEST_P(Use, Read_Bitcast) {
+    // _ = bitcast<f32>(*p2);
+    Run(Assign(Phony(), Bitcast<f32>(Deref("p2"))),
+        R"(56:78 warning: invalid aliased pointer argument
+12:34 note: aliases with another argument passed here)");
+}
+
+TEST_P(Use, Read_Convert) {
+    // _ = f32(*p2);
+    Run(Assign(Phony(), Construct<f32>(Deref("p2"))),
+        R"(56:78 warning: invalid aliased pointer argument
+12:34 note: aliases with another argument passed here)");
+}
+
+TEST_P(Use, Read_IndexAccessor) {
+    // var<private> data : array<f32, 4>;
+    // _ = data[*p2];
+    GlobalVar("data", ast::AddressSpace::kPrivate, ty.array<f32, 4>());
+    Run(Assign(Phony(), IndexAccessor("data", Deref("p2"))),
+        R"(56:78 warning: invalid aliased pointer argument
+12:34 note: aliases with another argument passed here)");
+}
+
+TEST_P(Use, Read_LetInitializer) {
+    // let x = *p2;
+    Run(Decl(Let("x", Deref("p2"))), R"(56:78 warning: invalid aliased pointer argument
+12:34 note: aliases with another argument passed here)");
+}
+
+TEST_P(Use, Read_VarInitializer) {
+    // var x = *p2;
+    Run(Decl(Var("x", ast::AddressSpace::kFunction, Deref("p2"))),
+        R"(56:78 warning: invalid aliased pointer argument
+12:34 note: aliases with another argument passed here)");
+}
+
+TEST_P(Use, Read_ReturnValue) {
+    // fn foo(p : ptr<function, i32>) -> i32 { return *p; }
+    // foo(p2);
+    Func("foo",
+         utils::Vector{
+             Param("p", ty.pointer<i32>(ast::AddressSpace::kFunction)),
+         },
+         ty.i32(),
+         utils::Vector{
+             Return(Deref("p")),
+         });
+    Run(Assign(Phony(), Call("foo", "p2")), R"(56:78 warning: invalid aliased pointer argument
+12:34 note: aliases with another argument passed here)");
+}
+
+TEST_P(Use, Read_Switch) {
+    // Switch (*p2) { default {} }
+    Run(Switch(Deref("p2"), utils::Vector{DefaultCase(Block())}),
+        R"(56:78 warning: invalid aliased pointer argument
+12:34 note: aliases with another argument passed here)");
+}
+
+TEST_P(Use, NoAccess_AddressOf_Deref) {
+    // Should not invoke the load-rule, and therefore expect no errors even when aliasing occurs.
+    // let newp = &(*p2);
+    Run(Decl(Let("newp", AddressOf(Deref("p2")))));
+}
+
+INSTANTIATE_TEST_SUITE_P(ResolverAliasAnalysisTest,
+                         Use,
+                         ::testing::Values(false, true),
+                         [](const ::testing::TestParamInfo<bool>& p) {
+                             return p.param ? "Aliased" : "Unaliased";
+                         });
+
+// Base test harness for tests that use a potentially aliased pointer in a variety of expressions.
+// As above, but using the bool type to test expressions that invoke that load-rule for booleans.
+//
+// fn target(p1 : ptr<function, bool>, p2 : ptr<function, bool>) {
+//   *p1 = true;
+//   <test statements>
+// }
+// fn caller() {
+//   var v1 : bool;
+//   var v2 : bool;
+//   target(&v1, aliased ? &v1 : &v2);
+// }
+class UseBool : public ResolverTestWithParam<bool> {
+  protected:
+    void SetUp() override {
+        Func("caller", utils::Empty, ty.void_(),
+             utils::Vector{
+                 Decl(Var("v1", ty.bool_())),
+                 Decl(Var("v2", ty.bool_())),
+                 CallStmt(Call("target", AddressOf(Source{{12, 34}}, "v1"),
+                               AddressOf(Source{{56, 78}}, GetParam() ? "v1" : "v2"))),
+             });
+    }
+
+    void Run(const ast::Statement* stmt, const char* err = nullptr) {
+        Func("target",
+             utils::Vector{
+                 Param("p1", ty.pointer<bool>(ast::AddressSpace::kFunction)),
+                 Param("p2", ty.pointer<bool>(ast::AddressSpace::kFunction)),
+             },
+             ty.void_(),
+             utils::Vector{
+                 Assign(Deref("p1"), true),
+                 stmt,
+             });
+        if (GetParam() && err) {
+            EXPECT_TRUE(r()->Resolve());
+            EXPECT_EQ(r()->error(), err);
+        } else {
+            EXPECT_TRUE(r()->Resolve()) << r()->error();
+        }
+    }
+};
+
+TEST_P(UseBool, Read_IfCond) {
+    // if (*p2) {}
+    Run(If(Deref("p2"), Block()), R"(56:78 warning: invalid aliased pointer argument
+12:34 note: aliases with another argument passed here)");
+}
+
+TEST_P(UseBool, Read_WhileCond) {
+    // while (*p2) {}
+    Run(While(Deref("p2"), Block()), R"(56:78 warning: invalid aliased pointer argument
+12:34 note: aliases with another argument passed here)");
+}
+
+TEST_P(UseBool, Read_ForCond) {
+    // for (; *p2; ) {}
+    Run(For(nullptr, Deref("p2"), nullptr, Block()),
+        R"(56:78 warning: invalid aliased pointer argument
+12:34 note: aliases with another argument passed here)");
+}
+
+TEST_P(UseBool, Read_BreakIf) {
+    // loop { continuing { break if (*p2); } }
+    Run(Loop(Block(), Block(BreakIf(Deref("p2")))),
+        R"(56:78 warning: invalid aliased pointer argument
+12:34 note: aliases with another argument passed here)");
+}
+
+INSTANTIATE_TEST_SUITE_P(ResolverAliasAnalysisTest,
+                         UseBool,
+                         ::testing::Values(false, true),
+                         [](const ::testing::TestParamInfo<bool>& p) {
+                             return p.param ? "Aliased" : "Unaliased";
+                         });
+
+TEST_F(ResolverAliasAnalysisTest, NoAccess_MemberAccessor) {
+    // Should not invoke the load-rule, and therefore expect no errors even when aliasing occurs.
+    //
+    // struct S { a : i32 }
+    // fn f2(p1 : ptr<function, S>, p2 : ptr<function, S>) {
+    //   let newp = &((*p2).a);
+    //   (*p1).a = 42;
+    // }
+    // fn f1() {
+    //   var v : S;
+    //   f2(&v, &v);
+    // }
+    Structure("S", utils::Vector{Member("a", ty.i32())});
+    Func("f2",
+         utils::Vector{
+             Param("p1", ty.pointer(ty.type_name("S"), ast::AddressSpace::kFunction)),
+             Param("p2", ty.pointer(ty.type_name("S"), ast::AddressSpace::kFunction)),
+         },
+         ty.void_(),
+         utils::Vector{
+             Decl(Let("newp", AddressOf(MemberAccessor(Deref("p2"), "a")))),
+             Assign(MemberAccessor(Deref("p1"), "a"), 42_a),
+         });
+    Func("f1", utils::Empty, ty.void_(),
+         utils::Vector{
+             Decl(Var("v", ty.type_name("S"))),
+             CallStmt(Call("f2", AddressOf("v"), AddressOf("v"))),
+         });
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAliasAnalysisTest, Read_MemberAccessor) {
+    // struct S { a : i32 }
+    // fn f2(p1 : ptr<function, S>, p2 : ptr<function, S>) {
+    //   _ = (*p2).a;
+    //   *p1 = S();
+    // }
+    // fn f1() {
+    //   var v : S;
+    //   f2(&v, &v);
+    // }
+    Structure("S", utils::Vector{Member("a", ty.i32())});
+    Func("f2",
+         utils::Vector{
+             Param("p1", ty.pointer(ty.type_name("S"), ast::AddressSpace::kFunction)),
+             Param("p2", ty.pointer(ty.type_name("S"), ast::AddressSpace::kFunction)),
+         },
+         ty.void_(),
+         utils::Vector{
+             Assign(Phony(), MemberAccessor(Deref("p2"), "a")),
+             Assign(Deref("p1"), Construct(ty.type_name("S"))),
+         });
+    Func("f1", utils::Empty, ty.void_(),
+         utils::Vector{
+             Decl(Var("v", ty.type_name("S"))),
+             CallStmt(
+                 Call("f2", AddressOf(Source{{12, 34}}, "v"), AddressOf(Source{{56, 76}}, "v"))),
+         });
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+    EXPECT_EQ(r()->error(), R"(56:76 warning: invalid aliased pointer argument
+12:34 note: aliases with another argument passed here)");
+}
+
+TEST_F(ResolverAliasAnalysisTest, Write_MemberAccessor) {
+    // struct S { a : i32 }
+    // fn f2(p1 : ptr<function, S>, p2 : ptr<function, S>) {
+    //   _ = *p2;
+    //   (*p1).a = 42;
+    // }
+    // fn f1() {
+    //   var v : S;
+    //   f2(&v, &v);
+    // }
+    Structure("S", utils::Vector{Member("a", ty.i32())});
+    Func("f2",
+         utils::Vector{
+             Param("p1", ty.pointer(ty.type_name("S"), ast::AddressSpace::kFunction)),
+             Param("p2", ty.pointer(ty.type_name("S"), ast::AddressSpace::kFunction)),
+         },
+         ty.void_(),
+         utils::Vector{
+             Assign(Phony(), Deref("p2")),
+             Assign(MemberAccessor(Deref("p1"), "a"), 42_a),
+         });
+    Func("f1", utils::Empty, ty.void_(),
+         utils::Vector{
+             Decl(Var("v", ty.type_name("S"))),
+             CallStmt(
+                 Call("f2", AddressOf(Source{{12, 34}}, "v"), AddressOf(Source{{56, 76}}, "v"))),
+         });
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+    EXPECT_EQ(r()->error(), R"(56:76 warning: invalid aliased pointer argument
+12:34 note: aliases with another argument passed here)");
+}
+
+TEST_F(ResolverAliasAnalysisTest, SinglePointerReadWrite) {
+    // Test that we can both read and write from a single pointer parameter.
+    //
+    // fn f1(p : ptr<function, i32>) {
+    //   _ = *p;
+    //   *p = 42;
+    // }
+    // fn f2() {
+    //   var v : i32;
+    //   f1(&v);
+    // }
+    Func("f1",
+         utils::Vector{
+             Param("p", ty.pointer<i32>(ast::AddressSpace::kFunction)),
+         },
+         ty.void_(),
+         utils::Vector{
+             Decl(Var("v", ty.i32())),
+             Assign(Phony(), Deref("p")),
+             Assign(Deref("p"), 42_a),
+         });
+    Func("f2", utils::Empty, ty.void_(),
+         utils::Vector{
+             Decl(Var("v", ty.i32())),
+             CallStmt(Call("f1", AddressOf("v"))),
+         });
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAliasAnalysisTest, AliasingInsideFunction) {
+    // Test that we can use two aliased pointers inside the same function they are created in.
+    //
+    // fn f1() {
+    //   var v : i32;
+    //   let p1 = &v;
+    //   let p2 = &v;
+    //   *p1 = 42;
+    //   *p2 = 42;
+    // }
+    Func("f1", utils::Empty, ty.void_(),
+         utils::Vector{
+             Decl(Var("v", ty.i32())),
+             Decl(Let("p1", AddressOf("v"))),
+             Decl(Let("p2", AddressOf("v"))),
+             Assign(Deref("p1"), 42_a),
+             Assign(Deref("p2"), 42_a),
+         });
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAliasAnalysisTest, NonOverlappingCalls) {
+    // Test that we pass the same pointer to multiple non-overlapping function calls.
+    //
+    // fn f2(p : ptr<function, i32>) {
+    //   *p = 42;
+    // }
+    // fn f3(p : ptr<function, i32>) {
+    //   *p = 42;
+    // }
+    // fn f1() {
+    //   var v : i32;
+    //   f2(&v);
+    //   f3(&v);
+    // }
+    Func("f2",
+         utils::Vector{
+             Param("p", ty.pointer<i32>(ast::AddressSpace::kFunction)),
+         },
+         ty.void_(),
+         utils::Vector{
+             Assign(Deref("p"), 42_a),
+         });
+    Func("f3",
+         utils::Vector{
+             Param("p", ty.pointer<i32>(ast::AddressSpace::kFunction)),
+         },
+         ty.void_(),
+         utils::Vector{
+             Assign(Deref("p"), 42_a),
+         });
+    Func("f1", utils::Empty, ty.void_(),
+         utils::Vector{
+             Decl(Var("v", ty.i32())),
+             CallStmt(Call("f2", AddressOf("v"))),
+             CallStmt(Call("f3", AddressOf("v"))),
+         });
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
+}  // namespace
+}  // namespace tint::resolver
diff --git a/src/tint/resolver/builtin_test.cc b/src/tint/resolver/builtin_test.cc
index 3d8a4db..c6ca1f4 100644
--- a/src/tint/resolver/builtin_test.cc
+++ b/src/tint/resolver/builtin_test.cc
@@ -34,6 +34,7 @@
 #include "src/tint/sem/member_accessor_expression.h"
 #include "src/tint/sem/sampled_texture.h"
 #include "src/tint/sem/statement.h"
+#include "src/tint/sem/test_helper.h"
 #include "src/tint/sem/variable.h"
 
 using ::testing::ElementsAre;
@@ -276,7 +277,12 @@
 TEST_P(ResolverBuiltinTest_FloatBuiltin_IdenticalType, OneParam_Scalar_f32) {
     auto param = GetParam();
 
-    auto* call = Call(param.name, 0.5_f);
+    auto val = 0.5_f;
+    if (param.name == std::string("acosh")) {
+        val = 1.0_f;
+    }
+
+    auto* call = Call(param.name, val);
     WrapInFunction(call);
 
     if (param.args_number == 1u) {
@@ -297,7 +303,10 @@
 TEST_P(ResolverBuiltinTest_FloatBuiltin_IdenticalType, OneParam_Vector_f32) {
     auto param = GetParam();
 
-    auto* call = Call(param.name, vec3<f32>(0.5_f, 0.5_f, 0.8_f));
+    auto val = param.name == std::string("acosh") ? vec3<f32>(1.0_f, 2.0_f, 3.0_f)
+                                                  : vec3<f32>(0.5_f, 0.5_f, 0.8_f);
+
+    auto* call = Call(param.name, val);
     WrapInFunction(call);
 
     if (param.args_number == 1u) {
@@ -366,7 +375,7 @@
 TEST_P(ResolverBuiltinTest_FloatBuiltin_IdenticalType, ThreeParams_Scalar_f32) {
     auto param = GetParam();
 
-    auto* call = Call(param.name, 1_f, 1_f, 1_f);
+    auto* call = Call(param.name, 0_f, 1_f, 2_f);
     WrapInFunction(call);
 
     if (param.args_number == 3u) {
@@ -387,8 +396,8 @@
 TEST_P(ResolverBuiltinTest_FloatBuiltin_IdenticalType, ThreeParams_Vector_f32) {
     auto param = GetParam();
 
-    auto* call = Call(param.name, vec3<f32>(1_f, 1_f, 3_f), vec3<f32>(1_f, 1_f, 3_f),
-                      vec3<f32>(1_f, 1_f, 3_f));
+    auto* call = Call(param.name, vec3<f32>(0_f, 0_f, 0_f), vec3<f32>(1_f, 1_f, 1_f),
+                      vec3<f32>(2_f, 2_f, 2_f));
     WrapInFunction(call);
 
     if (param.args_number == 3u) {
@@ -462,7 +471,12 @@
 
     Enable(ast::Extension::kF16);
 
-    auto* call = Call(param.name, 0.5_h);
+    auto val = 0.5_h;
+    if (param.name == std::string("acosh")) {
+        val = 1.0_h;
+    }
+
+    auto* call = Call(param.name, val);
     WrapInFunction(call);
 
     if (param.args_number == 1u) {
@@ -485,7 +499,10 @@
 
     Enable(ast::Extension::kF16);
 
-    auto* call = Call(param.name, vec3<f16>(0.5_h, 0.5_h, 0.8_h));
+    auto val = param.name == std::string("acosh") ? vec3<f16>(1.0_h, 2.0_h, 3.0_h)
+                                                  : vec3<f16>(0.5_h, 0.5_h, 0.8_h);
+
+    auto* call = Call(param.name, val);
     WrapInFunction(call);
 
     if (param.args_number == 1u) {
@@ -560,7 +577,7 @@
 
     Enable(ast::Extension::kF16);
 
-    auto* call = Call(param.name, 1_h, 1_h, 1_h);
+    auto* call = Call(param.name, 0_h, 1_h, 2_h);
     WrapInFunction(call);
 
     if (param.args_number == 3u) {
@@ -583,8 +600,8 @@
 
     Enable(ast::Extension::kF16);
 
-    auto* call = Call(param.name, vec3<f16>(1_h, 1_h, 3_h), vec3<f16>(1_h, 1_h, 3_h),
-                      vec3<f16>(1_h, 1_h, 3_h));
+    auto* call = Call(param.name, vec3<f16>(0_h, 0_h, 0_h), vec3<f16>(1_h, 1_h, 1_h),
+                      vec3<f16>(2_h, 2_h, 2_h));
     WrapInFunction(call);
 
     if (param.args_number == 3u) {
@@ -911,12 +928,12 @@
     ASSERT_NE(ty, nullptr);
     ASSERT_EQ(ty->Members().size(), 2u);
 
-    auto* sig = ty->Members()[0];
-    EXPECT_TRUE(sig->Type()->Is<sem::F32>());
-    EXPECT_EQ(sig->Offset(), 0u);
-    EXPECT_EQ(sig->Size(), 4u);
-    EXPECT_EQ(sig->Align(), 4u);
-    EXPECT_EQ(sig->Name(), Sym("sig"));
+    auto* fract = ty->Members()[0];
+    EXPECT_TRUE(fract->Type()->Is<sem::F32>());
+    EXPECT_EQ(fract->Offset(), 0u);
+    EXPECT_EQ(fract->Size(), 4u);
+    EXPECT_EQ(fract->Align(), 4u);
+    EXPECT_EQ(fract->Name(), Sym("fract"));
 
     auto* exp = ty->Members()[1];
     EXPECT_TRUE(exp->Type()->Is<sem::I32>());
@@ -942,12 +959,12 @@
     ASSERT_NE(ty, nullptr);
     ASSERT_EQ(ty->Members().size(), 2u);
 
-    auto* sig = ty->Members()[0];
-    EXPECT_TRUE(sig->Type()->Is<sem::F16>());
-    EXPECT_EQ(sig->Offset(), 0u);
-    EXPECT_EQ(sig->Size(), 2u);
-    EXPECT_EQ(sig->Align(), 2u);
-    EXPECT_EQ(sig->Name(), Sym("sig"));
+    auto* fract = ty->Members()[0];
+    EXPECT_TRUE(fract->Type()->Is<sem::F16>());
+    EXPECT_EQ(fract->Offset(), 0u);
+    EXPECT_EQ(fract->Size(), 2u);
+    EXPECT_EQ(fract->Align(), 2u);
+    EXPECT_EQ(fract->Name(), Sym("fract"));
 
     auto* exp = ty->Members()[1];
     EXPECT_TRUE(exp->Type()->Is<sem::I32>());
@@ -971,14 +988,14 @@
     ASSERT_NE(ty, nullptr);
     ASSERT_EQ(ty->Members().size(), 2u);
 
-    auto* sig = ty->Members()[0];
-    ASSERT_TRUE(sig->Type()->Is<sem::Vector>());
-    EXPECT_EQ(sig->Type()->As<sem::Vector>()->Width(), 3u);
-    EXPECT_TRUE(sig->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
-    EXPECT_EQ(sig->Offset(), 0u);
-    EXPECT_EQ(sig->Size(), 12u);
-    EXPECT_EQ(sig->Align(), 16u);
-    EXPECT_EQ(sig->Name(), Sym("sig"));
+    auto* fract = ty->Members()[0];
+    ASSERT_TRUE(fract->Type()->Is<sem::Vector>());
+    EXPECT_EQ(fract->Type()->As<sem::Vector>()->Width(), 3u);
+    EXPECT_TRUE(fract->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
+    EXPECT_EQ(fract->Offset(), 0u);
+    EXPECT_EQ(fract->Size(), 12u);
+    EXPECT_EQ(fract->Align(), 16u);
+    EXPECT_EQ(fract->Name(), Sym("fract"));
 
     auto* exp = ty->Members()[1];
     ASSERT_TRUE(exp->Type()->Is<sem::Vector>());
@@ -1006,14 +1023,14 @@
     ASSERT_NE(ty, nullptr);
     ASSERT_EQ(ty->Members().size(), 2u);
 
-    auto* sig = ty->Members()[0];
-    ASSERT_TRUE(sig->Type()->Is<sem::Vector>());
-    EXPECT_EQ(sig->Type()->As<sem::Vector>()->Width(), 3u);
-    EXPECT_TRUE(sig->Type()->As<sem::Vector>()->type()->Is<sem::F16>());
-    EXPECT_EQ(sig->Offset(), 0u);
-    EXPECT_EQ(sig->Size(), 6u);
-    EXPECT_EQ(sig->Align(), 8u);
-    EXPECT_EQ(sig->Name(), Sym("sig"));
+    auto* fract = ty->Members()[0];
+    ASSERT_TRUE(fract->Type()->Is<sem::Vector>());
+    EXPECT_EQ(fract->Type()->As<sem::Vector>()->Width(), 3u);
+    EXPECT_TRUE(fract->Type()->As<sem::Vector>()->type()->Is<sem::F16>());
+    EXPECT_EQ(fract->Offset(), 0u);
+    EXPECT_EQ(fract->Size(), 6u);
+    EXPECT_EQ(fract->Align(), 8u);
+    EXPECT_EQ(fract->Name(), Sym("fract"));
 
     auto* exp = ty->Members()[1];
     ASSERT_TRUE(exp->Type()->Is<sem::Vector>());
@@ -1028,6 +1045,29 @@
     EXPECT_EQ(ty->SizeNoPadding(), 28u);
 }
 
+// TODO(crbug.com/tint/1757): Remove once deprecation period for `frexp().sig` is over
+TEST_F(ResolverBuiltinFloatTest, FrexpVector_sig) {
+    Enable(ast::Extension::kF16);
+
+    auto* call = Call("frexp", vec3<f16>());
+    auto* expr = MemberAccessor(call, "sig");
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    ASSERT_NE(TypeOf(call), nullptr);
+    auto* ty = TypeOf(call)->As<sem::Struct>();
+    ASSERT_NE(ty, nullptr);
+    ASSERT_EQ(ty->Members().size(), 2u);
+
+    auto* sig = ty->Members()[0];
+    EXPECT_TYPE(sig->Type(), TypeOf(expr));
+
+    auto* access = Sem().Get<sem::StructMemberAccess>(expr);
+    ASSERT_NE(access, nullptr);
+    EXPECT_EQ(access->Member(), sig);
+}
+
 TEST_F(ResolverBuiltinFloatTest, Frexp_Error_FirstParamInt) {
     GlobalVar("v", ty.i32(), ast::AddressSpace::kWorkgroup);
     auto* call = Call("frexp", 1_i, AddressOf("v"));
@@ -1144,8 +1184,8 @@
     EXPECT_EQ(r()->error(), R"(error: no matching call to length()
 
 2 candidate functions:
-  length(T) -> T  where: T is f32 or f16
-  length(vecN<T>) -> T  where: T is f32 or f16
+  length(T) -> T  where: T is abstract-float, f32 or f16
+  length(vecN<T>) -> T  where: T is abstract-float, f32 or f16
 )");
 }
 
@@ -1158,8 +1198,8 @@
     EXPECT_EQ(r()->error(), R"(error: no matching call to length(f32, f32)
 
 2 candidate functions:
-  length(T) -> T  where: T is f32 or f16
-  length(vecN<T>) -> T  where: T is f32 or f16
+  length(T) -> T  where: T is abstract-float, f32 or f16
+  length(vecN<T>) -> T  where: T is abstract-float, f32 or f16
 )");
 }
 
@@ -2022,7 +2062,7 @@
               R"(error: no matching call to dot(f32, f32)
 
 1 candidate function:
-  dot(vecN<T>, vecN<T>) -> T  where: T is f32, i32, u32 or f16
+  dot(vecN<T>, vecN<T>) -> T  where: T is abstract-float, abstract-int, f32, i32, u32 or f16
 )");
 }
 
diff --git a/src/tint/resolver/builtin_validation_test.cc b/src/tint/resolver/builtin_validation_test.cc
index b67a9ee..7340387 100644
--- a/src/tint/resolver/builtin_validation_test.cc
+++ b/src/tint/resolver/builtin_validation_test.cc
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <unordered_set>
+
 #include "src/tint/ast/builtin_texture_helper_test.h"
 #include "src/tint/resolver/resolver_test_helper.h"
 #include "src/tint/sem/type_initializer.h"
diff --git a/src/tint/resolver/builtins_validation_test.cc b/src/tint/resolver/builtins_validation_test.cc
index 7190ef1..675ff49 100644
--- a/src/tint/resolver/builtins_validation_test.cc
+++ b/src/tint/resolver/builtins_validation_test.cc
@@ -1099,7 +1099,7 @@
 
     utils::Vector<const ast::Expression*, 8> params;
     for (uint32_t i = 0; i < num_params; ++i) {
-        params.Push(Expr(1_f));
+        params.Push(Expr(f32(i)));
     }
     auto* builtin = Call(name, params);
     Func("func", utils::Empty, ty.void_(),
@@ -1120,7 +1120,7 @@
 
     utils::Vector<const ast::Expression*, 8> params;
     for (uint32_t i = 0; i < num_params; ++i) {
-        params.Push(vec2<f32>(1_f, 1_f));
+        params.Push(vec2<f32>(f32(i), f32(i)));
     }
     auto* builtin = Call(name, params);
     Func("func", utils::Empty, ty.void_(),
@@ -1141,7 +1141,7 @@
 
     utils::Vector<const ast::Expression*, 8> params;
     for (uint32_t i = 0; i < num_params; ++i) {
-        params.Push(vec3<f32>(1_f, 1_f, 1_f));
+        params.Push(vec3<f32>(f32(i), f32(i), f32(i)));
     }
     auto* builtin = Call(name, params);
     Func("func", utils::Empty, ty.void_(),
@@ -1162,7 +1162,7 @@
 
     utils::Vector<const ast::Expression*, 8> params;
     for (uint32_t i = 0; i < num_params; ++i) {
-        params.Push(vec4<f32>(1_f, 1_f, 1_f, 1_f));
+        params.Push(vec4<f32>(f32(i), f32(i), f32(i), f32(i)));
     }
     auto* builtin = Call(name, params);
     Func("func", utils::Empty, ty.void_(),
diff --git a/src/tint/resolver/call_validation_test.cc b/src/tint/resolver/call_validation_test.cc
index 6c64fad..82037af 100644
--- a/src/tint/resolver/call_validation_test.cc
+++ b/src/tint/resolver/call_validation_test.cc
@@ -114,25 +114,7 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverCallValidationTest, PointerArgument_ConstIdentExpr) {
-    // fn foo(p: ptr<function, i32>) {}
-    // fn main() {
-    //   let z: i32 = 1i;
-    //   foo(&z);
-    // }
-    auto* param = Param("p", ty.pointer<i32>(ast::AddressSpace::kFunction));
-    Func("foo", utils::Vector{param}, ty.void_(), utils::Empty);
-    Func("main", utils::Empty, ty.void_(),
-         utils::Vector{
-             Decl(Let("z", ty.i32(), Expr(1_i))),
-             CallStmt(Call("foo", AddressOf(Expr(Source{{12, 34}}, "z")))),
-         });
-
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: cannot take the address of expression");
-}
-
-TEST_F(ResolverCallValidationTest, PointerArgument_NotIdentExprVar) {
+TEST_F(ResolverCallValidationTest, PointerArgument_NotWholeVar) {
     // struct S { m: i32; };
     // fn foo(p: ptr<function, i32>) {}
     // fn main() {
@@ -152,30 +134,8 @@
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 error: expected an address-of expression of a variable "
-              "identifier expression or a function parameter");
-}
-
-TEST_F(ResolverCallValidationTest, PointerArgument_AddressOfMemberAccessor) {
-    // struct S { m: i32; };
-    // fn foo(p: ptr<function, i32>) {}
-    // fn main() {
-    //   let v: S = S();
-    //   foo(&v.m);
-    // }
-    auto* S = Structure("S", utils::Vector{
-                                 Member("m", ty.i32()),
-                             });
-    auto* param = Param("p", ty.pointer<i32>(ast::AddressSpace::kFunction));
-    Func("foo", utils::Vector{param}, ty.void_(), utils::Empty);
-    Func("main", utils::Empty, ty.void_(),
-         utils::Vector{
-             Decl(Let("v", ty.Of(S), Construct(ty.Of(S)))),
-             CallStmt(Call("foo", AddressOf(MemberAccessor(Source{{12, 34}}, "v", "m")))),
-         });
-
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: cannot take the address of expression");
+              "12:34 error: arguments of pointer type must not point to a subset of the "
+              "originating variable");
 }
 
 TEST_F(ResolverCallValidationTest, PointerArgument_FunctionParam) {
@@ -235,65 +195,169 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverCallValidationTest, LetPointer) {
-    // fn x(p : ptr<function, i32>) -> i32 {}
-    // @fragment
-    // fn main() {
-    //   var v: i32;
-    //   let p: ptr<function, i32> = &v;
-    //   var c: i32 = x(p);
+TEST_F(ResolverCallValidationTest, PointerArgument_FunctionParam_NotWholeVar) {
+    // fn foo(p: ptr<function, i32>) {}
+    // fn bar(p: ptr<function, array<i32, 4>>) {
+    //   foo(&(*p)[0]);
     // }
-    Func("x",
+    Func("foo",
          utils::Vector{
              Param("p", ty.pointer<i32>(ast::AddressSpace::kFunction)),
          },
          ty.void_(), utils::Empty);
-    auto* v = Var("v", ty.i32());
-    auto* p = Let("p", ty.pointer(ty.i32(), ast::AddressSpace::kFunction), AddressOf(v));
-    auto* c = Var("c", ty.i32(), Call("x", Expr(Source{{12, 34}}, p)));
+    Func("bar",
+         utils::Vector{
+             Param("p", ty.pointer(ty.array<i32, 4>(), ast::AddressSpace::kFunction)),
+         },
+         ty.void_(),
+         utils::Vector{
+             CallStmt(Call("foo", AddressOf(Source{{12, 34}}, IndexAccessor(Deref("p"), 0_a)))),
+         });
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(),
+              "12:34 error: arguments of pointer type must not point to a subset of the "
+              "originating variable");
+}
+
+TEST_F(ResolverCallValidationTest, LetPointer) {
+    // fn foo(p : ptr<function, i32>) {}
+    // @fragment
+    // fn main() {
+    //   var v: i32;
+    //   let p: ptr<function, i32> = &v;
+    //   foo(p);
+    // }
+    Func("foo",
+         utils::Vector{
+             Param("p", ty.pointer<i32>(ast::AddressSpace::kFunction)),
+         },
+         ty.void_(), utils::Empty);
     Func("main", utils::Empty, ty.void_(),
          utils::Vector{
-             Decl(v),
-             Decl(p),
-             Decl(c),
+             Decl(Var("v", ty.i32())),
+             Decl(Let("p", ty.pointer(ty.i32(), ast::AddressSpace::kFunction), AddressOf("v"))),
+             CallStmt(Call("foo", Expr(Source{{12, 34}}, "p"))),
          },
          utils::Vector{
              Stage(ast::PipelineStage::kFragment),
          });
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              "12:34 error: expected an address-of expression of a variable "
-              "identifier expression or a function parameter");
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
 TEST_F(ResolverCallValidationTest, LetPointerPrivate) {
-    // let p: ptr<private, i32> = &v;
-    // fn foo(p : ptr<private, i32>) -> i32 {}
-    // var v: i32;
+    // fn foo(p : ptr<private, i32>) {}
+    // var<private> v: i32;
     // @fragment
     // fn main() {
-    //   var c: i32 = foo(p);
+    //   let p: ptr<private, i32> = &v;
+    //   foo(p);
     // }
     Func("foo",
          utils::Vector{
              Param("p", ty.pointer<i32>(ast::AddressSpace::kPrivate)),
          },
          ty.void_(), utils::Empty);
-    auto* v = GlobalVar("v", ty.i32(), ast::AddressSpace::kPrivate);
-    auto* p = Let("p", ty.pointer(ty.i32(), ast::AddressSpace::kPrivate), AddressOf(v));
-    auto* c = Var("c", ty.i32(), Call("foo", Expr(Source{{12, 34}}, p)));
+    GlobalVar("v", ty.i32(), ast::AddressSpace::kPrivate);
     Func("main", utils::Empty, ty.void_(),
          utils::Vector{
-             Decl(p),
-             Decl(c),
+             Decl(Let("p", ty.pointer(ty.i32(), ast::AddressSpace::kPrivate), AddressOf("v"))),
+             CallStmt(Call("foo", Expr(Source{{12, 34}}, "p"))),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverCallValidationTest, LetPointer_NotWholeVar) {
+    // fn foo(p : ptr<function, i32>) {}
+    // @fragment
+    // fn main() {
+    //   var v: array<i32, 4>;
+    //   let p: ptr<function, i32> = &(v[0]);
+    //   x(p);
+    // }
+    Func("foo",
+         utils::Vector{
+             Param("p", ty.pointer<i32>(ast::AddressSpace::kFunction)),
+         },
+         ty.void_(), utils::Empty);
+    Func("main", utils::Empty, ty.void_(),
+         utils::Vector{
+             Decl(Var("v", ty.array<i32, 4>())),
+             Decl(Let("p", ty.pointer(ty.i32(), ast::AddressSpace::kFunction),
+                      AddressOf(IndexAccessor("v", 0_a)))),
+             CallStmt(Call("foo", Expr(Source{{12, 34}}, "p"))),
          },
          utils::Vector{
              Stage(ast::PipelineStage::kFragment),
          });
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 error: expected an address-of expression of a variable "
-              "identifier expression or a function parameter");
+              "12:34 error: arguments of pointer type must not point to a subset of the "
+              "originating variable");
+}
+
+TEST_F(ResolverCallValidationTest, ComplexPointerChain) {
+    // fn foo(p : ptr<function, array<i32, 4>>) {}
+    // @fragment
+    // fn main() {
+    //   var v: array<i32, 4>;
+    //   let p1 = &v;
+    //   let p2 = p1;
+    //   let p3 = &*p2;
+    //   foo(&*p);
+    // }
+    Func("foo",
+         utils::Vector{
+             Param("p", ty.pointer(ty.array<i32, 4>(), ast::AddressSpace::kFunction)),
+         },
+         ty.void_(), utils::Empty);
+    Func("main", utils::Empty, ty.void_(),
+         utils::Vector{
+             Decl(Var("v", ty.array<i32, 4>())),
+             Decl(Let("p1", AddressOf("v"))),
+             Decl(Let("p2", Expr("p1"))),
+             Decl(Let("p3", AddressOf(Deref("p2")))),
+             CallStmt(Call("foo", AddressOf(Source{{12, 34}}, Deref("p3")))),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverCallValidationTest, ComplexPointerChain_NotWholeVar) {
+    // fn foo(p : ptr<function, i32>) {}
+    // @fragment
+    // fn main() {
+    //   var v: array<i32, 4>;
+    //   let p1 = &v;
+    //   let p2 = p1;
+    //   let p3 = &(*p2)[0];
+    //   foo(&*p);
+    // }
+    Func("foo",
+         utils::Vector{
+             Param("p", ty.pointer<i32>(ast::AddressSpace::kFunction)),
+         },
+         ty.void_(), utils::Empty);
+    Func("main", utils::Empty, ty.void_(),
+         utils::Vector{
+             Decl(Var("v", ty.array<i32, 4>())),
+             Decl(Let("p1", AddressOf("v"))),
+             Decl(Let("p2", Expr("p1"))),
+             Decl(Let("p3", AddressOf(IndexAccessor(Deref("p2"), 0_a)))),
+             CallStmt(Call("foo", AddressOf(Source{{12, 34}}, Deref("p3")))),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(),
+              "12:34 error: arguments of pointer type must not point to a subset of the "
+              "originating variable");
 }
 
 TEST_F(ResolverCallValidationTest, CallVariable) {
diff --git a/src/tint/resolver/const_eval.cc b/src/tint/resolver/const_eval.cc
index 2c012a1..c5b51c0 100644
--- a/src/tint/resolver/const_eval.cc
+++ b/src/tint/resolver/const_eval.cc
@@ -20,7 +20,6 @@
 #include <optional>
 #include <string>
 #include <type_traits>
-#include <unordered_map>
 #include <utility>
 
 #include "src/tint/program_builder.h"
@@ -40,7 +39,6 @@
 #include "src/tint/utils/bitcast.h"
 #include "src/tint/utils/compiler_macros.h"
 #include "src/tint/utils/map.h"
-#include "src/tint/utils/scoped_assignment.h"
 #include "src/tint/utils/transform.h"
 
 using namespace tint::number_suffixes;  // NOLINT
@@ -262,7 +260,11 @@
     static_assert(!std::is_same_v<UnwrapNumber<T>, T> || std::is_same_v<T, bool>,
                   "T must be a Number or bool");
 
-    Element(const sem::Type* t, T v) : type(t), value(v) {}
+    Element(const sem::Type* t, T v) : type(t), value(v) {
+        if constexpr (IsFloatingPoint<T>) {
+            TINT_ASSERT(Resolver, std::isfinite(v.value));
+        }
+    }
     ~Element() override = default;
     const sem::Type* Type() const override { return type; }
     std::variant<std::monostate, AInt, AFloat> Value() const override {
@@ -311,12 +313,10 @@
             } else if constexpr (IsFloatingPoint<TO>) {
                 // [x -> floating-point] - number not exactly representable
                 // https://www.w3.org/TR/WGSL/#floating-point-conversion
-                switch (conv.Failure()) {
-                    case ConversionFailure::kExceedsNegativeLimit:
-                        return builder.create<Element<TO>>(target_ty, -TO::Inf());
-                    case ConversionFailure::kExceedsPositiveLimit:
-                        return builder.create<Element<TO>>(target_ty, TO::Inf());
-                }
+                builder.Diagnostics().add_error(
+                    tint::diag::System::Resolver,
+                    OverflowErrorMessage(value, builder.FriendlyName(target_ty)), source);
+                return utils::Failure;
             } else if constexpr (IsFloatingPoint<FROM>) {
                 // [floating-point -> integer] - number not exactly representable
                 // https://www.w3.org/TR/WGSL/#floating-point-conversion
@@ -438,7 +438,14 @@
 
 /// CreateElement constructs and returns an Element<T>.
 template <typename T>
-const ImplConstant* CreateElement(ProgramBuilder& builder, const sem::Type* t, T v) {
+ImplResult CreateElement(ProgramBuilder& builder, const Source& source, const sem::Type* t, T v) {
+    if constexpr (IsFloatingPoint<T>) {
+        if (!std::isfinite(v.value)) {
+            auto msg = OverflowErrorMessage(v, builder.FriendlyName(t));
+            builder.Diagnostics().add_error(diag::System::Resolver, msg, source);
+            return utils::Failure;
+        }
+    }
     return builder.create<Element<T>>(t, v);
 }
 
@@ -463,18 +470,18 @@
             return nullptr;
         },
         [&](const sem::Struct* s) -> const ImplConstant* {
-            std::unordered_map<const sem::Type*, const ImplConstant*> zero_by_type;
+            utils::Hashmap<const sem::Type*, const ImplConstant*, 8> zero_by_type;
             utils::Vector<const sem::Constant*, 4> zeros;
             zeros.Reserve(s->Members().size());
             for (auto* member : s->Members()) {
-                auto* zero = utils::GetOrCreate(zero_by_type, member->Type(),
-                                                [&] { return ZeroValue(builder, member->Type()); });
+                auto* zero = zero_by_type.GetOrCreate(
+                    member->Type(), [&] { return ZeroValue(builder, member->Type()); });
                 if (!zero) {
                     return nullptr;
                 }
                 zeros.Push(zero);
             }
-            if (zero_by_type.size() == 1) {
+            if (zero_by_type.Count() == 1) {
                 // All members were of the same type, so the zero value is the same for all members.
                 return builder.create<Splat>(type, zeros[0], s->Members().size());
             }
@@ -482,7 +489,9 @@
         },
         [&](Default) -> const ImplConstant* {
             return ZeroTypeDispatch(type, [&](auto zero) -> const ImplConstant* {
-                return CreateElement(builder, type, zero);
+                auto el = CreateElement(builder, Source{}, type, zero);
+                TINT_ASSERT(Resolver, el);
+                return el.Get();
             });
         });
 }
@@ -654,14 +663,13 @@
 ConstEval::ConstEval(ProgramBuilder& b) : builder(b) {}
 
 template <typename NumberT>
-utils::Result<NumberT> ConstEval::Add(NumberT a, NumberT b) {
+utils::Result<NumberT> ConstEval::Add(const Source& source, NumberT a, NumberT b) {
     NumberT result;
-    if constexpr (IsAbstract<NumberT>) {
-        // Check for over/underflow for abstract values
+    if constexpr (IsAbstract<NumberT> || IsFloatingPoint<NumberT>) {
         if (auto r = CheckedAdd(a, b)) {
             result = r->value;
         } else {
-            AddError(OverflowErrorMessage(a, "+", b), *current_source);
+            AddError(OverflowErrorMessage(a, "+", b), source);
             return utils::Failure;
         }
     } else {
@@ -681,14 +689,13 @@
 }
 
 template <typename NumberT>
-utils::Result<NumberT> ConstEval::Sub(NumberT a, NumberT b) {
+utils::Result<NumberT> ConstEval::Sub(const Source& source, NumberT a, NumberT b) {
     NumberT result;
-    if constexpr (IsAbstract<NumberT>) {
-        // Check for over/underflow for abstract values
+    if constexpr (IsAbstract<NumberT> || IsFloatingPoint<NumberT>) {
         if (auto r = CheckedSub(a, b)) {
             result = r->value;
         } else {
-            AddError(OverflowErrorMessage(a, "-", b), *current_source);
+            AddError(OverflowErrorMessage(a, "-", b), source);
             return utils::Failure;
         }
     } else {
@@ -708,15 +715,15 @@
 }
 
 template <typename NumberT>
-utils::Result<NumberT> ConstEval::Mul(NumberT a, NumberT b) {
+utils::Result<NumberT> ConstEval::Mul(const Source& source, NumberT a, NumberT b) {
     using T = UnwrapNumber<NumberT>;
     NumberT result;
-    if constexpr (IsAbstract<NumberT>) {
+    if constexpr (IsAbstract<NumberT> || IsFloatingPoint<NumberT>) {
         // Check for over/underflow for abstract values
         if (auto r = CheckedMul(a, b)) {
             result = r->value;
         } else {
-            AddError(OverflowErrorMessage(a, "*", b), *current_source);
+            AddError(OverflowErrorMessage(a, "*", b), source);
             return utils::Failure;
         }
     } else {
@@ -735,16 +742,55 @@
 }
 
 template <typename NumberT>
-utils::Result<NumberT> ConstEval::Dot2(NumberT a1, NumberT a2, NumberT b1, NumberT b2) {
-    auto r1 = Mul(a1, b1);
+utils::Result<NumberT> ConstEval::Div(const Source& source, NumberT a, NumberT b) {
+    NumberT result;
+    if constexpr (IsAbstract<NumberT> || IsFloatingPoint<NumberT>) {
+        // Check for over/underflow for abstract values
+        if (auto r = CheckedDiv(a, b)) {
+            result = r->value;
+        } else {
+            AddError(OverflowErrorMessage(a, "/", b), source);
+            return utils::Failure;
+        }
+    } else {
+        using T = UnwrapNumber<NumberT>;
+        auto divide_values = [](T lhs, T rhs) {
+            if constexpr (std::is_integral_v<T>) {
+                // For integers, lhs / 0 returns lhs
+                if (rhs == 0) {
+                    return lhs;
+                }
+
+                if constexpr (std::is_signed_v<T>) {
+                    // For signed integers, for lhs / -1, return lhs if lhs is the
+                    // most negative value
+                    if (rhs == -1 && lhs == std::numeric_limits<T>::min()) {
+                        return lhs;
+                    }
+                }
+            }
+            return lhs / rhs;
+        };
+        result = divide_values(a.value, b.value);
+    }
+    return result;
+}
+
+template <typename NumberT>
+utils::Result<NumberT> ConstEval::Dot2(const Source& source,
+                                       NumberT a1,
+                                       NumberT a2,
+                                       NumberT b1,
+                                       NumberT b2) {
+    auto r1 = Mul(source, a1, b1);
     if (!r1) {
         return utils::Failure;
     }
-    auto r2 = Mul(a2, b2);
+    auto r2 = Mul(source, a2, b2);
     if (!r2) {
         return utils::Failure;
     }
-    auto r = Add(r1.Get(), r2.Get());
+    auto r = Add(source, r1.Get(), r2.Get());
     if (!r) {
         return utils::Failure;
     }
@@ -752,29 +798,30 @@
 }
 
 template <typename NumberT>
-utils::Result<NumberT> ConstEval::Dot3(NumberT a1,
+utils::Result<NumberT> ConstEval::Dot3(const Source& source,
+                                       NumberT a1,
                                        NumberT a2,
                                        NumberT a3,
                                        NumberT b1,
                                        NumberT b2,
                                        NumberT b3) {
-    auto r1 = Mul(a1, b1);
+    auto r1 = Mul(source, a1, b1);
     if (!r1) {
         return utils::Failure;
     }
-    auto r2 = Mul(a2, b2);
+    auto r2 = Mul(source, a2, b2);
     if (!r2) {
         return utils::Failure;
     }
-    auto r3 = Mul(a3, b3);
+    auto r3 = Mul(source, a3, b3);
     if (!r3) {
         return utils::Failure;
     }
-    auto r = Add(r1.Get(), r2.Get());
+    auto r = Add(source, r1.Get(), r2.Get());
     if (!r) {
         return utils::Failure;
     }
-    r = Add(r.Get(), r3.Get());
+    r = Add(source, r.Get(), r3.Get());
     if (!r) {
         return utils::Failure;
     }
@@ -782,7 +829,8 @@
 }
 
 template <typename NumberT>
-utils::Result<NumberT> ConstEval::Dot4(NumberT a1,
+utils::Result<NumberT> ConstEval::Dot4(const Source& source,
+                                       NumberT a1,
                                        NumberT a2,
                                        NumberT a3,
                                        NumberT a4,
@@ -790,31 +838,31 @@
                                        NumberT b2,
                                        NumberT b3,
                                        NumberT b4) {
-    auto r1 = Mul(a1, b1);
+    auto r1 = Mul(source, a1, b1);
     if (!r1) {
         return utils::Failure;
     }
-    auto r2 = Mul(a2, b2);
+    auto r2 = Mul(source, a2, b2);
     if (!r2) {
         return utils::Failure;
     }
-    auto r3 = Mul(a3, b3);
+    auto r3 = Mul(source, a3, b3);
     if (!r3) {
         return utils::Failure;
     }
-    auto r4 = Mul(a4, b4);
+    auto r4 = Mul(source, a4, b4);
     if (!r4) {
         return utils::Failure;
     }
-    auto r = Add(r1.Get(), r2.Get());
+    auto r = Add(source, r1.Get(), r2.Get());
     if (!r) {
         return utils::Failure;
     }
-    r = Add(r.Get(), r3.Get());
+    r = Add(source, r.Get(), r3.Get());
     if (!r) {
         return utils::Failure;
     }
-    r = Add(r.Get(), r4.Get());
+    r = Add(source, r.Get(), r4.Get());
     if (!r) {
         return utils::Failure;
     }
@@ -822,16 +870,20 @@
 }
 
 template <typename NumberT>
-utils::Result<NumberT> ConstEval::Det2(NumberT a1, NumberT a2, NumberT b1, NumberT b2) {
-    auto r1 = Mul(a1, b2);
+utils::Result<NumberT> ConstEval::Det2(const Source& source,
+                                       NumberT a1,
+                                       NumberT a2,
+                                       NumberT b1,
+                                       NumberT b2) {
+    auto r1 = Mul(source, a1, b2);
     if (!r1) {
         return utils::Failure;
     }
-    auto r2 = Mul(b1, a2);
+    auto r2 = Mul(source, b1, a2);
     if (!r2) {
         return utils::Failure;
     }
-    auto r = Sub(r1.Get(), r2.Get());
+    auto r = Sub(source, r1.Get(), r2.Get());
     if (!r) {
         return utils::Failure;
     }
@@ -839,108 +891,163 @@
 }
 
 template <typename NumberT>
-utils::Result<NumberT> ConstEval::Clamp(NumberT e, NumberT low, NumberT high) {
+utils::Result<NumberT> ConstEval::Sqrt(const Source& source, NumberT v) {
+    if (v < NumberT(0)) {
+        AddError("sqrt must be called with a value >= 0", source);
+        return utils::Failure;
+    }
+    return NumberT{std::sqrt(v)};
+}
+
+auto ConstEval::SqrtFunc(const Source& source, const sem::Type* elem_ty) {
+    return [=](auto v) -> ImplResult {
+        if (auto r = Sqrt(source, v)) {
+            return CreateElement(builder, source, elem_ty, r.Get());
+        }
+        return utils::Failure;
+    };
+}
+
+template <typename NumberT>
+utils::Result<NumberT> ConstEval::Clamp(const Source&, NumberT e, NumberT low, NumberT high) {
     return NumberT{std::min(std::max(e, low), high)};
 }
 
-auto ConstEval::ClampFunc(const sem::Type* elem_ty) {
+auto ConstEval::ClampFunc(const Source& source, const sem::Type* elem_ty) {
     return [=](auto e, auto low, auto high) -> ImplResult {
-        if (auto r = Clamp(e, low, high)) {
-            return CreateElement(builder, elem_ty, r.Get());
+        if (auto r = Clamp(source, e, low, high)) {
+            return CreateElement(builder, source, elem_ty, r.Get());
         }
         return utils::Failure;
     };
 }
 
-auto ConstEval::AddFunc(const sem::Type* elem_ty) {
+auto ConstEval::AddFunc(const Source& source, const sem::Type* elem_ty) {
     return [=](auto a1, auto a2) -> ImplResult {
-        if (auto r = Add(a1, a2)) {
-            return CreateElement(builder, elem_ty, r.Get());
+        if (auto r = Add(source, a1, a2)) {
+            return CreateElement(builder, source, elem_ty, r.Get());
         }
         return utils::Failure;
     };
 }
 
-auto ConstEval::SubFunc(const sem::Type* elem_ty) {
+auto ConstEval::SubFunc(const Source& source, const sem::Type* elem_ty) {
     return [=](auto a1, auto a2) -> ImplResult {
-        if (auto r = Sub(a1, a2)) {
-            return CreateElement(builder, elem_ty, r.Get());
+        if (auto r = Sub(source, a1, a2)) {
+            return CreateElement(builder, source, elem_ty, r.Get());
         }
         return utils::Failure;
     };
 }
 
-auto ConstEval::MulFunc(const sem::Type* elem_ty) {
+auto ConstEval::MulFunc(const Source& source, const sem::Type* elem_ty) {
     return [=](auto a1, auto a2) -> ImplResult {
-        if (auto r = Mul(a1, a2)) {
-            return CreateElement(builder, elem_ty, r.Get());
+        if (auto r = Mul(source, a1, a2)) {
+            return CreateElement(builder, source, elem_ty, r.Get());
         }
         return utils::Failure;
     };
 }
 
-auto ConstEval::Dot2Func(const sem::Type* elem_ty) {
+auto ConstEval::DivFunc(const Source& source, const sem::Type* elem_ty) {
+    return [=](auto a1, auto a2) -> ImplResult {
+        if (auto r = Div(source, a1, a2)) {
+            return CreateElement(builder, source, elem_ty, r.Get());
+        }
+        return utils::Failure;
+    };
+}
+
+auto ConstEval::Dot2Func(const Source& source, const sem::Type* elem_ty) {
     return [=](auto a1, auto a2, auto b1, auto b2) -> ImplResult {
-        if (auto r = Dot2(a1, a2, b1, b2)) {
-            return CreateElement(builder, elem_ty, r.Get());
+        if (auto r = Dot2(source, a1, a2, b1, b2)) {
+            return CreateElement(builder, source, elem_ty, r.Get());
         }
         return utils::Failure;
     };
 }
 
-auto ConstEval::Dot3Func(const sem::Type* elem_ty) {
+auto ConstEval::Dot3Func(const Source& source, const sem::Type* elem_ty) {
     return [=](auto a1, auto a2, auto a3, auto b1, auto b2, auto b3) -> ImplResult {
-        if (auto r = Dot3(a1, a2, a3, b1, b2, b3)) {
-            return CreateElement(builder, elem_ty, r.Get());
+        if (auto r = Dot3(source, a1, a2, a3, b1, b2, b3)) {
+            return CreateElement(builder, source, elem_ty, r.Get());
         }
         return utils::Failure;
     };
 }
 
-auto ConstEval::Dot4Func(const sem::Type* elem_ty) {
+auto ConstEval::Dot4Func(const Source& source, const sem::Type* elem_ty) {
     return
         [=](auto a1, auto a2, auto a3, auto a4, auto b1, auto b2, auto b3, auto b4) -> ImplResult {
-            if (auto r = Dot4(a1, a2, a3, a4, b1, b2, b3, b4)) {
-                return CreateElement(builder, elem_ty, r.Get());
+            if (auto r = Dot4(source, a1, a2, a3, a4, b1, b2, b3, b4)) {
+                return CreateElement(builder, source, elem_ty, r.Get());
             }
             return utils::Failure;
         };
 }
 
-auto ConstEval::Det2Func(const sem::Type* elem_ty) {
+ConstEval::Result ConstEval::Dot(const Source& source,
+                                 const sem::Constant* v1,
+                                 const sem::Constant* v2) {
+    auto* vec_ty = v1->Type()->As<sem::Vector>();
+    TINT_ASSERT(Resolver, vec_ty);
+    auto* elem_ty = vec_ty->type();
+    switch (vec_ty->Width()) {
+        case 2:
+            return Dispatch_fia_fiu32_f16(   //
+                Dot2Func(source, elem_ty),   //
+                v1->Index(0), v1->Index(1),  //
+                v2->Index(0), v2->Index(1));
+        case 3:
+            return Dispatch_fia_fiu32_f16(                 //
+                Dot3Func(source, elem_ty),                 //
+                v1->Index(0), v1->Index(1), v1->Index(2),  //
+                v2->Index(0), v2->Index(1), v2->Index(2));
+        case 4:
+            return Dispatch_fia_fiu32_f16(                               //
+                Dot4Func(source, elem_ty),                               //
+                v1->Index(0), v1->Index(1), v1->Index(2), v1->Index(3),  //
+                v2->Index(0), v2->Index(1), v2->Index(2), v2->Index(3));
+    }
+    TINT_ICE(Resolver, builder.Diagnostics()) << "Expected vector";
+    return utils::Failure;
+}
+
+auto ConstEval::Det2Func(const Source& source, const sem::Type* elem_ty) {
     return [=](auto a, auto b, auto c, auto d) -> ImplResult {
-        if (auto r = Det2(a, b, c, d)) {
-            return CreateElement(builder, elem_ty, r.Get());
+        if (auto r = Det2(source, a, b, c, d)) {
+            return CreateElement(builder, source, elem_ty, r.Get());
         }
         return utils::Failure;
     };
 }
 
 ConstEval::Result ConstEval::Literal(const sem::Type* ty, const ast::LiteralExpression* literal) {
+    auto& source = literal->source;
     return Switch(
         literal,
         [&](const ast::BoolLiteralExpression* lit) {
-            return CreateElement(builder, ty, lit->value);
+            return CreateElement(builder, source, ty, lit->value);
         },
         [&](const ast::IntLiteralExpression* lit) -> ImplResult {
             switch (lit->suffix) {
                 case ast::IntLiteralExpression::Suffix::kNone:
-                    return CreateElement(builder, ty, AInt(lit->value));
+                    return CreateElement(builder, source, ty, AInt(lit->value));
                 case ast::IntLiteralExpression::Suffix::kI:
-                    return CreateElement(builder, ty, i32(lit->value));
+                    return CreateElement(builder, source, ty, i32(lit->value));
                 case ast::IntLiteralExpression::Suffix::kU:
-                    return CreateElement(builder, ty, u32(lit->value));
+                    return CreateElement(builder, source, ty, u32(lit->value));
             }
             return nullptr;
         },
         [&](const ast::FloatLiteralExpression* lit) -> ImplResult {
             switch (lit->suffix) {
                 case ast::FloatLiteralExpression::Suffix::kNone:
-                    return CreateElement(builder, ty, AFloat(lit->value));
+                    return CreateElement(builder, source, ty, AFloat(lit->value));
                 case ast::FloatLiteralExpression::Suffix::kF:
-                    return CreateElement(builder, ty, f32(lit->value));
+                    return CreateElement(builder, source, ty, f32(lit->value));
                 case ast::FloatLiteralExpression::Suffix::kH:
-                    return CreateElement(builder, ty, f16(lit->value));
+                    return CreateElement(builder, source, ty, f16(lit->value));
             }
             return nullptr;
         });
@@ -1118,10 +1225,10 @@
 
 ConstEval::Result ConstEval::OpComplement(const sem::Type* ty,
                                           utils::VectorRef<const sem::Constant*> args,
-                                          const Source&) {
+                                          const Source& source) {
     auto transform = [&](const sem::Constant* c) {
         auto create = [&](auto i) {
-            return CreateElement(builder, c->Type(), decltype(i)(~i.value));
+            return CreateElement(builder, source, c->Type(), decltype(i)(~i.value));
         };
         return Dispatch_ia_iu32(create, c);
     };
@@ -1130,7 +1237,7 @@
 
 ConstEval::Result ConstEval::OpUnaryMinus(const sem::Type* ty,
                                           utils::VectorRef<const sem::Constant*> args,
-                                          const Source&) {
+                                          const Source& source) {
     auto transform = [&](const sem::Constant* c) {
         auto create = [&](auto i) {
             // For signed integrals, avoid C++ UB by not negating the
@@ -1143,9 +1250,9 @@
                 if (v != std::numeric_limits<T>::min()) {
                     v = -v;
                 }
-                return CreateElement(builder, c->Type(), decltype(i)(v));
+                return CreateElement(builder, source, c->Type(), decltype(i)(v));
             } else {
-                return CreateElement(builder, c->Type(), decltype(i)(-i.value));
+                return CreateElement(builder, source, c->Type(), decltype(i)(-i.value));
             }
         };
         return Dispatch_fia_fi32_f16(create, c);
@@ -1155,9 +1262,11 @@
 
 ConstEval::Result ConstEval::OpNot(const sem::Type* ty,
                                    utils::VectorRef<const sem::Constant*> args,
-                                   const Source&) {
+                                   const Source& source) {
     auto transform = [&](const sem::Constant* c) {
-        auto create = [&](auto i) { return CreateElement(builder, c->Type(), decltype(i)(!i)); };
+        auto create = [&](auto i) {
+            return CreateElement(builder, source, c->Type(), decltype(i)(!i));
+        };
         return Dispatch_bool(create, c);
     };
     return TransformElements(builder, ty, transform, args[0]);
@@ -1166,9 +1275,8 @@
 ConstEval::Result ConstEval::OpPlus(const sem::Type* ty,
                                     utils::VectorRef<const sem::Constant*> args,
                                     const Source& source) {
-    TINT_SCOPED_ASSIGNMENT(current_source, &source);
     auto transform = [&](const sem::Constant* c0, const sem::Constant* c1) {
-        return Dispatch_fia_fiu32_f16(AddFunc(c0->Type()), c0, c1);
+        return Dispatch_fia_fiu32_f16(AddFunc(source, c0->Type()), c0, c1);
     };
 
     return TransformBinaryElements(builder, ty, transform, args[0], args[1]);
@@ -1177,9 +1285,8 @@
 ConstEval::Result ConstEval::OpMinus(const sem::Type* ty,
                                      utils::VectorRef<const sem::Constant*> args,
                                      const Source& source) {
-    TINT_SCOPED_ASSIGNMENT(current_source, &source);
     auto transform = [&](const sem::Constant* c0, const sem::Constant* c1) {
-        return Dispatch_fia_fiu32_f16(SubFunc(c0->Type()), c0, c1);
+        return Dispatch_fia_fiu32_f16(SubFunc(source, c0->Type()), c0, c1);
     };
 
     return TransformBinaryElements(builder, ty, transform, args[0], args[1]);
@@ -1188,9 +1295,8 @@
 ConstEval::Result ConstEval::OpMultiply(const sem::Type* ty,
                                         utils::VectorRef<const sem::Constant*> args,
                                         const Source& source) {
-    TINT_SCOPED_ASSIGNMENT(current_source, &source);
     auto transform = [&](const sem::Constant* c0, const sem::Constant* c1) {
-        return Dispatch_fia_fiu32_f16(MulFunc(c0->Type()), c0, c1);
+        return Dispatch_fia_fiu32_f16(MulFunc(source, c0->Type()), c0, c1);
     };
 
     return TransformBinaryElements(builder, ty, transform, args[0], args[1]);
@@ -1199,7 +1305,6 @@
 ConstEval::Result ConstEval::OpMultiplyMatVec(const sem::Type* ty,
                                               utils::VectorRef<const sem::Constant*> args,
                                               const Source& source) {
-    TINT_SCOPED_ASSIGNMENT(current_source, &source);
     auto* mat_ty = args[0]->Type()->As<sem::Matrix>();
     auto* vec_ty = args[1]->Type()->As<sem::Vector>();
     auto* elem_ty = vec_ty->type();
@@ -1208,29 +1313,29 @@
         ImplResult result;
         switch (mat_ty->columns()) {
             case 2:
-                result = Dispatch_fa_f32_f16(Dot2Func(elem_ty),        //
-                                             m->Index(0)->Index(row),  //
-                                             m->Index(1)->Index(row),  //
-                                             v->Index(0),              //
+                result = Dispatch_fa_f32_f16(Dot2Func(source, elem_ty),  //
+                                             m->Index(0)->Index(row),    //
+                                             m->Index(1)->Index(row),    //
+                                             v->Index(0),                //
                                              v->Index(1));
                 break;
             case 3:
-                result = Dispatch_fa_f32_f16(Dot3Func(elem_ty),        //
-                                             m->Index(0)->Index(row),  //
-                                             m->Index(1)->Index(row),  //
-                                             m->Index(2)->Index(row),  //
-                                             v->Index(0),              //
+                result = Dispatch_fa_f32_f16(Dot3Func(source, elem_ty),  //
+                                             m->Index(0)->Index(row),    //
+                                             m->Index(1)->Index(row),    //
+                                             m->Index(2)->Index(row),    //
+                                             v->Index(0),                //
                                              v->Index(1), v->Index(2));
                 break;
             case 4:
-                result = Dispatch_fa_f32_f16(Dot4Func(elem_ty),        //
-                                             m->Index(0)->Index(row),  //
-                                             m->Index(1)->Index(row),  //
-                                             m->Index(2)->Index(row),  //
-                                             m->Index(3)->Index(row),  //
-                                             v->Index(0),              //
-                                             v->Index(1),              //
-                                             v->Index(2),              //
+                result = Dispatch_fa_f32_f16(Dot4Func(source, elem_ty),  //
+                                             m->Index(0)->Index(row),    //
+                                             m->Index(1)->Index(row),    //
+                                             m->Index(2)->Index(row),    //
+                                             m->Index(3)->Index(row),    //
+                                             v->Index(0),                //
+                                             v->Index(1),                //
+                                             v->Index(2),                //
                                              v->Index(3));
                 break;
         }
@@ -1250,7 +1355,6 @@
 ConstEval::Result ConstEval::OpMultiplyVecMat(const sem::Type* ty,
                                               utils::VectorRef<const sem::Constant*> args,
                                               const Source& source) {
-    TINT_SCOPED_ASSIGNMENT(current_source, &source);
     auto* vec_ty = args[0]->Type()->As<sem::Vector>();
     auto* mat_ty = args[1]->Type()->As<sem::Matrix>();
     auto* elem_ty = vec_ty->type();
@@ -1259,30 +1363,30 @@
         ImplResult result;
         switch (mat_ty->rows()) {
             case 2:
-                result = Dispatch_fa_f32_f16(Dot2Func(elem_ty),        //
-                                             m->Index(col)->Index(0),  //
-                                             m->Index(col)->Index(1),  //
-                                             v->Index(0),              //
+                result = Dispatch_fa_f32_f16(Dot2Func(source, elem_ty),  //
+                                             m->Index(col)->Index(0),    //
+                                             m->Index(col)->Index(1),    //
+                                             v->Index(0),                //
                                              v->Index(1));
                 break;
             case 3:
-                result = Dispatch_fa_f32_f16(Dot3Func(elem_ty),        //
-                                             m->Index(col)->Index(0),  //
-                                             m->Index(col)->Index(1),  //
+                result = Dispatch_fa_f32_f16(Dot3Func(source, elem_ty),  //
+                                             m->Index(col)->Index(0),    //
+                                             m->Index(col)->Index(1),    //
                                              m->Index(col)->Index(2),
                                              v->Index(0),  //
                                              v->Index(1),  //
                                              v->Index(2));
                 break;
             case 4:
-                result = Dispatch_fa_f32_f16(Dot4Func(elem_ty),        //
-                                             m->Index(col)->Index(0),  //
-                                             m->Index(col)->Index(1),  //
-                                             m->Index(col)->Index(2),  //
-                                             m->Index(col)->Index(3),  //
-                                             v->Index(0),              //
-                                             v->Index(1),              //
-                                             v->Index(2),              //
+                result = Dispatch_fa_f32_f16(Dot4Func(source, elem_ty),  //
+                                             m->Index(col)->Index(0),    //
+                                             m->Index(col)->Index(1),    //
+                                             m->Index(col)->Index(2),    //
+                                             m->Index(col)->Index(3),    //
+                                             v->Index(0),                //
+                                             v->Index(1),                //
+                                             v->Index(2),                //
                                              v->Index(3));
         }
         return result;
@@ -1302,7 +1406,6 @@
 ConstEval::Result ConstEval::OpMultiplyMatMat(const sem::Type* ty,
                                               utils::VectorRef<const sem::Constant*> args,
                                               const Source& source) {
-    TINT_SCOPED_ASSIGNMENT(current_source, &source);
     auto* mat1 = args[0];
     auto* mat2 = args[1];
     auto* mat1_ty = mat1->Type()->As<sem::Matrix>();
@@ -1316,30 +1419,30 @@
         ImplResult result;
         switch (mat1_ty->columns()) {
             case 2:
-                result = Dispatch_fa_f32_f16(Dot2Func(elem_ty),  //
-                                             m1e(row, 0),        //
-                                             m1e(row, 1),        //
-                                             m2e(0, col),        //
+                result = Dispatch_fa_f32_f16(Dot2Func(source, elem_ty),  //
+                                             m1e(row, 0),                //
+                                             m1e(row, 1),                //
+                                             m2e(0, col),                //
                                              m2e(1, col));
                 break;
             case 3:
-                result = Dispatch_fa_f32_f16(Dot3Func(elem_ty),  //
-                                             m1e(row, 0),        //
-                                             m1e(row, 1),        //
-                                             m1e(row, 2),        //
-                                             m2e(0, col),        //
-                                             m2e(1, col),        //
+                result = Dispatch_fa_f32_f16(Dot3Func(source, elem_ty),  //
+                                             m1e(row, 0),                //
+                                             m1e(row, 1),                //
+                                             m1e(row, 2),                //
+                                             m2e(0, col),                //
+                                             m2e(1, col),                //
                                              m2e(2, col));
                 break;
             case 4:
-                result = Dispatch_fa_f32_f16(Dot4Func(elem_ty),  //
-                                             m1e(row, 0),        //
-                                             m1e(row, 1),        //
-                                             m1e(row, 2),        //
-                                             m1e(row, 3),        //
-                                             m2e(0, col),        //
-                                             m2e(1, col),        //
-                                             m2e(2, col),        //
+                result = Dispatch_fa_f32_f16(Dot4Func(source, elem_ty),  //
+                                             m1e(row, 0),                //
+                                             m1e(row, 1),                //
+                                             m1e(row, 2),                //
+                                             m1e(row, 3),                //
+                                             m2e(0, col),                //
+                                             m2e(1, col),                //
+                                             m2e(2, col),                //
                                              m2e(3, col));
                 break;
         }
@@ -1368,41 +1471,7 @@
                                       utils::VectorRef<const sem::Constant*> args,
                                       const Source& source) {
     auto transform = [&](const sem::Constant* c0, const sem::Constant* c1) {
-        auto create = [&](auto i, auto j) -> ImplResult {
-            using NumberT = decltype(i);
-            NumberT result;
-            if constexpr (IsAbstract<NumberT>) {
-                // Check for over/underflow for abstract values
-                if (auto r = CheckedDiv(i, j)) {
-                    result = r->value;
-                } else {
-                    AddError(OverflowErrorMessage(i, "/", j), source);
-                    return utils::Failure;
-                }
-            } else {
-                using T = UnwrapNumber<NumberT>;
-                auto divide_values = [](T lhs, T rhs) {
-                    if constexpr (std::is_integral_v<T>) {
-                        // For integers, lhs / 0 returns lhs
-                        if (rhs == 0) {
-                            return lhs;
-                        }
-
-                        if constexpr (std::is_signed_v<T>) {
-                            // For signed integers, for lhs / -1, return lhs if lhs is the
-                            // most negative value
-                            if (rhs == -1 && lhs == std::numeric_limits<T>::min()) {
-                                return lhs;
-                            }
-                        }
-                    }
-                    return lhs / rhs;
-                };
-                result = divide_values(i.value, j.value);
-            }
-            return CreateElement(builder, c0->Type(), result);
-        };
-        return Dispatch_fia_fiu32_f16(create, c0, c1);
+        return Dispatch_fia_fiu32_f16(DivFunc(source, c0->Type()), c0, c1);
     };
 
     return TransformBinaryElements(builder, ty, transform, args[0], args[1]);
@@ -1410,10 +1479,10 @@
 
 ConstEval::Result ConstEval::OpEqual(const sem::Type* ty,
                                      utils::VectorRef<const sem::Constant*> args,
-                                     const Source&) {
+                                     const Source& source) {
     auto transform = [&](const sem::Constant* c0, const sem::Constant* c1) {
         auto create = [&](auto i, auto j) -> ImplResult {
-            return CreateElement(builder, sem::Type::DeepestElementOf(ty), i == j);
+            return CreateElement(builder, source, sem::Type::DeepestElementOf(ty), i == j);
         };
         return Dispatch_fia_fiu32_f16_bool(create, c0, c1);
     };
@@ -1423,10 +1492,10 @@
 
 ConstEval::Result ConstEval::OpNotEqual(const sem::Type* ty,
                                         utils::VectorRef<const sem::Constant*> args,
-                                        const Source&) {
+                                        const Source& source) {
     auto transform = [&](const sem::Constant* c0, const sem::Constant* c1) {
         auto create = [&](auto i, auto j) -> ImplResult {
-            return CreateElement(builder, sem::Type::DeepestElementOf(ty), i != j);
+            return CreateElement(builder, source, sem::Type::DeepestElementOf(ty), i != j);
         };
         return Dispatch_fia_fiu32_f16_bool(create, c0, c1);
     };
@@ -1436,10 +1505,10 @@
 
 ConstEval::Result ConstEval::OpLessThan(const sem::Type* ty,
                                         utils::VectorRef<const sem::Constant*> args,
-                                        const Source&) {
+                                        const Source& source) {
     auto transform = [&](const sem::Constant* c0, const sem::Constant* c1) {
         auto create = [&](auto i, auto j) -> ImplResult {
-            return CreateElement(builder, sem::Type::DeepestElementOf(ty), i < j);
+            return CreateElement(builder, source, sem::Type::DeepestElementOf(ty), i < j);
         };
         return Dispatch_fia_fiu32_f16(create, c0, c1);
     };
@@ -1449,10 +1518,10 @@
 
 ConstEval::Result ConstEval::OpGreaterThan(const sem::Type* ty,
                                            utils::VectorRef<const sem::Constant*> args,
-                                           const Source&) {
+                                           const Source& source) {
     auto transform = [&](const sem::Constant* c0, const sem::Constant* c1) {
         auto create = [&](auto i, auto j) -> ImplResult {
-            return CreateElement(builder, sem::Type::DeepestElementOf(ty), i > j);
+            return CreateElement(builder, source, sem::Type::DeepestElementOf(ty), i > j);
         };
         return Dispatch_fia_fiu32_f16(create, c0, c1);
     };
@@ -1462,10 +1531,10 @@
 
 ConstEval::Result ConstEval::OpLessThanEqual(const sem::Type* ty,
                                              utils::VectorRef<const sem::Constant*> args,
-                                             const Source&) {
+                                             const Source& source) {
     auto transform = [&](const sem::Constant* c0, const sem::Constant* c1) {
         auto create = [&](auto i, auto j) -> ImplResult {
-            return CreateElement(builder, sem::Type::DeepestElementOf(ty), i <= j);
+            return CreateElement(builder, source, sem::Type::DeepestElementOf(ty), i <= j);
         };
         return Dispatch_fia_fiu32_f16(create, c0, c1);
     };
@@ -1475,10 +1544,10 @@
 
 ConstEval::Result ConstEval::OpGreaterThanEqual(const sem::Type* ty,
                                                 utils::VectorRef<const sem::Constant*> args,
-                                                const Source&) {
+                                                const Source& source) {
     auto transform = [&](const sem::Constant* c0, const sem::Constant* c1) {
         auto create = [&](auto i, auto j) -> ImplResult {
-            return CreateElement(builder, sem::Type::DeepestElementOf(ty), i >= j);
+            return CreateElement(builder, source, sem::Type::DeepestElementOf(ty), i >= j);
         };
         return Dispatch_fia_fiu32_f16(create, c0, c1);
     };
@@ -1488,7 +1557,7 @@
 
 ConstEval::Result ConstEval::OpAnd(const sem::Type* ty,
                                    utils::VectorRef<const sem::Constant*> args,
-                                   const Source&) {
+                                   const Source& source) {
     auto transform = [&](const sem::Constant* c0, const sem::Constant* c1) {
         auto create = [&](auto i, auto j) -> ImplResult {
             using T = decltype(i);
@@ -1498,7 +1567,7 @@
             } else {  // integral
                 result = i & j;
             }
-            return CreateElement(builder, sem::Type::DeepestElementOf(ty), result);
+            return CreateElement(builder, source, sem::Type::DeepestElementOf(ty), result);
         };
         return Dispatch_ia_iu32_bool(create, c0, c1);
     };
@@ -1508,7 +1577,7 @@
 
 ConstEval::Result ConstEval::OpOr(const sem::Type* ty,
                                   utils::VectorRef<const sem::Constant*> args,
-                                  const Source&) {
+                                  const Source& source) {
     auto transform = [&](const sem::Constant* c0, const sem::Constant* c1) {
         auto create = [&](auto i, auto j) -> ImplResult {
             using T = decltype(i);
@@ -1518,7 +1587,7 @@
             } else {  // integral
                 result = i | j;
             }
-            return CreateElement(builder, sem::Type::DeepestElementOf(ty), result);
+            return CreateElement(builder, source, sem::Type::DeepestElementOf(ty), result);
         };
         return Dispatch_ia_iu32_bool(create, c0, c1);
     };
@@ -1528,26 +1597,23 @@
 
 ConstEval::Result ConstEval::OpXor(const sem::Type* ty,
                                    utils::VectorRef<const sem::Constant*> args,
-                                   const Source&) {
+                                   const Source& source) {
     auto transform = [&](const sem::Constant* c0, const sem::Constant* c1) {
-        auto create = [&](auto i, auto j) -> const ImplConstant* {
-            return CreateElement(builder, sem::Type::DeepestElementOf(ty), decltype(i){i ^ j});
+        auto create = [&](auto i, auto j) -> ImplResult {
+            return CreateElement(builder, source, sem::Type::DeepestElementOf(ty),
+                                 decltype(i){i ^ j});
         };
         return Dispatch_ia_iu32(create, c0, c1);
     };
 
-    auto r = TransformElements(builder, ty, transform, args[0], args[1]);
-    if (builder.Diagnostics().contains_errors()) {
-        return utils::Failure;
-    }
-    return r;
+    return TransformElements(builder, ty, transform, args[0], args[1]);
 }
 
 ConstEval::Result ConstEval::OpShiftLeft(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 e1, auto e2) -> const ImplConstant* {
+        auto create = [&](auto e1, auto e2) -> ImplResult {
             using NumberT = decltype(e1);
             using T = UnwrapNumber<NumberT>;
             using UT = std::make_unsigned_t<T>;
@@ -1565,13 +1631,13 @@
                     UT mask = ~UT{0} << (bit_width - must_match_msb);
                     if ((e1u & mask) != 0 && (e1u & mask) != mask) {
                         AddError("shift left operation results in sign change", source);
-                        return nullptr;
+                        return utils::Failure;
                     }
                 } else {
                     // If shift value >= bit_width, then any non-zero value would overflow
                     if (e1 != 0) {
                         AddError(OverflowErrorMessage(e1, "<<", e2), source);
-                        return nullptr;
+                        return utils::Failure;
                     }
 
                     // It's UB in C++ to shift by greater or equal to the bit width (even if the lhs
@@ -1587,7 +1653,7 @@
                         "shift left value must be less than the bit width of the lhs, which is " +
                             std::to_string(bit_width),
                         source);
-                    return nullptr;
+                    return utils::Failure;
                 }
 
                 if constexpr (std::is_signed_v<T>) {
@@ -1597,7 +1663,7 @@
                     UT mask = ~UT{0} << (bit_width - must_match_msb);
                     if ((e1u & mask) != 0 && (e1u & mask) != mask) {
                         AddError("shift left operation results in sign change", source);
-                        return nullptr;
+                        return utils::Failure;
                     }
                 } else {
                     // If T is an unsigned integer type, and any of the e2 most significant bits of
@@ -1607,6 +1673,7 @@
                         UT mask = ~UT{0} << (bit_width - must_be_zero_msb);
                         if ((e1u & mask) != 0) {
                             AddError(OverflowErrorMessage(e1, "<<", e2), source);
+                            return utils::Failure;
                         }
                     }
                 }
@@ -1614,7 +1681,7 @@
 
             // Avoid UB by left shifting as unsigned value
             auto result = static_cast<T>(static_cast<UT>(e1) << e2);
-            return CreateElement(builder, sem::Type::DeepestElementOf(ty), NumberT{result});
+            return CreateElement(builder, source, sem::Type::DeepestElementOf(ty), NumberT{result});
         };
         return Dispatch_ia_iu32(create, c0, c1);
     };
@@ -1622,19 +1689,15 @@
     if (!sem::Type::DeepestElementOf(args[1]->Type())->Is<sem::U32>()) {
         TINT_ICE(Resolver, builder.Diagnostics())
             << "Element type of rhs of ShiftLeft must be a u32";
-        return nullptr;
-    }
-
-    auto r = TransformElements(builder, ty, transform, args[0], args[1]);
-    if (builder.Diagnostics().contains_errors()) {
         return utils::Failure;
     }
-    return r;
+
+    return TransformElements(builder, ty, transform, args[0], args[1]);
 }
 
 ConstEval::Result ConstEval::abs(const sem::Type* ty,
                                  utils::VectorRef<const sem::Constant*> args,
-                                 const Source&) {
+                                 const Source& source) {
     auto transform = [&](const sem::Constant* c0) {
         auto create = [&](auto e) {
             using NumberT = decltype(e);
@@ -1650,7 +1713,7 @@
             } else {
                 result = NumberT{std::abs(e)};
             }
-            return CreateElement(builder, c0->Type(), result);
+            return CreateElement(builder, source, c0->Type(), result);
         };
         return Dispatch_fia_fiu32_f16(create, c0);
     };
@@ -1668,23 +1731,41 @@
                          source);
                 return utils::Failure;
             }
-            return CreateElement(builder, c0->Type(), NumberT(std::acos(i.value)));
+            return CreateElement(builder, source, c0->Type(), NumberT(std::acos(i.value)));
         };
         return Dispatch_fa_f32_f16(create, c0);
     };
     return TransformElements(builder, ty, transform, args[0]);
 }
 
+ConstEval::Result ConstEval::acosh(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)) {
+                AddError("acosh must be called with a value >= 1.0", source);
+                return utils::Failure;
+            }
+            return CreateElement(builder, source, c0->Type(), NumberT(std::acosh(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());
+                                 const Source& source) {
+    return CreateElement(builder, source, ty, !args[0]->AnyZero());
 }
 
 ConstEval::Result ConstEval::any(const sem::Type* ty,
                                  utils::VectorRef<const sem::Constant*> args,
-                                 const Source&) {
-    return CreateElement(builder, ty, !args[0]->AllZero());
+                                 const Source& source) {
+    return CreateElement(builder, source, ty, !args[0]->AllZero());
 }
 
 ConstEval::Result ConstEval::asin(const sem::Type* ty,
@@ -1698,7 +1779,7 @@
                          source);
                 return utils::Failure;
             }
-            return CreateElement(builder, c0->Type(), NumberT(std::asin(i.value)));
+            return CreateElement(builder, source, c0->Type(), NumberT(std::asin(i.value)));
         };
         return Dispatch_fa_f32_f16(create, c0);
     };
@@ -1707,27 +1788,23 @@
 
 ConstEval::Result ConstEval::asinh(const sem::Type* ty,
                                    utils::VectorRef<const sem::Constant*> args,
-                                   const Source&) {
+                                   const Source& source) {
     auto transform = [&](const sem::Constant* c0) {
         auto create = [&](auto i) {
-            return CreateElement(builder, c0->Type(), decltype(i)(std::asinh(i.value)));
+            return CreateElement(builder, source, c0->Type(), decltype(i)(std::asinh(i.value)));
         };
         return Dispatch_fa_f32_f16(create, c0);
     };
 
-    auto r = TransformElements(builder, ty, transform, args[0]);
-    if (builder.Diagnostics().contains_errors()) {
-        return utils::Failure;
-    }
-    return r;
+    return TransformElements(builder, ty, transform, args[0]);
 }
 
 ConstEval::Result ConstEval::atan(const sem::Type* ty,
                                   utils::VectorRef<const sem::Constant*> args,
-                                  const Source&) {
+                                  const Source& source) {
     auto transform = [&](const sem::Constant* c0) {
         auto create = [&](auto i) {
-            return CreateElement(builder, c0->Type(), decltype(i)(std::atan(i.value)));
+            return CreateElement(builder, source, c0->Type(), decltype(i)(std::atan(i.value)));
         };
         return Dispatch_fa_f32_f16(create, c0);
     };
@@ -1745,7 +1822,7 @@
                          source);
                 return utils::Failure;
             }
-            return CreateElement(builder, c0->Type(), NumberT(std::atanh(i.value)));
+            return CreateElement(builder, source, c0->Type(), NumberT(std::atanh(i.value)));
         };
         return Dispatch_fa_f32_f16(create, c0);
     };
@@ -1755,10 +1832,11 @@
 
 ConstEval::Result ConstEval::atan2(const sem::Type* ty,
                                    utils::VectorRef<const sem::Constant*> args,
-                                   const Source&) {
+                                   const Source& source) {
     auto transform = [&](const sem::Constant* c0, const sem::Constant* c1) {
         auto create = [&](auto i, auto j) {
-            return CreateElement(builder, c0->Type(), decltype(i)(std::atan2(i.value, j.value)));
+            return CreateElement(builder, source, c0->Type(),
+                                 decltype(i)(std::atan2(i.value, j.value)));
         };
         return Dispatch_fa_f32_f16(create, c0, c1);
     };
@@ -1767,10 +1845,10 @@
 
 ConstEval::Result ConstEval::ceil(const sem::Type* ty,
                                   utils::VectorRef<const sem::Constant*> args,
-                                  const Source&) {
+                                  const Source& source) {
     auto transform = [&](const sem::Constant* c0) {
         auto create = [&](auto e) {
-            return CreateElement(builder, c0->Type(), decltype(e)(std::ceil(e)));
+            return CreateElement(builder, source, c0->Type(), decltype(e)(std::ceil(e)));
         };
         return Dispatch_fa_f32_f16(create, c0);
     };
@@ -1779,23 +1857,49 @@
 
 ConstEval::Result ConstEval::clamp(const sem::Type* ty,
                                    utils::VectorRef<const sem::Constant*> args,
-                                   const Source&) {
+                                   const Source& source) {
     auto transform = [&](const sem::Constant* c0, const sem::Constant* c1,
                          const sem::Constant* c2) {
-        return Dispatch_fia_fiu32_f16(ClampFunc(c0->Type()), c0, c1, c2);
+        return Dispatch_fia_fiu32_f16(ClampFunc(source, c0->Type()), c0, c1, c2);
     };
     return TransformElements(builder, ty, transform, args[0], args[1], args[2]);
 }
 
+ConstEval::Result ConstEval::cos(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);
+            return CreateElement(builder, source, c0->Type(), NumberT(std::cos(i.value)));
+        };
+        return Dispatch_fa_f32_f16(create, c0);
+    };
+    return TransformElements(builder, ty, transform, args[0]);
+}
+
+ConstEval::Result ConstEval::cosh(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);
+            return CreateElement(builder, source, c0->Type(), NumberT(std::cosh(i.value)));
+        };
+        return Dispatch_fa_f32_f16(create, c0);
+    };
+    return TransformElements(builder, ty, transform, args[0]);
+}
+
 ConstEval::Result ConstEval::countLeadingZeros(const sem::Type* ty,
                                                utils::VectorRef<const sem::Constant*> args,
-                                               const Source&) {
+                                               const Source& source) {
     auto transform = [&](const sem::Constant* c0) {
         auto create = [&](auto e) {
             using NumberT = decltype(e);
             using T = UnwrapNumber<NumberT>;
             auto count = CountLeadingBits(T{e}, T{0});
-            return CreateElement(builder, c0->Type(), NumberT(count));
+            return CreateElement(builder, source, c0->Type(), NumberT(count));
         };
         return Dispatch_iu32(create, c0);
     };
@@ -1804,7 +1908,7 @@
 
 ConstEval::Result ConstEval::countOneBits(const sem::Type* ty,
                                           utils::VectorRef<const sem::Constant*> args,
-                                          const Source&) {
+                                          const Source& source) {
     auto transform = [&](const sem::Constant* c0) {
         auto create = [&](auto e) {
             using NumberT = decltype(e);
@@ -1819,7 +1923,7 @@
                 }
             }
 
-            return CreateElement(builder, c0->Type(), NumberT(count));
+            return CreateElement(builder, source, c0->Type(), NumberT(count));
         };
         return Dispatch_iu32(create, c0);
     };
@@ -1828,13 +1932,13 @@
 
 ConstEval::Result ConstEval::countTrailingZeros(const sem::Type* ty,
                                                 utils::VectorRef<const sem::Constant*> args,
-                                                const Source&) {
+                                                const Source& source) {
     auto transform = [&](const sem::Constant* c0) {
         auto create = [&](auto e) {
             using NumberT = decltype(e);
             using T = UnwrapNumber<NumberT>;
             auto count = CountTrailingBits(T{e}, T{0});
-            return CreateElement(builder, c0->Type(), NumberT(count));
+            return CreateElement(builder, source, c0->Type(), NumberT(count));
         };
         return Dispatch_iu32(create, c0);
     };
@@ -1844,7 +1948,6 @@
 ConstEval::Result ConstEval::cross(const sem::Type* ty,
                                    utils::VectorRef<const sem::Constant*> args,
                                    const Source& source) {
-    TINT_SCOPED_ASSIGNMENT(current_source, &source);
     auto* u = args[0];
     auto* v = args[1];
     auto* elem_ty = u->Type()->As<sem::Vector>()->type();
@@ -1868,17 +1971,15 @@
     auto* v1 = v->Index(1);
     auto* v2 = v->Index(2);
 
-    // auto x = Dispatch_fa_f32_f16(ab_minus_cd_func(elem_ty), u->Index(1), v->Index(2),
-    //                              v->Index(1), u->Index(2));
-    auto x = Dispatch_fa_f32_f16(Det2Func(elem_ty), u1, u2, v1, v2);
+    auto x = Dispatch_fa_f32_f16(Det2Func(source, elem_ty), u1, u2, v1, v2);
     if (!x) {
         return utils::Failure;
     }
-    auto y = Dispatch_fa_f32_f16(Det2Func(elem_ty), v0, v2, u0, u2);
+    auto y = Dispatch_fa_f32_f16(Det2Func(source, elem_ty), v0, v2, u0, u2);
     if (!y) {
         return utils::Failure;
     }
-    auto z = Dispatch_fa_f32_f16(Det2Func(elem_ty), u0, u1, v0, v1);
+    auto z = Dispatch_fa_f32_f16(Det2Func(source, elem_ty), u0, u1, v0, v1);
     if (!z) {
         return utils::Failure;
     }
@@ -1887,6 +1988,42 @@
                            utils::Vector<const sem::Constant*, 3>{x.Get(), y.Get(), z.Get()});
 }
 
+ConstEval::Result ConstEval::degrees(const sem::Type* ty,
+                                     utils::VectorRef<const sem::Constant*> args,
+                                     const Source& source) {
+    auto transform = [&](const sem::Constant* c0) {
+        auto create = [&](auto e) -> ImplResult {
+            using NumberT = decltype(e);
+            using T = UnwrapNumber<NumberT>;
+
+            auto pi = kPi<T>;
+            auto scale = Div(source, NumberT(180), NumberT(pi));
+            if (!scale) {
+                AddNote("when calculating degrees", source);
+                return utils::Failure;
+            }
+            auto result = Mul(source, e, scale.Get());
+            if (!result) {
+                AddNote("when calculating degrees", source);
+                return utils::Failure;
+            }
+            return CreateElement(builder, source, c0->Type(), result.Get());
+        };
+        return Dispatch_fa_f32_f16(create, c0);
+    };
+    return TransformElements(builder, ty, transform, args[0]);
+}
+
+ConstEval::Result ConstEval::dot(const sem::Type*,
+                                 utils::VectorRef<const sem::Constant*> args,
+                                 const Source& source) {
+    auto r = Dot(source, args[0], args[1]);
+    if (!r) {
+        AddNote("when calculating dot", source);
+    }
+    return r;
+}
+
 ConstEval::Result ConstEval::extractBits(const sem::Type* ty,
                                          utils::VectorRef<const sem::Constant*> args,
                                          const Source& source) {
@@ -1935,7 +2072,7 @@
 
                 result = NumberT{r};
             }
-            return CreateElement(builder, c0->Type(), result);
+            return CreateElement(builder, source, c0->Type(), result);
         };
         return Dispatch_iu32(create, c0);
     };
@@ -1944,7 +2081,7 @@
 
 ConstEval::Result ConstEval::firstLeadingBit(const sem::Type* ty,
                                              utils::VectorRef<const sem::Constant*> args,
-                                             const Source&) {
+                                             const Source& source) {
     auto transform = [&](const sem::Constant* c0) {
         auto create = [&](auto e) {
             using NumberT = decltype(e);
@@ -1979,7 +2116,7 @@
                 }
             }
 
-            return CreateElement(builder, c0->Type(), result);
+            return CreateElement(builder, source, c0->Type(), result);
         };
         return Dispatch_iu32(create, c0);
     };
@@ -1988,7 +2125,7 @@
 
 ConstEval::Result ConstEval::firstTrailingBit(const sem::Type* ty,
                                               utils::VectorRef<const sem::Constant*> args,
-                                              const Source&) {
+                                              const Source& source) {
     auto transform = [&](const sem::Constant* c0) {
         auto create = [&](auto e) {
             using NumberT = decltype(e);
@@ -2005,7 +2142,7 @@
                 result = NumberT(pos);
             }
 
-            return CreateElement(builder, c0->Type(), result);
+            return CreateElement(builder, source, c0->Type(), result);
         };
         return Dispatch_iu32(create, c0);
     };
@@ -2014,10 +2151,10 @@
 
 ConstEval::Result ConstEval::floor(const sem::Type* ty,
                                    utils::VectorRef<const sem::Constant*> args,
-                                   const Source&) {
+                                   const Source& source) {
     auto transform = [&](const sem::Constant* c0) {
         auto create = [&](auto e) {
-            return CreateElement(builder, c0->Type(), decltype(e)(std::floor(e)));
+            return CreateElement(builder, source, c0->Type(), decltype(e)(std::floor(e)));
         };
         return Dispatch_fa_f32_f16(create, c0);
     };
@@ -2038,19 +2175,19 @@
             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);
 
+            constexpr UT w = sizeof(UT) * 8;
+            if (o > w || c > w || (o + c) > w) {
+                AddError("'offset + 'count' must be less than or equal to the bit width of 'e'",
+                         source);
+                return utils::Failure;
+            }
+
             NumberT result;
             if (c == UT{0}) {
                 // The result is e if c is 0
@@ -2069,18 +2206,133 @@
                 result = NumberT{r};
             }
 
-            return CreateElement(builder, c0->Type(), result);
+            return CreateElement(builder, source, c0->Type(), result);
         };
         return Dispatch_iu32(create, c0, c1);
     };
     return TransformElements(builder, ty, transform, args[0], args[1]);
 }
 
+ConstEval::Result ConstEval::length(const sem::Type* ty,
+                                    utils::VectorRef<const sem::Constant*> args,
+                                    const Source& source) {
+    auto calculate = [&]() -> ImplResult {
+        auto* vec_ty = args[0]->Type()->As<sem::Vector>();
+
+        // Evaluates to the absolute value of e if T is scalar.
+        if (vec_ty == nullptr) {
+            auto create = [&](auto e) {
+                using NumberT = decltype(e);
+                return CreateElement(builder, source, ty, NumberT{std::abs(e)});
+            };
+            return Dispatch_fa_f32_f16(create, args[0]);
+        }
+
+        // Evaluates to sqrt(e[0]^2 + e[1]^2 + ...) if T is a vector type.
+        auto d = Dot(source, args[0], args[0]);
+        if (!d) {
+            return utils::Failure;
+        }
+        return Dispatch_fa_f32_f16(SqrtFunc(source, ty), d.Get());
+    };
+    auto r = calculate();
+    if (!r) {
+        AddNote("when calculating length", source);
+    }
+    return r;
+}
+
+ConstEval::Result ConstEval::max(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 e0, auto e1) {
+            return CreateElement(builder, source, c0->Type(), decltype(e0)(std::max(e0, e1)));
+        };
+        return Dispatch_fia_fiu32_f16(create, c0, c1);
+    };
+    return TransformElements(builder, ty, transform, args[0], args[1]);
+}
+
+ConstEval::Result ConstEval::min(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 e0, auto e1) {
+            return CreateElement(builder, source, c0->Type(), decltype(e0)(std::min(e0, e1)));
+        };
+        return Dispatch_fia_fiu32_f16(create, c0, c1);
+    };
+    return TransformElements(builder, ty, transform, args[0], args[1]);
+}
+
+ConstEval::Result ConstEval::modf(const sem::Type* ty,
+                                  utils::VectorRef<const sem::Constant*> args,
+                                  const Source& source) {
+    auto transform_fract = [&](const sem::Constant* c) {
+        auto create = [&](auto e) {
+            return CreateElement(builder, source, c->Type(),
+                                 decltype(e)(e.value - std::trunc(e.value)));
+        };
+        return Dispatch_fa_f32_f16(create, c);
+    };
+    auto transform_whole = [&](const sem::Constant* c) {
+        auto create = [&](auto e) {
+            return CreateElement(builder, source, c->Type(), decltype(e)(std::trunc(e.value)));
+        };
+        return Dispatch_fa_f32_f16(create, c);
+    };
+
+    utils::Vector<const sem::Constant*, 2> fields;
+
+    if (auto fract = TransformElements(builder, args[0]->Type(), transform_fract, args[0])) {
+        fields.Push(fract.Get());
+    } else {
+        return utils::Failure;
+    }
+
+    if (auto whole = TransformElements(builder, args[0]->Type(), transform_whole, args[0])) {
+        fields.Push(whole.Get());
+    } else {
+        return utils::Failure;
+    }
+
+    return CreateComposite(builder, ty, std::move(fields));
+}
+
+ConstEval::Result ConstEval::pack2x16float(const sem::Type* ty,
+                                           utils::VectorRef<const sem::Constant*> args,
+                                           const Source& source) {
+    auto convert = [&](f32 val) -> utils::Result<uint32_t> {
+        auto conv = CheckedConvert<f16>(val);
+        if (!conv) {
+            AddError(OverflowErrorMessage(val, "f16"), source);
+            return utils::Failure;
+        }
+        uint16_t v = conv.Get().BitsRepresentation();
+        return utils::Result<uint32_t>{v};
+    };
+
+    auto* e = args[0];
+    auto e0 = convert(e->Index(0)->As<f32>());
+    if (!e0) {
+        return utils::Failure;
+    }
+
+    auto e1 = convert(e->Index(1)->As<f32>());
+    if (!e1) {
+        return utils::Failure;
+    }
+
+    u32 ret = u32((e0.Get() & 0x0000'ffff) | (e1.Get() << 16));
+    return CreateElement(builder, source, ty, ret);
+}
+
 ConstEval::Result ConstEval::pack2x16snorm(const sem::Type* ty,
                                            utils::VectorRef<const sem::Constant*> args,
-                                           const Source&) {
+                                           const Source& source) {
     auto calc = [&](f32 val) -> u32 {
-        auto clamped = Clamp(val, f32(-1.0f), f32(1.0f)).Get();
+        auto clamped = Clamp(source, val, f32(-1.0f), f32(1.0f)).Get();
         return u32(utils::Bitcast<uint16_t>(
             static_cast<int16_t>(std::floor(0.5f + (32767.0f * clamped)))));
     };
@@ -2090,14 +2342,14 @@
     auto e1 = calc(e->Index(1)->As<f32>());
 
     u32 ret = u32((e0 & 0x0000'ffff) | (e1 << 16));
-    return CreateElement(builder, ty, ret);
+    return CreateElement(builder, source, ty, ret);
 }
 
 ConstEval::Result ConstEval::pack2x16unorm(const sem::Type* ty,
                                            utils::VectorRef<const sem::Constant*> args,
-                                           const Source&) {
+                                           const Source& source) {
     auto calc = [&](f32 val) -> u32 {
-        auto clamped = Clamp(val, f32(0.0f), f32(1.0f)).Get();
+        auto clamped = Clamp(source, val, f32(0.0f), f32(1.0f)).Get();
         return u32{std::floor(0.5f + (65535.0f * clamped))};
     };
 
@@ -2106,14 +2358,14 @@
     auto e1 = calc(e->Index(1)->As<f32>());
 
     u32 ret = u32((e0 & 0x0000'ffff) | (e1 << 16));
-    return CreateElement(builder, ty, ret);
+    return CreateElement(builder, source, ty, ret);
 }
 
 ConstEval::Result ConstEval::pack4x8snorm(const sem::Type* ty,
                                           utils::VectorRef<const sem::Constant*> args,
-                                          const Source&) {
+                                          const Source& source) {
     auto calc = [&](f32 val) -> u32 {
-        auto clamped = Clamp(val, f32(-1.0f), f32(1.0f)).Get();
+        auto clamped = Clamp(source, val, f32(-1.0f), f32(1.0f)).Get();
         return u32(
             utils::Bitcast<uint8_t>(static_cast<int8_t>(std::floor(0.5f + (127.0f * clamped)))));
     };
@@ -2126,14 +2378,14 @@
 
     uint32_t mask = 0x0000'00ff;
     u32 ret = u32((e0 & mask) | ((e1 & mask) << 8) | ((e2 & mask) << 16) | ((e3 & mask) << 24));
-    return CreateElement(builder, ty, ret);
+    return CreateElement(builder, source, ty, ret);
 }
 
 ConstEval::Result ConstEval::pack4x8unorm(const sem::Type* ty,
                                           utils::VectorRef<const sem::Constant*> args,
-                                          const Source&) {
+                                          const Source& source) {
     auto calc = [&](f32 val) -> u32 {
-        auto clamped = Clamp(val, f32(0.0f), f32(1.0f)).Get();
+        auto clamped = Clamp(source, val, f32(0.0f), f32(1.0f)).Get();
         return u32{std::floor(0.5f + (255.0f * clamped))};
     };
 
@@ -2145,12 +2397,38 @@
 
     uint32_t mask = 0x0000'00ff;
     u32 ret = u32((e0 & mask) | ((e1 & mask) << 8) | ((e2 & mask) << 16) | ((e3 & mask) << 24));
-    return CreateElement(builder, ty, ret);
+    return CreateElement(builder, source, ty, ret);
+}
+
+ConstEval::Result ConstEval::radians(const sem::Type* ty,
+                                     utils::VectorRef<const sem::Constant*> args,
+                                     const Source& source) {
+    auto transform = [&](const sem::Constant* c0) {
+        auto create = [&](auto e) -> ImplResult {
+            using NumberT = decltype(e);
+            using T = UnwrapNumber<NumberT>;
+
+            auto pi = kPi<T>;
+            auto scale = Div(source, NumberT(pi), NumberT(180));
+            if (!scale) {
+                AddNote("when calculating radians", source);
+                return utils::Failure;
+            }
+            auto result = Mul(source, e, scale.Get());
+            if (!result) {
+                AddNote("when calculating radians", source);
+                return utils::Failure;
+            }
+            return CreateElement(builder, source, c0->Type(), result.Get());
+        };
+        return Dispatch_fa_f32_f16(create, c0);
+    };
+    return TransformElements(builder, ty, transform, args[0]);
 }
 
 ConstEval::Result ConstEval::reverseBits(const sem::Type* ty,
                                          utils::VectorRef<const sem::Constant*> args,
-                                         const Source&) {
+                                         const Source& source) {
     auto transform = [&](const sem::Constant* c0) {
         auto create = [&](auto in_e) -> ImplResult {
             using NumberT = decltype(in_e);
@@ -2168,20 +2446,56 @@
                 }
             }
 
-            return CreateElement(builder, c0->Type(), NumberT{r});
+            return CreateElement(builder, source, 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&) {
+ConstEval::Result ConstEval::round(const sem::Type* ty,
+                                   utils::VectorRef<const sem::Constant*> args,
+                                   const Source& source) {
     auto transform = [&](const sem::Constant* c0) {
         auto create = [&](auto e) {
             using NumberT = decltype(e);
-            return CreateElement(builder, c0->Type(),
+            using T = UnwrapNumber<NumberT>;
+
+            auto integral = NumberT(0);
+            auto fract = std::abs(std::modf(e.value, &(integral.value)));
+            // When e lies halfway between integers k and k + 1, the result is k when k is even,
+            // and k + 1 when k is odd.
+            NumberT result = NumberT(0.0);
+            if (fract == NumberT(0.5)) {
+                // If the integral value is negative, then we need to subtract one in order to move
+                // to the correct `k`. The half way check is `k` and `k + 1` which in the positive
+                // case is `x` and `x + 1` but in the negative case is `x - 1` and `x`.
+                T integral_val = integral.value;
+                if (std::signbit(integral_val)) {
+                    integral_val = std::abs(integral_val - 1);
+                }
+                if (uint64_t(integral_val) % 2 == 0) {
+                    result = NumberT(std::floor(e.value));
+                } else {
+                    result = NumberT(std::ceil(e.value));
+                }
+            } else {
+                result = NumberT(std::round(e.value));
+            }
+            return CreateElement(builder, source, c0->Type(), result);
+        };
+        return Dispatch_fa_f32_f16(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& source) {
+    auto transform = [&](const sem::Constant* c0) {
+        auto create = [&](auto e) {
+            using NumberT = decltype(e);
+            return CreateElement(builder, source, c0->Type(),
                                  NumberT(std::min(std::max(e, NumberT(0.0)), NumberT(1.0))));
         };
         return Dispatch_fa_f32_f16(create, c0);
@@ -2191,11 +2505,11 @@
 
 ConstEval::Result ConstEval::select_bool(const sem::Type* ty,
                                          utils::VectorRef<const sem::Constant*> args,
-                                         const Source&) {
+                                         const Source& source) {
     auto cond = args[2]->As<bool>();
     auto transform = [&](const sem::Constant* c0, const sem::Constant* c1) {
         auto create = [&](auto f, auto t) -> ImplResult {
-            return CreateElement(builder, sem::Type::DeepestElementOf(ty), cond ? t : f);
+            return CreateElement(builder, source, sem::Type::DeepestElementOf(ty), cond ? t : f);
         };
         return Dispatch_fia_fiu32_f16_bool(create, c0, c1);
     };
@@ -2205,12 +2519,12 @@
 
 ConstEval::Result ConstEval::select_boolvec(const sem::Type* ty,
                                             utils::VectorRef<const sem::Constant*> args,
-                                            const Source&) {
+                                            const Source& source) {
     auto transform = [&](const sem::Constant* c0, const sem::Constant* c1, size_t index) {
         auto create = [&](auto f, auto t) -> ImplResult {
             // Get corresponding bool value at the current vector value index
             auto cond = args[2]->Index(index)->As<bool>();
-            return CreateElement(builder, sem::Type::DeepestElementOf(ty), cond ? t : f);
+            return CreateElement(builder, source, sem::Type::DeepestElementOf(ty), cond ? t : f);
         };
         return Dispatch_fia_fiu32_f16_bool(create, c0, c1);
     };
@@ -2220,7 +2534,7 @@
 
 ConstEval::Result ConstEval::sign(const sem::Type* ty,
                                   utils::VectorRef<const sem::Constant*> args,
-                                  const Source&) {
+                                  const Source& source) {
     auto transform = [&](const sem::Constant* c0) {
         auto create = [&](auto e) -> ImplResult {
             using NumberT = decltype(e);
@@ -2233,30 +2547,179 @@
             } else {
                 result = zero;
             }
-            return CreateElement(builder, c0->Type(), result);
+            return CreateElement(builder, source, c0->Type(), result);
         };
         return Dispatch_fa_f32_f16(create, c0);
     };
     return TransformElements(builder, ty, transform, args[0]);
 }
 
+ConstEval::Result ConstEval::sin(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);
+            return CreateElement(builder, source, c0->Type(), NumberT(std::sin(i.value)));
+        };
+        return Dispatch_fa_f32_f16(create, c0);
+    };
+    return TransformElements(builder, ty, transform, args[0]);
+}
+
+ConstEval::Result ConstEval::sinh(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);
+            return CreateElement(builder, source, c0->Type(), NumberT(std::sinh(i.value)));
+        };
+        return Dispatch_fa_f32_f16(create, c0);
+    };
+    return TransformElements(builder, ty, transform, args[0]);
+}
+
+ConstEval::Result ConstEval::smoothstep(const sem::Type* ty,
+                                        utils::VectorRef<const sem::Constant*> args,
+                                        const Source& source) {
+    auto transform = [&](const sem::Constant* c0, const sem::Constant* c1,
+                         const sem::Constant* c2) {
+        auto create = [&](auto low, auto high, auto x) -> ImplResult {
+            using NumberT = decltype(low);
+
+            auto err = [&] {
+                AddNote("when calculating smoothstep", source);
+                return utils::Failure;
+            };
+
+            // t = clamp((x - low) / (high - low), 0.0, 1.0)
+            auto x_minus_low = Sub(source, x, low);
+            auto high_minus_low = Sub(source, high, low);
+            if (!x_minus_low || !high_minus_low) {
+                return err();
+            }
+
+            auto div = Div(source, x_minus_low.Get(), high_minus_low.Get());
+            if (!div) {
+                return err();
+            }
+
+            auto clamp = Clamp(source, div.Get(), NumberT(0), NumberT(1));
+            auto t = clamp.Get();
+
+            // result = t * t * (3.0 - 2.0 * t)
+            auto t_times_t = Mul(source, t, t);
+            auto t_times_2 = Mul(source, NumberT(2), t);
+            if (!t_times_t || !t_times_2) {
+                return err();
+            }
+
+            auto three_minus_t_times_2 = Sub(source, NumberT(3), t_times_2.Get());
+            if (!three_minus_t_times_2) {
+                return err();
+            }
+
+            auto result = Mul(source, t_times_t.Get(), three_minus_t_times_2.Get());
+            if (!result) {
+                return err();
+            }
+            return CreateElement(builder, source, c0->Type(), result.Get());
+        };
+        return Dispatch_fa_f32_f16(create, c0, c1, c2);
+    };
+    return TransformElements(builder, ty, transform, args[0], args[1], args[2]);
+}
+
 ConstEval::Result ConstEval::step(const sem::Type* ty,
                                   utils::VectorRef<const sem::Constant*> args,
-                                  const Source&) {
+                                  const Source& source) {
     auto transform = [&](const sem::Constant* c0, const sem::Constant* c1) {
         auto create = [&](auto edge, auto x) -> ImplResult {
             using NumberT = decltype(edge);
             NumberT result = x.value < edge.value ? NumberT(0.0) : NumberT(1.0);
-            return CreateElement(builder, c0->Type(), result);
+            return CreateElement(builder, source, c0->Type(), result);
         };
         return Dispatch_fa_f32_f16(create, c0, c1);
     };
     return TransformElements(builder, ty, transform, args[0], args[1]);
 }
 
+ConstEval::Result ConstEval::sqrt(const sem::Type* ty,
+                                  utils::VectorRef<const sem::Constant*> args,
+                                  const Source& source) {
+    auto transform = [&](const sem::Constant* c0) {
+        return Dispatch_fa_f32_f16(SqrtFunc(source, c0->Type()), c0);
+    };
+
+    return TransformElements(builder, ty, transform, args[0]);
+}
+
+ConstEval::Result ConstEval::tan(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);
+            return CreateElement(builder, source, c0->Type(), NumberT(std::tan(i.value)));
+        };
+        return Dispatch_fa_f32_f16(create, c0);
+    };
+    return TransformElements(builder, ty, transform, args[0]);
+}
+
+ConstEval::Result ConstEval::tanh(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);
+            return CreateElement(builder, source, c0->Type(), NumberT(std::tanh(i.value)));
+        };
+        return Dispatch_fa_f32_f16(create, c0);
+    };
+    return TransformElements(builder, ty, transform, args[0]);
+}
+
+ConstEval::Result ConstEval::trunc(const sem::Type* ty,
+                                   utils::VectorRef<const sem::Constant*> args,
+                                   const Source& source) {
+    auto transform = [&](const sem::Constant* c0) {
+        auto create = [&](auto i) {
+            return CreateElement(builder, source, c0->Type(), decltype(i)(std::trunc(i.value)));
+        };
+        return Dispatch_fa_f32_f16(create, c0);
+    };
+    return TransformElements(builder, ty, transform, args[0]);
+}
+
+ConstEval::Result ConstEval::unpack2x16float(const sem::Type* ty,
+                                             utils::VectorRef<const sem::Constant*> args,
+                                             const Source& source) {
+    auto* inner_ty = sem::Type::DeepestElementOf(ty);
+    auto e = args[0]->As<u32>().value;
+
+    utils::Vector<const sem::Constant*, 2> els;
+    els.Reserve(2);
+    for (size_t i = 0; i < 2; ++i) {
+        auto in = f16::FromBits(uint16_t((e >> (16 * i)) & 0x0000'ffff));
+        auto val = CheckedConvert<f32>(in);
+        if (!val) {
+            AddError(OverflowErrorMessage(in, "f32"), source);
+            return utils::Failure;
+        }
+        auto el = CreateElement(builder, source, inner_ty, val.Get());
+        if (!el) {
+            return el;
+        }
+        els.Push(el.Get());
+    }
+    return CreateComposite(builder, ty, std::move(els));
+}
+
 ConstEval::Result ConstEval::unpack2x16snorm(const sem::Type* ty,
                                              utils::VectorRef<const sem::Constant*> args,
-                                             const Source&) {
+                                             const Source& source) {
     auto* inner_ty = sem::Type::DeepestElementOf(ty);
     auto e = args[0]->As<u32>().value;
 
@@ -2265,14 +2728,18 @@
     for (size_t i = 0; i < 2; ++i) {
         auto val = f32(
             std::max(static_cast<float>(int16_t((e >> (16 * i)) & 0x0000'ffff)) / 32767.f, -1.f));
-        els.Push(CreateElement(builder, inner_ty, val));
+        auto el = CreateElement(builder, source, inner_ty, val);
+        if (!el) {
+            return el;
+        }
+        els.Push(el.Get());
     }
     return CreateComposite(builder, ty, std::move(els));
 }
 
 ConstEval::Result ConstEval::unpack2x16unorm(const sem::Type* ty,
                                              utils::VectorRef<const sem::Constant*> args,
-                                             const Source&) {
+                                             const Source& source) {
     auto* inner_ty = sem::Type::DeepestElementOf(ty);
     auto e = args[0]->As<u32>().value;
 
@@ -2280,14 +2747,18 @@
     els.Reserve(2);
     for (size_t i = 0; i < 2; ++i) {
         auto val = f32(static_cast<float>(uint16_t((e >> (16 * i)) & 0x0000'ffff)) / 65535.f);
-        els.Push(CreateElement(builder, inner_ty, val));
+        auto el = CreateElement(builder, source, inner_ty, val);
+        if (!el) {
+            return el;
+        }
+        els.Push(el.Get());
     }
     return CreateComposite(builder, ty, std::move(els));
 }
 
 ConstEval::Result ConstEval::unpack4x8snorm(const sem::Type* ty,
                                             utils::VectorRef<const sem::Constant*> args,
-                                            const Source&) {
+                                            const Source& source) {
     auto* inner_ty = sem::Type::DeepestElementOf(ty);
     auto e = args[0]->As<u32>().value;
 
@@ -2296,14 +2767,18 @@
     for (size_t i = 0; i < 4; ++i) {
         auto val =
             f32(std::max(static_cast<float>(int8_t((e >> (8 * i)) & 0x0000'00ff)) / 127.f, -1.f));
-        els.Push(CreateElement(builder, inner_ty, val));
+        auto el = CreateElement(builder, source, inner_ty, val);
+        if (!el) {
+            return el;
+        }
+        els.Push(el.Get());
     }
     return CreateComposite(builder, ty, std::move(els));
 }
 
 ConstEval::Result ConstEval::unpack4x8unorm(const sem::Type* ty,
                                             utils::VectorRef<const sem::Constant*> args,
-                                            const Source&) {
+                                            const Source& source) {
     auto* inner_ty = sem::Type::DeepestElementOf(ty);
     auto e = args[0]->As<u32>().value;
 
@@ -2311,28 +2786,26 @@
     els.Reserve(4);
     for (size_t i = 0; i < 4; ++i) {
         auto val = f32(static_cast<float>(uint8_t((e >> (8 * i)) & 0x0000'00ff)) / 255.f);
-        els.Push(CreateElement(builder, inner_ty, val));
+        auto el = CreateElement(builder, source, inner_ty, val);
+        if (!el) {
+            return el;
+        }
+        els.Push(el.Get());
     }
     return CreateComposite(builder, ty, std::move(els));
 }
 
 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>()));
+                                           const Source& source) {
+    auto transform = [&](const sem::Constant* c) -> ImplResult {
+        auto value = c->As<f32>();
+        auto conv = CheckedConvert<f32>(f16(value));
         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));
-            }
+            AddError(OverflowErrorMessage(value, "f16"), source);
+            return utils::Failure;
         }
-        return CreateElement(builder, c->Type(), conv.Get());
+        return CreateElement(builder, source, c->Type(), conv.Get());
     };
     return TransformElements(builder, ty, transform, args[0]);
 }
@@ -2354,4 +2827,8 @@
     builder.Diagnostics().add_warning(diag::System::Resolver, msg, source);
 }
 
+void ConstEval::AddNote(const std::string& msg, const Source& source) const {
+    builder.Diagnostics().add_note(diag::System::Resolver, msg, source);
+}
+
 }  // namespace tint::resolver
diff --git a/src/tint/resolver/const_eval.h b/src/tint/resolver/const_eval.h
index 9907b36..f290adb 100644
--- a/src/tint/resolver/const_eval.h
+++ b/src/tint/resolver/const_eval.h
@@ -105,7 +105,7 @@
     /// Convert the `value` to `target_type`
     /// @param ty the result type
     /// @param value the value being converted
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the converted value, or null if the value cannot be calculated
     Result Convert(const sem::Type* ty, const sem::Constant* value, const Source& source);
 
@@ -116,7 +116,7 @@
     /// Type conversion
     /// @param ty the result type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the converted value, or null if the value cannot be calculated
     Result Conv(const sem::Type* ty,
                 utils::VectorRef<const sem::Constant*> args,
@@ -125,7 +125,7 @@
     /// Zero value type initializer
     /// @param ty the result type
     /// @param args the input arguments (no arguments provided)
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the constructed value, or null if the value cannot be calculated
     Result Zero(const sem::Type* ty,
                 utils::VectorRef<const sem::Constant*> args,
@@ -134,7 +134,7 @@
     /// Identity value type initializer
     /// @param ty the result type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the constructed value, or null if the value cannot be calculated
     Result Identity(const sem::Type* ty,
                     utils::VectorRef<const sem::Constant*> args,
@@ -143,7 +143,7 @@
     /// Vector splat initializer
     /// @param ty the vector type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the constructed value, or null if the value cannot be calculated
     Result VecSplat(const sem::Type* ty,
                     utils::VectorRef<const sem::Constant*> args,
@@ -152,7 +152,7 @@
     /// Vector initializer using scalars
     /// @param ty the vector type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the constructed value, or null if the value cannot be calculated
     Result VecInitS(const sem::Type* ty,
                     utils::VectorRef<const sem::Constant*> args,
@@ -161,7 +161,7 @@
     /// Vector initializer using a mix of scalars and smaller vectors
     /// @param ty the vector type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the constructed value, or null if the value cannot be calculated
     Result VecInitM(const sem::Type* ty,
                     utils::VectorRef<const sem::Constant*> args,
@@ -170,7 +170,7 @@
     /// Matrix initializer using scalar values
     /// @param ty the matrix type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the constructed value, or null if the value cannot be calculated
     Result MatInitS(const sem::Type* ty,
                     utils::VectorRef<const sem::Constant*> args,
@@ -179,7 +179,7 @@
     /// Matrix initializer using column vectors
     /// @param ty the matrix type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the constructed value, or null if the value cannot be calculated
     Result MatInitV(const sem::Type* ty,
                     utils::VectorRef<const sem::Constant*> args,
@@ -192,7 +192,7 @@
     /// Complement operator '~'
     /// @param ty the integer type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result OpComplement(const sem::Type* ty,
                         utils::VectorRef<const sem::Constant*> args,
@@ -201,7 +201,7 @@
     /// Unary minus operator '-'
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result OpUnaryMinus(const sem::Type* ty,
                         utils::VectorRef<const sem::Constant*> args,
@@ -210,7 +210,7 @@
     /// Unary not operator '!'
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result OpNot(const sem::Type* ty,
                  utils::VectorRef<const sem::Constant*> args,
@@ -223,7 +223,7 @@
     /// Plus operator '+'
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result OpPlus(const sem::Type* ty,
                   utils::VectorRef<const sem::Constant*> args,
@@ -232,7 +232,7 @@
     /// Minus operator '-'
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result OpMinus(const sem::Type* ty,
                    utils::VectorRef<const sem::Constant*> args,
@@ -241,7 +241,7 @@
     /// Multiply operator '*' for the same type on the LHS and RHS
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result OpMultiply(const sem::Type* ty,
                       utils::VectorRef<const sem::Constant*> args,
@@ -250,7 +250,7 @@
     /// Multiply operator '*' for matCxR<T> * vecC<T>
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result OpMultiplyMatVec(const sem::Type* ty,
                             utils::VectorRef<const sem::Constant*> args,
@@ -259,7 +259,7 @@
     /// Multiply operator '*' for vecR<T> * matCxR<T>
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result OpMultiplyVecMat(const sem::Type* ty,
                             utils::VectorRef<const sem::Constant*> args,
@@ -268,7 +268,7 @@
     /// Multiply operator '*' for matKxR<T> * matCxK<T>
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result OpMultiplyMatMat(const sem::Type* ty,
                             utils::VectorRef<const sem::Constant*> args,
@@ -277,7 +277,7 @@
     /// Divide operator '/'
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result OpDivide(const sem::Type* ty,
                     utils::VectorRef<const sem::Constant*> args,
@@ -286,7 +286,7 @@
     /// Equality operator '=='
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result OpEqual(const sem::Type* ty,
                    utils::VectorRef<const sem::Constant*> args,
@@ -295,7 +295,7 @@
     /// Inequality operator '!='
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result OpNotEqual(const sem::Type* ty,
                       utils::VectorRef<const sem::Constant*> args,
@@ -304,7 +304,7 @@
     /// Less than operator '<'
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result OpLessThan(const sem::Type* ty,
                       utils::VectorRef<const sem::Constant*> args,
@@ -313,7 +313,7 @@
     /// Greater than operator '>'
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result OpGreaterThan(const sem::Type* ty,
                          utils::VectorRef<const sem::Constant*> args,
@@ -322,7 +322,7 @@
     /// Less than or equal operator '<='
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result OpLessThanEqual(const sem::Type* ty,
                            utils::VectorRef<const sem::Constant*> args,
@@ -331,7 +331,7 @@
     /// Greater than or equal operator '>='
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result OpGreaterThanEqual(const sem::Type* ty,
                               utils::VectorRef<const sem::Constant*> args,
@@ -340,7 +340,7 @@
     /// Bitwise and operator '&'
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result OpAnd(const sem::Type* ty,
                  utils::VectorRef<const sem::Constant*> args,
@@ -349,7 +349,7 @@
     /// Bitwise or operator '|'
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result OpOr(const sem::Type* ty,
                 utils::VectorRef<const sem::Constant*> args,
@@ -358,7 +358,7 @@
     /// Bitwise xor operator '^'
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result OpXor(const sem::Type* ty,
                  utils::VectorRef<const sem::Constant*> args,
@@ -367,7 +367,7 @@
     /// Bitwise shift left operator '<<'
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result OpShiftLeft(const sem::Type* ty,
                        utils::VectorRef<const sem::Constant*> args,
@@ -380,34 +380,43 @@
     /// abs builtin
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @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
+    /// @param source the source location
     /// @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);
 
+    /// acosh builtin
+    /// @param ty the expression type
+    /// @param args the input arguments
+    /// @param source the source location
+    /// @return the result value, or null if the value cannot be calculated
+    Result acosh(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
+    /// @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);
+
     /// any builtin
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result any(const sem::Type* ty,
                utils::VectorRef<const sem::Constant*> args,
@@ -416,7 +425,7 @@
     /// asin builtin
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result asin(const sem::Type* ty,
                 utils::VectorRef<const sem::Constant*> args,
@@ -425,7 +434,7 @@
     /// asinh builtin
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result asinh(const sem::Type* ty,
                  utils::VectorRef<const sem::Constant*> args,
@@ -434,7 +443,7 @@
     /// atan builtin
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result atan(const sem::Type* ty,
                 utils::VectorRef<const sem::Constant*> args,
@@ -443,7 +452,7 @@
     /// atanh builtin
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result atanh(const sem::Type* ty,
                  utils::VectorRef<const sem::Constant*> args,
@@ -452,7 +461,7 @@
     /// atan2 builtin
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result atan2(const sem::Type* ty,
                  utils::VectorRef<const sem::Constant*> args,
@@ -461,7 +470,7 @@
     /// ceil builtin
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result ceil(const sem::Type* ty,
                 utils::VectorRef<const sem::Constant*> args,
@@ -470,16 +479,34 @@
     /// clamp builtin
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result clamp(const sem::Type* ty,
                  utils::VectorRef<const sem::Constant*> args,
                  const Source& source);
 
+    /// cos builtin
+    /// @param ty the expression type
+    /// @param args the input arguments
+    /// @param source the source location
+    /// @return the result value, or null if the value cannot be calculated
+    Result cos(const sem::Type* ty,
+               utils::VectorRef<const sem::Constant*> args,
+               const Source& source);
+
+    /// cosh builtin
+    /// @param ty the expression type
+    /// @param args the input arguments
+    /// @param source the source location
+    /// @return the result value, or null if the value cannot be calculated
+    Result cosh(const sem::Type* ty,
+                utils::VectorRef<const sem::Constant*> args,
+                const Source& source);
+
     /// countLeadingZeros builtin
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result countLeadingZeros(const sem::Type* ty,
                              utils::VectorRef<const sem::Constant*> args,
@@ -488,7 +515,7 @@
     /// countOneBits builtin
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result countOneBits(const sem::Type* ty,
                         utils::VectorRef<const sem::Constant*> args,
@@ -497,7 +524,7 @@
     /// countTrailingZeros builtin
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result countTrailingZeros(const sem::Type* ty,
                               utils::VectorRef<const sem::Constant*> args,
@@ -506,17 +533,34 @@
     /// cross builtin
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result cross(const sem::Type* ty,
                  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 degrees(const sem::Type* ty,
+                   utils::VectorRef<const sem::Constant*> args,
+                   const Source& source);
+
+    /// dot builtin
+    /// @param ty the expression type
+    /// @param args the input arguments
+    /// @param source the source location
+    /// @return the result value, or null if the value cannot be calculated
+    Result dot(const sem::Type* ty,
+               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
+    /// @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);
@@ -524,7 +568,7 @@
     /// firstLeadingBit builtin
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result firstLeadingBit(const sem::Type* ty,
                            utils::VectorRef<const sem::Constant*> args,
@@ -533,7 +577,7 @@
     /// firstTrailingBit builtin
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result firstTrailingBit(const sem::Type* ty,
                             utils::VectorRef<const sem::Constant*> args,
@@ -542,7 +586,7 @@
     /// floor builtin
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result floor(const sem::Type* ty,
                  utils::VectorRef<const sem::Constant*> args,
@@ -551,16 +595,61 @@
     /// insertBits builtin
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @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);
 
+    /// length builtin
+    /// @param ty the expression type
+    /// @param args the input arguments
+    /// @param source the source location
+    /// @return the result value, or null if the value cannot be calculated
+    Result length(const sem::Type* ty,
+                  utils::VectorRef<const sem::Constant*> args,
+                  const Source& source);
+
+    /// max builtin
+    /// @param ty the expression type
+    /// @param args the input arguments
+    /// @param source the source location
+    /// @return the result value, or null if the value cannot be calculated
+    Result max(const sem::Type* ty,
+               utils::VectorRef<const sem::Constant*> args,
+               const Source& source);
+
+    /// min builtin
+    /// @param ty the expression type
+    /// @param args the input arguments
+    /// @param source the source location
+    /// @return the result value, or null if the value cannot be calculated
+    Result min(const sem::Type* ty,  // NOLINT(build/include_what_you_use)  -- confused by min
+               utils::VectorRef<const sem::Constant*> args,
+               const Source& source);
+
+    /// modf builtin
+    /// @param ty the expression type
+    /// @param args the input arguments
+    /// @param source the source location
+    /// @return the result value, or null if the value cannot be calculated
+    Result modf(const sem::Type* ty,
+                utils::VectorRef<const sem::Constant*> args,
+                const Source& source);
+
+    /// pack2x16float builtin
+    /// @param ty the expression type
+    /// @param args the input arguments
+    /// @param source the source location
+    /// @return the result value, or null if the value cannot be calculated
+    Result pack2x16float(const sem::Type* ty,
+                         utils::VectorRef<const sem::Constant*> args,
+                         const Source& source);
+
     /// pack2x16snorm builtin
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result pack2x16snorm(const sem::Type* ty,
                          utils::VectorRef<const sem::Constant*> args,
@@ -569,7 +658,7 @@
     /// pack2x16unorm builtin
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result pack2x16unorm(const sem::Type* ty,
                          utils::VectorRef<const sem::Constant*> args,
@@ -578,7 +667,7 @@
     /// pack4x8snorm builtin
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result pack4x8snorm(const sem::Type* ty,
                         utils::VectorRef<const sem::Constant*> args,
@@ -587,25 +676,43 @@
     /// pack4x8unorm builtin
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result pack4x8unorm(const sem::Type* ty,
                         utils::VectorRef<const sem::Constant*> args,
                         const Source& source);
 
-    /// reverseBits builtin
+    /// radians 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 radians(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
+    /// @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);
 
+    /// round builtin
+    /// @param ty the expression type
+    /// @param args the input arguments
+    /// @param source the source location
+    /// @return the result value, or null if the value cannot be calculated
+    Result round(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
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result saturate(const sem::Type* ty,
                     utils::VectorRef<const sem::Constant*> args,
@@ -614,7 +721,7 @@
     /// select builtin with single bool third arg
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result select_bool(const sem::Type* ty,
                        utils::VectorRef<const sem::Constant*> args,
@@ -623,7 +730,7 @@
     /// select builtin with vector of bool third arg
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result select_boolvec(const sem::Type* ty,
                           utils::VectorRef<const sem::Constant*> args,
@@ -632,25 +739,97 @@
     /// sign builtin
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result sign(const sem::Type* ty,
                 utils::VectorRef<const sem::Constant*> args,
                 const Source& source);
 
+    /// sin builtin
+    /// @param ty the expression type
+    /// @param args the input arguments
+    /// @param source the source location
+    /// @return the result value, or null if the value cannot be calculated
+    Result sin(const sem::Type* ty,
+               utils::VectorRef<const sem::Constant*> args,
+               const Source& source);
+
+    /// sinh builtin
+    /// @param ty the expression type
+    /// @param args the input arguments
+    /// @param source the source location
+    /// @return the result value, or null if the value cannot be calculated
+    Result sinh(const sem::Type* ty,
+                utils::VectorRef<const sem::Constant*> args,
+                const Source& source);
+
+    /// smoothstep builtin
+    /// @param ty the expression type
+    /// @param args the input arguments
+    /// @param source the source location
+    /// @return the result value, or null if the value cannot be calculated
+    Result smoothstep(const sem::Type* ty,
+                      utils::VectorRef<const sem::Constant*> args,
+                      const Source& source);
+
     /// step builtin
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result step(const sem::Type* ty,
                 utils::VectorRef<const sem::Constant*> args,
                 const Source& source);
 
+    /// sqrt builtin
+    /// @param ty the expression type
+    /// @param args the input arguments
+    /// @param source the source location
+    /// @return the result value, or null if the value cannot be calculated
+    Result sqrt(const sem::Type* ty,
+                utils::VectorRef<const sem::Constant*> args,
+                const Source& source);
+
+    /// tan builtin
+    /// @param ty the expression type
+    /// @param args the input arguments
+    /// @param source the source location
+    /// @return the result value, or null if the value cannot be calculated
+    Result tan(const sem::Type* ty,
+               utils::VectorRef<const sem::Constant*> args,
+               const Source& source);
+
+    /// tanh builtin
+    /// @param ty the expression type
+    /// @param args the input arguments
+    /// @param source the source location
+    /// @return the result value, or null if the value cannot be calculated
+    Result tanh(const sem::Type* ty,
+                utils::VectorRef<const sem::Constant*> args,
+                const Source& source);
+
+    /// trunc builtin
+    /// @param ty the expression type
+    /// @param args the input arguments
+    /// @param source the source location
+    /// @return the result value, or null if the value cannot be calculated
+    Result trunc(const sem::Type* ty,
+                 utils::VectorRef<const sem::Constant*> args,
+                 const Source& source);
+
+    /// unpack2x16float builtin
+    /// @param ty the expression type
+    /// @param args the input arguments
+    /// @param source the source location
+    /// @return the result value, or null if the value cannot be calculated
+    Result unpack2x16float(const sem::Type* ty,
+                           utils::VectorRef<const sem::Constant*> args,
+                           const Source& source);
+
     /// unpack2x16snorm builtin
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result unpack2x16snorm(const sem::Type* ty,
                            utils::VectorRef<const sem::Constant*> args,
@@ -659,7 +838,7 @@
     /// unpack2x16unorm builtin
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result unpack2x16unorm(const sem::Type* ty,
                            utils::VectorRef<const sem::Constant*> args,
@@ -668,7 +847,7 @@
     /// unpack4x8snorm builtin
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result unpack4x8snorm(const sem::Type* ty,
                           utils::VectorRef<const sem::Constant*> args,
@@ -677,7 +856,7 @@
     /// unpack4x8unorm builtin
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result unpack4x8unorm(const sem::Type* ty,
                           utils::VectorRef<const sem::Constant*> args,
@@ -686,7 +865,7 @@
     /// quantizeToF16 builtin
     /// @param ty the expression type
     /// @param args the input arguments
-    /// @param source the source location of the conversion
+    /// @param source the source location
     /// @return the result value, or null if the value cannot be calculated
     Result quantizeToF16(const sem::Type* ty,
                          utils::VectorRef<const sem::Constant*> args,
@@ -699,37 +878,57 @@
     /// Adds the given warning message to the diagnostics
     void AddWarning(const std::string& msg, const Source& source) const;
 
+    /// Adds the given note message to the diagnostics
+    void AddNote(const std::string& msg, const Source& source) const;
+
     /// Adds two Number<T>s
+    /// @param source the source location
     /// @param a the lhs number
     /// @param b the rhs number
     /// @returns the result number on success, or logs an error and returns Failure
     template <typename NumberT>
-    utils::Result<NumberT> Add(NumberT a, NumberT b);
+    utils::Result<NumberT> Add(const Source& source, NumberT a, NumberT b);
 
     /// Subtracts two Number<T>s
+    /// @param source the source location
     /// @param a the lhs number
     /// @param b the rhs number
     /// @returns the result number on success, or logs an error and returns Failure
     template <typename NumberT>
-    utils::Result<NumberT> Sub(NumberT a, NumberT b);
+    utils::Result<NumberT> Sub(const Source& source, NumberT a, NumberT b);
 
     /// Multiplies two Number<T>s
+    /// @param source the source location
     /// @param a the lhs number
     /// @param b the rhs number
     /// @returns the result number on success, or logs an error and returns Failure
     template <typename NumberT>
-    utils::Result<NumberT> Mul(NumberT a, NumberT b);
+    utils::Result<NumberT> Mul(const Source& source, NumberT a, NumberT b);
+
+    /// Divides two Number<T>s
+    /// @param source the source location
+    /// @param a the lhs number
+    /// @param b the rhs number
+    /// @returns the result number on success, or logs an error and returns Failure
+    template <typename NumberT>
+    utils::Result<NumberT> Div(const Source& source, NumberT a, NumberT b);
 
     /// Returns the dot product of (a1,a2) with (b1,b2)
+    /// @param source the source location
     /// @param a1 component 1 of lhs vector
     /// @param a2 component 2 of lhs vector
     /// @param b1 component 1 of rhs vector
     /// @param b2 component 2 of rhs vector
     /// @returns the result number on success, or logs an error and returns Failure
     template <typename NumberT>
-    utils::Result<NumberT> Dot2(NumberT a1, NumberT a2, NumberT b1, NumberT b2);
+    utils::Result<NumberT> Dot2(const Source& source,
+                                NumberT a1,
+                                NumberT a2,
+                                NumberT b1,
+                                NumberT b2);
 
     /// Returns the dot product of (a1,a2,a3) with (b1,b2,b3)
+    /// @param source the source location
     /// @param a1 component 1 of lhs vector
     /// @param a2 component 2 of lhs vector
     /// @param a3 component 3 of lhs vector
@@ -738,7 +937,8 @@
     /// @param b3 component 3 of rhs vector
     /// @returns the result number on success, or logs an error and returns Failure
     template <typename NumberT>
-    utils::Result<NumberT> Dot3(NumberT a1,
+    utils::Result<NumberT> Dot3(const Source& source,
+                                NumberT a1,
                                 NumberT a2,
                                 NumberT a3,
                                 NumberT b1,
@@ -746,6 +946,7 @@
                                 NumberT b3);
 
     /// Returns the dot product of (a1,b1,c1,d1) with (a2,b2,c2,d2)
+    /// @param source the source location
     /// @param a1 component 1 of lhs vector
     /// @param a2 component 2 of lhs vector
     /// @param a3 component 3 of lhs vector
@@ -756,7 +957,8 @@
     /// @param b4 component 4 of rhs vector
     /// @returns the result number on success, or logs an error and returns Failure
     template <typename NumberT>
-    utils::Result<NumberT> Dot4(NumberT a1,
+    utils::Result<NumberT> Dot4(const Source& source,
+                                NumberT a1,
                                 NumberT a2,
                                 NumberT a3,
                                 NumberT a4,
@@ -766,71 +968,108 @@
                                 NumberT b4);
 
     /// Returns the determinant of the 2x2 matrix [(a1, a2), (b1, b2)]
+    /// @param source the source location
     /// @param a1 component 1 of the first column vector
     /// @param a2 component 2 of the first column vector
     /// @param b1 component 1 of the second column vector
     /// @param b2 component 2 of the second column vector
     template <typename NumberT>
-    utils::Result<NumberT> Det2(NumberT a1, NumberT a2, NumberT b1, NumberT b2);
+    utils::Result<NumberT> Det2(const Source& source,
+                                NumberT a1,
+                                NumberT a2,
+                                NumberT b1,
+                                NumberT b2);
+
+    template <typename NumberT>
+    utils::Result<NumberT> Sqrt(const Source& source, NumberT v);
 
     /// Clamps e between low and high
+    /// @param source the source location
     /// @param e the number to clamp
     /// @param low the lower bound
     /// @param high the upper bound
     /// @returns the result number on success, or logs an error and returns Failure
     template <typename NumberT>
-    utils::Result<NumberT> Clamp(NumberT e, NumberT low, NumberT high);
+    utils::Result<NumberT> Clamp(const Source& source, NumberT e, NumberT low, NumberT high);
 
     /// Returns a callable that calls Add, and creates a Constant with its result of type `elem_ty`
     /// if successful, or returns Failure otherwise.
+    /// @param source the source location
     /// @param elem_ty the element type of the Constant to create on success
     /// @returns the callable function
-    auto AddFunc(const sem::Type* elem_ty);
+    auto AddFunc(const Source& source, const sem::Type* elem_ty);
 
     /// Returns a callable that calls Sub, and creates a Constant with its result of type `elem_ty`
     /// if successful, or returns Failure otherwise.
+    /// @param source the source location
     /// @param elem_ty the element type of the Constant to create on success
     /// @returns the callable function
-    auto SubFunc(const sem::Type* elem_ty);
+    auto SubFunc(const Source& source, const sem::Type* elem_ty);
 
     /// Returns a callable that calls Mul, and creates a Constant with its result of type `elem_ty`
     /// if successful, or returns Failure otherwise.
+    /// @param source the source location
     /// @param elem_ty the element type of the Constant to create on success
     /// @returns the callable function
-    auto MulFunc(const sem::Type* elem_ty);
+    auto MulFunc(const Source& source, const sem::Type* elem_ty);
+
+    /// Returns a callable that calls Div, and creates a Constant with its result of type `elem_ty`
+    /// if successful, or returns Failure otherwise.
+    /// @param source the source location
+    /// @param elem_ty the element type of the Constant to create on success
+    /// @returns the callable function
+    auto DivFunc(const Source& source, const sem::Type* elem_ty);
 
     /// Returns a callable that calls Dot2, and creates a Constant with its result of type `elem_ty`
     /// if successful, or returns Failure otherwise.
+    /// @param source the source location
     /// @param elem_ty the element type of the Constant to create on success
     /// @returns the callable function
-    auto Dot2Func(const sem::Type* elem_ty);
+    auto Dot2Func(const Source& source, const sem::Type* elem_ty);
 
     /// Returns a callable that calls Dot3, and creates a Constant with its result of type `elem_ty`
     /// if successful, or returns Failure otherwise.
+    /// @param source the source location
     /// @param elem_ty the element type of the Constant to create on success
     /// @returns the callable function
-    auto Dot3Func(const sem::Type* elem_ty);
+    auto Dot3Func(const Source& source, const sem::Type* elem_ty);
 
     /// Returns a callable that calls Dot4, and creates a Constant with its result of type `elem_ty`
     /// if successful, or returns Failure otherwise.
+    /// @param source the source location
     /// @param elem_ty the element type of the Constant to create on success
     /// @returns the callable function
-    auto Dot4Func(const sem::Type* elem_ty);
+    auto Dot4Func(const Source& source, const sem::Type* elem_ty);
 
     /// Returns a callable that calls Det2, and creates a Constant with its result of type `elem_ty`
     /// if successful, or returns Failure otherwise.
+    /// @param source the source location
     /// @param elem_ty the element type of the Constant to create on success
     /// @returns the callable function
-    auto Det2Func(const sem::Type* elem_ty);
+    auto Det2Func(const Source& source, const sem::Type* elem_ty);
 
     /// Returns a callable that calls Clamp, and creates a Constant with its result of type
     /// `elem_ty` if successful, or returns Failure otherwise.
+    /// @param source the source location
     /// @param elem_ty the element type of the Constant to create on success
     /// @returns the callable function
-    auto ClampFunc(const sem::Type* elem_ty);
+    auto ClampFunc(const Source& source, const sem::Type* elem_ty);
+
+    /// Returns a callable that calls SqrtFunc, and creates a Constant with its
+    /// result of type `elem_ty` if successful, or returns Failure otherwise.
+    /// @param source the source location
+    /// @param elem_ty the element type of the Constant to create on success
+    /// @returns the callable function
+    auto SqrtFunc(const Source& source, const sem::Type* elem_ty);
+
+    /// Returns the dot product of v1 and v2.
+    /// @param source the source location
+    /// @param v1 the first vector
+    /// @param v2 the second vector
+    /// @returns the dot product
+    Result Dot(const Source& source, const sem::Constant* v1, const sem::Constant* v2);
 
     ProgramBuilder& builder;
-    const Source* current_source = nullptr;
 };
 
 }  // namespace tint::resolver
diff --git a/src/tint/resolver/const_eval_binary_op_test.cc b/src/tint/resolver/const_eval_binary_op_test.cc
index 8708aa8..70176c7 100644
--- a/src/tint/resolver/const_eval_binary_op_test.cc
+++ b/src/tint/resolver/const_eval_binary_op_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
 using ::testing::HasSubstr;
 
@@ -24,10 +26,16 @@
 using resolver::operator<<;
 
 struct Case {
+    struct Success {
+        Types value;
+    };
+    struct Failure {
+        std::string error;
+    };
+
     Types lhs;
     Types rhs;
-    Types expected;
-    bool overflow;
+    utils::Result<Success, Failure> expected;
 };
 
 struct ErrorCase {
@@ -37,20 +45,37 @@
 
 /// Creates a Case with Values of any type
 template <typename T, typename U, typename V>
-Case C(Value<T> lhs, Value<U> rhs, Value<V> expected, bool overflow = false) {
-    return Case{std::move(lhs), std::move(rhs), std::move(expected), overflow};
+Case C(Value<T> lhs, Value<U> rhs, Value<V> expected) {
+    return Case{std::move(lhs), std::move(rhs), Case::Success{std::move(expected)}};
 }
 
 /// Convenience overload that creates a Case with just scalars
 template <typename T, typename U, typename V, typename = std::enable_if_t<!IsValue<T>>>
-Case C(T lhs, U rhs, V expected, bool overflow = false) {
-    return Case{Val(lhs), Val(rhs), Val(expected), overflow};
+Case C(T lhs, U rhs, V expected) {
+    return Case{Val(lhs), Val(rhs), Case::Success{Val(expected)}};
+}
+
+/// Creates an failure Case with Values of any type
+template <typename T, typename U>
+Case E(Value<T> lhs, Value<U> rhs, std::string error) {
+    return Case{std::move(lhs), std::move(rhs), Case::Failure{std::move(error)}};
+}
+
+/// Convenience overload that creates an error Case with just scalars
+template <typename T, typename U, typename = std::enable_if_t<!IsValue<T>>>
+Case E(T lhs, U rhs, std::string error) {
+    return Case{Val(lhs), Val(rhs), Case::Failure{std::move(error)}};
 }
 
 /// Prints Case to ostream
 static std::ostream& operator<<(std::ostream& o, const Case& c) {
-    o << "lhs: " << c.lhs << ", rhs: " << c.rhs << ", expected: " << c.expected
-      << ", overflow: " << c.overflow;
+    o << "lhs: " << c.lhs << ", rhs: " << c.rhs << ", expected: ";
+    if (c.expected) {
+        auto s = c.expected.Get();
+        o << s.value;
+    } else {
+        o << "[ERROR: " << c.expected.Failure().error << "]";
+    }
     return o;
 }
 
@@ -66,38 +91,25 @@
     auto op = std::get<0>(GetParam());
     auto& c = std::get<1>(GetParam());
 
-    auto* expected = ToValueBase(c.expected);
-    if (expected->IsAbstract() && c.overflow) {
-        // Overflow is not allowed for abstract types. This is tested separately.
-        return;
-    }
-
-    auto* lhs = ToValueBase(c.lhs);
-    auto* rhs = ToValueBase(c.rhs);
-
-    auto* lhs_expr = lhs->Expr(*this);
-    auto* rhs_expr = rhs->Expr(*this);
-    auto* expr = create<ast::BinaryExpression>(op, lhs_expr, rhs_expr);
+    auto* lhs_expr = ToValueBase(c.lhs)->Expr(*this);
+    auto* rhs_expr = ToValueBase(c.rhs)->Expr(*this);
+    auto* expr = create<ast::BinaryExpression>(Source{{12, 34}}, op, lhs_expr, rhs_expr);
     GlobalConst("C", expr);
-    ASSERT_TRUE(r()->Resolve()) << r()->error();
 
-    auto* sem = Sem().Get(expr);
-    const sem::Constant* value = sem->ConstantValue();
-    ASSERT_NE(value, nullptr);
-    EXPECT_TYPE(value->Type(), sem->Type());
+    if (c.expected) {
+        ASSERT_TRUE(r()->Resolve()) << r()->error();
+        auto expected_case = c.expected.Get();
+        auto* expected = ToValueBase(expected_case.value);
 
-    auto values_flat = ScalarArgsFrom(value);
-    auto expected_values_flat = expected->Args();
-    ASSERT_EQ(values_flat.values.Length(), expected_values_flat.values.Length());
-    for (size_t i = 0; i < values_flat.values.Length(); ++i) {
-        auto& a = values_flat.values[i];
-        auto& b = expected_values_flat.values[i];
-        EXPECT_EQ(a, b);
-        if (expected->IsIntegral()) {
-            // 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(builder::As<AInt>(a), builder::As<AInt>(b));
-        }
+        auto* sem = Sem().Get(expr);
+        const sem::Constant* value = sem->ConstantValue();
+        ASSERT_NE(value, nullptr);
+        EXPECT_TYPE(value->Type(), sem->Type());
+
+        CheckConstant(value, expected);
+    } else {
+        ASSERT_FALSE(r()->Resolve());
+        EXPECT_EQ(r()->error(), c.expected.Failure().error);
     }
 }
 
@@ -113,27 +125,47 @@
 template <typename T>
 std::vector<Case> OpAddIntCases() {
     static_assert(IsIntegral<T>);
-    return {
+    auto r = std::vector<Case>{
         C(T{0}, T{0}, T{0}),
         C(T{1}, T{2}, T{3}),
         C(T::Lowest(), T{1}, T{T::Lowest() + 1}),
         C(T::Highest(), Negate(T{1}), T{T::Highest() - 1}),
         C(T::Lowest(), T::Highest(), Negate(T{1})),
-        C(T::Highest(), T{1}, T::Lowest(), true),
-        C(T::Lowest(), Negate(T{1}), T::Highest(), true),
     };
+    if constexpr (IsAbstract<T>) {
+        auto error_msg = [](auto a, auto b) {
+            return "12:34 error: " + OverflowErrorMessage(a, "+", b);
+        };
+        ConcatInto(  //
+            r, std::vector<Case>{
+                   E(T::Highest(), T{1}, error_msg(T::Highest(), T{1})),
+                   E(T::Lowest(), Negate(T{1}), error_msg(T::Lowest(), Negate(T{1}))),
+               });
+    } else {
+        ConcatInto(  //
+            r, std::vector<Case>{
+                   C(T::Highest(), T{1}, T::Lowest()),
+                   C(T::Lowest(), Negate(T{1}), T::Highest()),
+               });
+    }
+
+    return r;
 }
 template <typename T>
 std::vector<Case> OpAddFloatCases() {
     static_assert(IsFloatingPoint<T>);
-    return {
+    auto error_msg = [](auto a, auto b) {
+        return "12:34 error: " + OverflowErrorMessage(a, "+", b);
+    };
+    return std::vector<Case>{
         C(T{0}, T{0}, T{0}),
         C(T{1}, T{2}, T{3}),
         C(T::Lowest(), T{1}, T{T::Lowest() + 1}),
         C(T::Highest(), Negate(T{1}), T{T::Highest() - 1}),
         C(T::Lowest(), T::Highest(), T{0}),
-        C(T::Highest(), T::Highest(), T::Inf(), true),
-        C(T::Lowest(), Negate(T::Highest()), -T::Inf(), true),
+
+        E(T::Highest(), T::Highest(), error_msg(T::Highest(), T::Highest())),
+        E(T::Lowest(), Negate(T::Highest()), error_msg(T::Lowest(), Negate(T::Highest()))),
     };
 }
 INSTANTIATE_TEST_SUITE_P(Add,
@@ -150,27 +182,46 @@
 template <typename T>
 std::vector<Case> OpSubIntCases() {
     static_assert(IsIntegral<T>);
-    return {
+    auto r = std::vector<Case>{
         C(T{0}, T{0}, T{0}),
         C(T{3}, T{2}, T{1}),
         C(T{T::Lowest() + 1}, T{1}, T::Lowest()),
         C(T{T::Highest() - 1}, Negate(T{1}), T::Highest()),
         C(Negate(T{1}), T::Highest(), T::Lowest()),
-        C(T::Lowest(), T{1}, T::Highest(), true),
-        C(T::Highest(), Negate(T{1}), T::Lowest(), true),
     };
+    if constexpr (IsAbstract<T>) {
+        auto error_msg = [](auto a, auto b) {
+            return "12:34 error: " + OverflowErrorMessage(a, "-", b);
+        };
+        ConcatInto(  //
+            r, std::vector<Case>{
+                   E(T::Lowest(), T{1}, error_msg(T::Lowest(), T{1})),
+                   E(T::Highest(), Negate(T{1}), error_msg(T::Highest(), Negate(T{1}))),
+               });
+    } else {
+        ConcatInto(  //
+            r, std::vector<Case>{
+                   C(T::Lowest(), T{1}, T::Highest()),
+                   C(T::Highest(), Negate(T{1}), T::Lowest()),
+               });
+    }
+    return r;
 }
 template <typename T>
 std::vector<Case> OpSubFloatCases() {
     static_assert(IsFloatingPoint<T>);
-    return {
+    auto error_msg = [](auto a, auto b) {
+        return "12:34 error: " + OverflowErrorMessage(a, "-", b);
+    };
+    return std::vector<Case>{
         C(T{0}, T{0}, T{0}),
         C(T{3}, T{2}, T{1}),
         C(T::Highest(), T{1}, T{T::Highest() - 1}),
         C(T::Lowest(), Negate(T{1}), T{T::Lowest() + 1}),
         C(T{0}, T::Highest(), T::Lowest()),
-        C(T::Highest(), Negate(T::Highest()), T::Inf(), true),
-        C(T::Lowest(), T::Highest(), -T::Inf(), true),
+
+        E(T::Highest(), Negate(T::Highest()), error_msg(T::Highest(), Negate(T::Highest()))),
+        E(T::Lowest(), T::Highest(), error_msg(T::Lowest(), T::Highest())),
     };
 }
 INSTANTIATE_TEST_SUITE_P(Sub,
@@ -186,21 +237,39 @@
 
 template <typename T>
 std::vector<Case> OpMulScalarCases() {
-    return {
+    auto r = std::vector<Case>{
         C(T{0}, T{0}, T{0}),
         C(T{1}, T{2}, T{2}),
         C(T{2}, T{3}, T{6}),
         C(Negate(T{2}), T{3}, Negate(T{6})),
         C(T::Highest(), T{1}, T::Highest()),
         C(T::Lowest(), T{1}, T::Lowest()),
-        C(T::Highest(), T::Highest(), Mul(T::Highest(), T::Highest()), true),
-        C(T::Lowest(), T::Lowest(), Mul(T::Lowest(), T::Lowest()), true),
     };
+    if constexpr (IsAbstract<T> || IsFloatingPoint<T>) {
+        auto error_msg = [](auto a, auto b) {
+            return "12:34 error: " + OverflowErrorMessage(a, "*", b);
+        };
+        ConcatInto(  //
+            r, std::vector<Case>{
+                   // Fail if result is +/-inf
+                   E(T::Highest(), T::Highest(), error_msg(T::Highest(), T::Highest())),
+                   E(T::Lowest(), T::Lowest(), error_msg(T::Lowest(), T::Lowest())),
+                   E(T::Highest(), T{2}, error_msg(T::Highest(), T{2})),
+                   E(T::Lowest(), Negate(T{2}), error_msg(T::Lowest(), Negate(T{2}))),
+               });
+    } else {
+        ConcatInto(  //
+            r, std::vector<Case>{
+                   C(T::Highest(), T::Highest(), Mul(T::Highest(), T::Highest())),
+                   C(T::Lowest(), T::Lowest(), Mul(T::Lowest(), T::Lowest())),
+               });
+    }
+    return r;
 }
 
 template <typename T>
 std::vector<Case> OpMulVecCases() {
-    return {
+    auto r = std::vector<Case>{
         // s * vec3 = vec3
         C(Val(T{2.0}), Vec(T{1.25}, T{2.25}, T{3.25}), Vec(T{2.5}, T{4.5}, T{6.5})),
         // vec3 * s = vec3
@@ -208,11 +277,30 @@
         // vec3 * vec3 = vec3
         C(Vec(T{1.25}, T{2.25}, T{3.25}), Vec(T{2.0}, T{2.0}, T{2.0}), Vec(T{2.5}, T{4.5}, T{6.5})),
     };
+    if constexpr (IsAbstract<T> || IsFloatingPoint<T>) {
+        auto error_msg = [](auto a, auto b) {
+            return "12:34 error: " + OverflowErrorMessage(a, "*", b);
+        };
+        ConcatInto(  //
+            r,
+            std::vector<Case>{
+                // Fail if result is +/-inf
+                E(Val(T::Highest()), Vec(T{2}, T{1}), error_msg(T::Highest(), T{2})),
+                E(Val(T::Lowest()), Vec(Negate(T{2}), T{1}), error_msg(T::Lowest(), Negate(T{2}))),
+            });
+    } else {
+        ConcatInto(  //
+            r, std::vector<Case>{
+                   C(Val(T::Highest()), Vec(T{2}, T{1}), Vec(T{-2}, T::Highest())),
+                   C(Val(T::Lowest()), Vec(Negate(T{2}), T{1}), Vec(T{0}, T{T::Lowest()})),
+               });
+    }
+    return r;
 }
 
 template <typename T>
 std::vector<Case> OpMulMatCases() {
-    return {
+    auto r = std::vector<Case>{
         // s * mat3x2 = mat3x2
         C(Val(T{2.25}),
           Mat({T{1.0}, T{4.0}},  //
@@ -248,6 +336,68 @@
           Mat({T{24.25}, T{31.0}},           //
               {T{51.25}, T{67.0}})),         //
     };
+    auto error_msg = [](auto a, const char* op, auto b) {
+        return "12:34 error: " + OverflowErrorMessage(a, op, b);
+    };
+    ConcatIntoIf<IsAbstract<T> || IsFloatingPoint<T>>(  //
+        r, std::vector<Case>{
+               // vector-matrix multiply
+
+               // Overflow from first multiplication of dot product of vector and matrix column 0
+               // i.e. (v[0] * m[0][0] + v[1] * m[0][1])
+               //            ^
+               E(Vec(T::Highest(), T{1.0}),  //
+                 Mat({T{2.0}, T{1.0}},       //
+                     {T{1.0}, T{1.0}}),      //
+                 error_msg(T{2}, "*", T::Highest())),
+
+               // Overflow from second multiplication of dot product of vector and matrix column 0
+               // i.e. (v[0] * m[0][0] + v[1] * m[0][1])
+               //                             ^
+               E(Vec(T{1.0}, T::Highest()),  //
+                 Mat({T{1.0}, T{2.0}},       //
+                     {T{1.0}, T{1.0}}),      //
+                 error_msg(T{2}, "*", T::Highest())),
+
+               // Overflow from addition of dot product of vector and matrix column 0
+               // i.e. (v[0] * m[0][0] + v[1] * m[0][1])
+               //                      ^
+               E(Vec(T::Highest(), T::Highest()),  //
+                 Mat({T{1.0}, T{1.0}},             //
+                     {T{1.0}, T{1.0}}),            //
+                 error_msg(T::Highest(), "+", T::Highest())),
+
+               // matrix-matrix multiply
+
+               // Overflow from first multiplication of dot product of lhs row 0 and rhs column 0
+               // i.e. m1[0][0] * m2[0][0] + m1[0][1] * m[1][0]
+               //               ^
+               E(Mat({T::Highest(), T{1.0}},  //
+                     {T{1.0}, T{1.0}}),       //
+                 Mat({T{2.0}, T{1.0}},        //
+                     {T{1.0}, T{1.0}}),       //
+                 error_msg(T::Highest(), "*", T{2.0})),
+
+               // Overflow from second multiplication of dot product of lhs row 0 and rhs column 0
+               // i.e. m1[0][0] * m2[0][0] + m1[0][1] * m[1][0]
+               //                                     ^
+               E(Mat({T{1.0}, T{1.0}},         //
+                     {T::Highest(), T{1.0}}),  //
+                 Mat({T{1.0}, T{2.0}},         //
+                     {T{1.0}, T{1.0}}),        //
+                 error_msg(T::Highest(), "*", T{2.0})),
+
+               // Overflow from addition of dot product of lhs row 0 and rhs column 0
+               // i.e. m1[0][0] * m2[0][0] + m1[0][1] * m[1][0]
+               //                          ^
+               E(Mat({T::Highest(), T{1.0}},   //
+                     {T::Highest(), T{1.0}}),  //
+                 Mat({T{1.0}, T{1.0}},         //
+                     {T{1.0}, T{1.0}}),        //
+                 error_msg(T::Highest(), "+", T::Highest())),
+           });
+
+    return r;
 }
 
 INSTANTIATE_TEST_SUITE_P(Mul,
@@ -285,22 +435,34 @@
         C(Val(T{0}), Val(T::Highest()), Val(T{0})),
         C(Val(T{0}), Val(T::Lowest()), Val(T{0})),
     };
-    ConcatIntoIf<IsIntegral<T>>(  //
+    ConcatIntoIf<!IsAbstract<T> && IsIntegral<T>>(  //
         r, std::vector<Case>{
                // e1, when e2 is zero.
-               C(T{123}, T{0}, T{123}, true),
+               C(T{123}, T{0}, T{123}),
            });
-    ConcatIntoIf<IsSignedIntegral<T>>(  //
+    ConcatIntoIf<!IsAbstract<T> && IsSignedIntegral<T>>(  //
         r, std::vector<Case>{
                // e1, when e1 is the most negative value in T, and e2 is -1.
-               C(T::Smallest(), T{-1}, T::Smallest(), true),
+               C(T::Smallest(), T{-1}, T::Smallest()),
+           });
+
+    auto error_msg = [](auto a, auto b) {
+        return "12:34 error: " + OverflowErrorMessage(a, "/", b);
+    };
+    ConcatIntoIf<IsAbstract<T>>(  //
+        r, std::vector<Case>{
+               // Most negative value divided by -1
+               E(AInt::Lowest(), -1_a, error_msg(AInt::Lowest(), -1_a)),
            });
     return r;
 }
 
 template <typename T>
 std::vector<Case> OpDivFloatCases() {
-    return {
+    auto error_msg = [](auto a, auto b) {
+        return "12:34 error: " + OverflowErrorMessage(a, "/", b);
+    };
+    std::vector<Case> r = {
         C(Val(T{0}), Val(T{1}), Val(T{0})),
         C(Val(T{1}), Val(T{1}), Val(T{1})),
         C(Val(T{1}), Val(T{1}), Val(T{1})),
@@ -311,11 +473,14 @@
         C(Val(T::Highest()), Val(T::Highest()), Val(T{1})),
         C(Val(T{0}), Val(T::Highest()), Val(T{0})),
         C(Val(T{0}), Val(T::Lowest()), Val(-T{0})),
-        C(T{123}, T{0}, T::Inf(), true),
-        C(T{-123}, -T{0}, T::Inf(), true),
-        C(T{-123}, T{0}, -T::Inf(), true),
-        C(T{123}, -T{0}, -T::Inf(), true),
+
+        // Divide by zero
+        E(T{123}, T{0}, error_msg(T{123}, T{0})),
+        E(Negate(T{123}), Negate(T{0}), error_msg(Negate(T{123}), Negate(T{0}))),
+        E(Negate(T{123}), T{0}, error_msg(Negate(T{123}), T{0})),
+        E(T{123}, Negate(T{0}), error_msg(T{123}, Negate(T{0}))),
     };
+    return r;
 }
 INSTANTIATE_TEST_SUITE_P(Div,
                          ResolverConstEvalBinaryOpTest,
@@ -653,8 +818,8 @@
                C(Negate(T{0}), ST{u32::Highest()}, Negate(T{0})),  //
            });
 
-    // Cases that are fine for signed values (no sign change), but would overflow unsigned values.
-    // See ResolverConstEvalBinaryOpTest_Overflow for negative tests.
+    // Cases that are fine for signed values (no sign change), but would overflow
+    // unsigned values. See below for negative tests.
     ConcatIntoIf<IsSignedIntegral<T>>(  //
         r, std::vector<Case>{
                C(B::TwoLeftMost, ST{1}, B::LeftMost),      //
@@ -678,6 +843,39 @@
                C(B::AllButLeftMost, ST{1}, B::AllButRightMost),
            });
 
+    auto error_msg = [](auto a, auto b) {
+        return "12:34 error: " + OverflowErrorMessage(a, "<<", b);
+    };
+    ConcatIntoIf<IsAbstract<T>>(  //
+        r, std::vector<Case>{
+               // ShiftLeft of AInts that result in values not representable as AInts.
+               // Note that for i32/u32, these would error because shift value is larger than 32.
+               E(B::All, T{B::NumBits}, error_msg(B::All, T{B::NumBits})),
+               E(B::RightMost, T{B::NumBits}, error_msg(B::RightMost, T{B::NumBits})),
+               E(B::AllButLeftMost, T{B::NumBits}, error_msg(B::AllButLeftMost, T{B::NumBits})),
+               E(B::AllButLeftMost, T{B::NumBits + 1},
+                 error_msg(B::AllButLeftMost, T{B::NumBits + 1})),
+               E(B::AllButLeftMost, T{B::NumBits + 1000},
+                 error_msg(B::AllButLeftMost, T{B::NumBits + 1000})),
+           });
+    ConcatIntoIf<IsUnsignedIntegral<T>>(  //
+        r, std::vector<Case>{
+               // ShiftLeft of u32s that overflow (non-zero bits get shifted out)
+               E(T{0b00010}, T{31}, error_msg(T{0b00010}, T{31})),
+               E(T{0b00100}, T{30}, error_msg(T{0b00100}, T{30})),
+               E(T{0b01000}, T{29}, error_msg(T{0b01000}, T{29})),
+               E(T{0b10000}, T{28}, error_msg(T{0b10000}, T{28})),
+               //...
+               E(T{1 << 28}, T{4}, error_msg(T{1 << 28}, T{4})),
+               E(T{1 << 29}, T{3}, error_msg(T{1 << 29}, T{3})),
+               E(T{1 << 30}, T{2}, error_msg(T{1 << 30}, T{2})),
+               E(T{1u << 31}, T{1}, error_msg(T{1u << 31}, T{1})),
+
+               // And some more
+               E(B::All, T{1}, error_msg(B::All, T{1})),
+               E(B::AllButLeftMost, T{2}, error_msg(B::AllButLeftMost, T{2})),
+           });
+
     return r;
 }
 INSTANTIATE_TEST_SUITE_P(ShiftLeft,
@@ -688,153 +886,6 @@
                                                       ShiftLeftCases<i32>(),   //
                                                       ShiftLeftCases<u32>()))));
 
-// Tests for errors on overflow/underflow of binary operations with abstract numbers
-struct OverflowCase {
-    ast::BinaryOp op;
-    Types lhs;
-    Types rhs;
-};
-
-static std::ostream& operator<<(std::ostream& o, const OverflowCase& c) {
-    o << ast::FriendlyName(c.op) << ", lhs: " << c.lhs << ", rhs: " << c.rhs;
-    return o;
-}
-using ResolverConstEvalBinaryOpTest_Overflow = ResolverTestWithParam<OverflowCase>;
-TEST_P(ResolverConstEvalBinaryOpTest_Overflow, Test) {
-    Enable(ast::Extension::kF16);
-    auto& c = GetParam();
-    auto* lhs = ToValueBase(c.lhs);
-    auto* rhs = ToValueBase(c.rhs);
-    auto* lhs_expr = lhs->Expr(*this);
-    auto* rhs_expr = rhs->Expr(*this);
-    auto* expr = create<ast::BinaryExpression>(Source{{1, 1}}, c.op, lhs_expr, rhs_expr);
-    GlobalConst("C", expr);
-    ASSERT_FALSE(r()->Resolve());
-    EXPECT_THAT(r()->error(), HasSubstr("1:1 error: '"));
-    EXPECT_THAT(r()->error(), HasSubstr("' cannot be represented as '" + lhs->TypeName() + "'"));
-}
-INSTANTIATE_TEST_SUITE_P(
-    Test,
-    ResolverConstEvalBinaryOpTest_Overflow,
-    testing::Values(
-
-        // scalar-scalar add
-        OverflowCase{ast::BinaryOp::kAdd, Val(AInt::Highest()), Val(1_a)},
-        OverflowCase{ast::BinaryOp::kAdd, Val(AInt::Lowest()), Val(-1_a)},
-        OverflowCase{ast::BinaryOp::kAdd, Val(AFloat::Highest()), Val(AFloat::Highest())},
-        OverflowCase{ast::BinaryOp::kAdd, Val(AFloat::Lowest()), Val(AFloat::Lowest())},
-        // scalar-scalar subtract
-        OverflowCase{ast::BinaryOp::kSubtract, Val(AInt::Lowest()), Val(1_a)},
-        OverflowCase{ast::BinaryOp::kSubtract, Val(AInt::Highest()), Val(-1_a)},
-        OverflowCase{ast::BinaryOp::kSubtract, Val(AFloat::Highest()), Val(AFloat::Lowest())},
-        OverflowCase{ast::BinaryOp::kSubtract, Val(AFloat::Lowest()), Val(AFloat::Highest())},
-
-        // scalar-scalar multiply
-        OverflowCase{ast::BinaryOp::kMultiply, Val(AInt::Highest()), Val(2_a)},
-        OverflowCase{ast::BinaryOp::kMultiply, Val(AInt::Lowest()), Val(-2_a)},
-
-        // scalar-vector multiply
-        OverflowCase{ast::BinaryOp::kMultiply, Val(AInt::Highest()), Vec(2_a, 1_a)},
-        OverflowCase{ast::BinaryOp::kMultiply, Val(AInt::Lowest()), Vec(-2_a, 1_a)},
-
-        // vector-matrix multiply
-
-        // Overflow from first multiplication of dot product of vector and matrix column 0
-        // i.e. (v[0] * m[0][0] + v[1] * m[0][1])
-        //            ^
-        OverflowCase{ast::BinaryOp::kMultiply,       //
-                     Vec(AFloat::Highest(), 1.0_a),  //
-                     Mat({2.0_a, 1.0_a},             //
-                         {1.0_a, 1.0_a})},
-
-        // Overflow from second multiplication of dot product of vector and matrix column 0
-        // i.e. (v[0] * m[0][0] + v[1] * m[0][1])
-        //                             ^
-        OverflowCase{ast::BinaryOp::kMultiply,       //
-                     Vec(1.0_a, AFloat::Highest()),  //
-                     Mat({1.0_a, 2.0_a},             //
-                         {1.0_a, 1.0_a})},
-
-        // Overflow from addition of dot product of vector and matrix column 0
-        // i.e. (v[0] * m[0][0] + v[1] * m[0][1])
-        //                      ^
-        OverflowCase{ast::BinaryOp::kMultiply,                   //
-                     Vec(AFloat::Highest(), AFloat::Highest()),  //
-                     Mat({1.0_a, 1.0_a},                         //
-                         {1.0_a, 1.0_a})},
-
-        // matrix-matrix multiply
-
-        // Overflow from first multiplication of dot product of lhs row 0 and rhs column 0
-        // i.e. m1[0][0] * m2[0][0] + m1[0][1] * m[1][0]
-        //               ^
-        OverflowCase{ast::BinaryOp::kMultiply,        //
-                     Mat({AFloat::Highest(), 1.0_a},  //
-                         {1.0_a, 1.0_a}),             //
-                     Mat({2.0_a, 1.0_a},              //
-                         {1.0_a, 1.0_a})},
-
-        // Overflow from second multiplication of dot product of lhs row 0 and rhs column 0
-        // i.e. m1[0][0] * m2[0][0] + m1[0][1] * m[1][0]
-        //                                     ^
-        OverflowCase{ast::BinaryOp::kMultiply,        //
-                     Mat({1.0_a, AFloat::Highest()},  //
-                         {1.0_a, 1.0_a}),             //
-                     Mat({1.0_a, 1.0_a},              //
-                         {2.0_a, 1.0_a})},
-
-        // Overflow from addition of dot product of lhs row 0 and rhs column 0
-        // i.e. m1[0][0] * m2[0][0] + m1[0][1] * m[1][0]
-        //                          ^
-        OverflowCase{ast::BinaryOp::kMultiply,         //
-                     Mat({AFloat::Highest(), 1.0_a},   //
-                         {AFloat::Highest(), 1.0_a}),  //
-                     Mat({1.0_a, 1.0_a},               //
-                         {1.0_a, 1.0_a})},
-
-        // Divide by zero
-        OverflowCase{ast::BinaryOp::kDivide, Val(123_a), Val(0_a)},
-        OverflowCase{ast::BinaryOp::kDivide, Val(-123_a), Val(-0_a)},
-        OverflowCase{ast::BinaryOp::kDivide, Val(-123_a), Val(0_a)},
-        OverflowCase{ast::BinaryOp::kDivide, Val(123_a), Val(-0_a)},
-
-        // Most negative value divided by -1
-        OverflowCase{ast::BinaryOp::kDivide, Val(AInt::Lowest()), Val(-1_a)},
-
-        // ShiftLeft of AInts that result in values not representable as AInts.
-        // Note that for i32/u32, these would error because shift value is larger than 32.
-        OverflowCase{ast::BinaryOp::kShiftLeft,                   //
-                     Val(AInt{BitValues<AInt>::All}),             //
-                     Val(AInt{BitValues<AInt>::NumBits})},        //
-        OverflowCase{ast::BinaryOp::kShiftLeft,                   //
-                     Val(AInt{BitValues<AInt>::RightMost}),       //
-                     Val(AInt{BitValues<AInt>::NumBits})},        //
-        OverflowCase{ast::BinaryOp::kShiftLeft,                   //
-                     Val(AInt{BitValues<AInt>::AllButLeftMost}),  //
-                     Val(AInt{BitValues<AInt>::NumBits})},        //
-        OverflowCase{ast::BinaryOp::kShiftLeft,                   //
-                     Val(AInt{BitValues<AInt>::AllButLeftMost}),  //
-                     Val(AInt{BitValues<AInt>::NumBits + 1})},    //
-        OverflowCase{ast::BinaryOp::kShiftLeft,                   //
-                     Val(AInt{BitValues<AInt>::AllButLeftMost}),  //
-                     Val(AInt{BitValues<AInt>::NumBits + 1000})},
-
-        // ShiftLeft of u32s that overflow (non-zero bits get shifted out)
-        OverflowCase{ast::BinaryOp::kShiftLeft, Val(0b00010_u), Val(31_u)},
-        OverflowCase{ast::BinaryOp::kShiftLeft, Val(0b00100_u), Val(30_u)},
-        OverflowCase{ast::BinaryOp::kShiftLeft, Val(0b01000_u), Val(29_u)},
-        OverflowCase{ast::BinaryOp::kShiftLeft, Val(0b10000_u), Val(28_u)},
-        // ...
-        OverflowCase{ast::BinaryOp::kShiftLeft, Val(u32(1u << 28)), Val(4_u)},
-        OverflowCase{ast::BinaryOp::kShiftLeft, Val(u32(1u << 29)), Val(3_u)},
-        OverflowCase{ast::BinaryOp::kShiftLeft, Val(u32(1u << 30)), Val(2_u)},
-        OverflowCase{ast::BinaryOp::kShiftLeft, Val(u32(1u << 31)), Val(1_u)},
-        // And some more
-        OverflowCase{ast::BinaryOp::kShiftLeft, Val(BitValues<u32>::All), Val(1_u)},
-        OverflowCase{ast::BinaryOp::kShiftLeft, Val(BitValues<u32>::AllButLeftMost), Val(2_u)}
-
-        ));
-
 TEST_F(ResolverConstEvalTest, BinaryAbstractAddOverflow_AInt) {
     GlobalConst("c", Add(Source{{1, 1}}, Expr(AInt::Highest()), 1_a));
     EXPECT_FALSE(r()->Resolve());
@@ -853,15 +904,16 @@
     GlobalConst("c", Add(Source{{1, 1}}, Expr(AFloat::Highest()), AFloat::Highest()));
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "1:1 error: '1.7976931348623157081e+308 + 1.7976931348623157081e+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) {
     GlobalConst("c", Add(Source{{1, 1}}, Expr(AFloat::Lowest()), AFloat::Lowest()));
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(
-        r()->error(),
-        "1:1 error: '-1.7976931348623157081e+308 + -1.7976931348623157081e+308' cannot be represented as 'abstract-float'");
+    EXPECT_EQ(r()->error(),
+              "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 fddca38..eccd711 100644
--- a/src/tint/resolver/const_eval_builtin_test.cc
+++ b/src/tint/resolver/const_eval_builtin_test.cc
@@ -26,8 +26,9 @@
 using resolver::operator<<;
 
 struct Case {
-    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, utils::VectorRef<Types> expected_values)
+        : args(std::move(in_args)),
+          expected(Success{std::move(expected_values), CheckConstantFlags{}}) {}
 
     Case(utils::VectorRef<Types> in_args, std::string expected_err)
         : args(std::move(in_args)), expected(Failure{std::move(expected_err)}) {}
@@ -35,26 +36,27 @@
     /// Expected value may be positive or negative
     Case& PosOrNeg() {
         Success s = expected.Get();
-        s.pos_or_neg = true;
+        s.flags.pos_or_neg = true;
         expected = s;
         return *this;
     }
 
-    /// Expected value should be compared using FLOAT_EQ instead of EQ
-    Case& FloatComp() {
+    /// Expected value should be compared using EXPECT_FLOAT_EQ instead of EXPECT_EQ.
+    /// If optional epsilon is passed in, will be compared using EXPECT_NEAR with that epsilon.
+    Case& FloatComp(std::optional<double> epsilon = {}) {
         Success s = expected.Get();
-        s.float_compare = true;
+        s.flags.float_compare = true;
+        s.flags.float_compare_epsilon = epsilon;
         expected = s;
         return *this;
     }
 
     struct Success {
-        Types value;
-        bool pos_or_neg = false;
-        bool float_compare = false;
+        utils::Vector<Types, 2> values;
+        CheckConstantFlags flags;
     };
     struct Failure {
-        std::string error = nullptr;
+        std::string error;
     };
 
     utils::Vector<Types, 8> args;
@@ -69,7 +71,20 @@
     o << "expected: ";
     if (c.expected) {
         auto s = c.expected.Get();
-        o << s.value << ", pos_or_neg: " << s.pos_or_neg;
+        if (s.values.Length() == 1) {
+            o << s.values[0];
+        } else {
+            o << "[";
+            for (auto& v : s.values) {
+                if (&v != &s.values[0]) {
+                    o << ", ";
+                }
+                o << v;
+            }
+            o << "]";
+        }
+        o << ", pos_or_neg: " << s.flags.pos_or_neg;
+        o << ", float_compare: " << s.flags.float_compare;
     } else {
         o << "[ERROR: " << c.expected.Failure().error << "]";
     }
@@ -80,7 +95,12 @@
 
 /// 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)};
+    return Case{utils::Vector<Types, 8>{args}, utils::Vector<Types, 2>{std::move(result)}};
+}
+
+/// Creates a Case with Values for args and result
+static Case C(std::initializer_list<Types> args, std::initializer_list<Types> results) {
+    return Case{utils::Vector<Types, 8>{args}, utils::Vector<Types, 2>{results}};
 }
 
 /// Convenience overload that creates a Case with just scalars
@@ -91,7 +111,21 @@
     }
     Types result = Val(0_a);
     std::visit([&](auto&& v) { result = Val(v); }, sresult);
-    return Case{std::move(args), std::move(result)};
+    return Case{std::move(args), utils::Vector<Types, 2>{std::move(result)}};
+}
+
+/// Creates a Case with Values for args and result
+static Case C(std::initializer_list<ScalarTypes> sargs,
+              std::initializer_list<ScalarTypes> sresults) {
+    utils::Vector<Types, 8> args;
+    for (auto& sa : sargs) {
+        std::visit([&](auto&& v) { return args.Push(Val(v)); }, sa);
+    }
+    utils::Vector<Types, 2> results;
+    for (auto& sa : sresults) {
+        std::visit([&](auto&& v) { return results.Push(Val(v)); }, sa);
+    }
+    return Case{std::move(args), std::move(results)};
 }
 
 /// Creates a Case with Values for args and expected error
@@ -108,16 +142,6 @@
     return Case{std::move(args), std::move(err)};
 }
 
-/// Returns the overflow error message for binary ops
-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();
-}
-
 using ResolverConstEvalBuiltinTest = ResolverTestWithParam<std::tuple<sem::BuiltinType, Case>>;
 
 TEST_P(ResolverConstEvalBuiltinTest, Test) {
@@ -132,14 +156,10 @@
     }
 
     auto* expr = Call(Source{{12, 34}}, sem::str(builtin), std::move(args));
-
     GlobalConst("C", expr);
 
     if (c.expected) {
-        auto expected = c.expected.Get();
-
-        auto* expected_expr = ToValueBase(expected.value)->Expr(*this);
-        GlobalConst("E", expected_expr);
+        auto expected_case = c.expected.Get();
 
         ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -149,43 +169,19 @@
         ASSERT_NE(value, nullptr);
         EXPECT_TYPE(value->Type(), sem->Type());
 
-        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 {
-                            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>());
-                    }
-                },
-                expected.value);
-
-            return HasFailure() ? Action::kStop : Action::kContinue;
-        });
+        if (value->Type()->Is<sem::Struct>()) {
+            // The result type of the constant-evaluated expression is a structure.
+            // Compare each of the fields individually.
+            for (size_t i = 0; i < expected_case.values.Length(); i++) {
+                CheckConstant(value->Index(i), ToValueBase(expected_case.values[i]),
+                              expected_case.flags);
+            }
+        } else {
+            // Return type is not a structure. Just compare the single value
+            ASSERT_EQ(expected_case.values.Length(), 1u)
+                << "const-eval returned non-struct, but Case expected multiple values";
+            CheckConstant(value, ToValueBase(expected_case.values[0]), expected_case.flags);
+        }
     } else {
         EXPECT_FALSE(r()->Resolve());
         EXPECT_EQ(r()->error(), c.expected.Failure().error);
@@ -201,7 +197,7 @@
                          C({1.0_a, 0_a}, kPiOver2<AFloat>),
                      })));
 
-template <typename T, bool finite_only>
+template <typename T>
 std::vector<Case> AbsCases() {
     std::vector<Case> cases = {
         C({T(0)}, T(0)),
@@ -211,7 +207,6 @@
         // Vector tests
         C({Vec(T(2.0), T::Highest())}, Vec(T(2.0), T::Highest())),
     };
-
     ConcatIntoIf<IsSignedIntegral<T>>(
         cases,
         std::vector<Case>{
@@ -226,27 +221,18 @@
             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>()))));
+                     testing::ValuesIn(Concat(AbsCases<AInt>(),  //
+                                              AbsCases<i32>(),
+                                              AbsCases<u32>(),
+                                              AbsCases<AFloat>(),
+                                              AbsCases<f32>(),
+                                              AbsCases<f16>()))));
 
 static std::vector<Case> AllCases() {
     return {
@@ -306,9 +292,9 @@
     ResolverConstEvalBuiltinTest,
     testing::Combine(testing::Values(sem::BuiltinType::kAny), testing::ValuesIn(AnyCases())));
 
-template <typename T, bool finite_only>
+template <typename T>
 std::vector<Case> Atan2Cases() {
-    std::vector<Case> cases = {
+    return {
         // If y is +/-0 and x is negative or -0, +/-PI is returned
         C({T(0.0), -T(0.0)}, kPi<T>).PosOrNeg().FloatComp(),
 
@@ -329,59 +315,18 @@
             .FloatComp(),
         C({Vec(T(1.0), T(1.0)), Vec(T(0.0), -T(0.0))}, Vec(kPiOver2<T>, kPiOver2<T>)).FloatComp(),
     };
-
-    ConcatIntoIf<!finite_only>(  //
-        cases, std::vector<Case>{
-                   // If y is +/-INF and x is finite, +/-PI/2 is returned
-                   C({T::Inf(), T(0.0)}, kPiOver2<T>).PosOrNeg().FloatComp(),
-                   C({-T::Inf(), T(0.0)}, kPiOver2<T>).PosOrNeg().FloatComp(),
-
-                   // If y is +/-INF and x is -INF, +/-3PI/4 is returned
-                   C({T::Inf(), -T::Inf()}, k3PiOver4<T>).PosOrNeg().FloatComp(),
-                   C({-T::Inf(), -T::Inf()}, k3PiOver4<T>).PosOrNeg().FloatComp(),
-
-                   // If y is +/-INF and x is +INF, +/-PI/4 is returned
-                   C({T::Inf(), T::Inf()}, kPiOver4<T>).PosOrNeg().FloatComp(),
-                   C({-T::Inf(), T::Inf()}, kPiOver4<T>).PosOrNeg().FloatComp(),
-
-                   // If x is -INF and y is finite and positive, +PI is returned
-                   C({T(0.0), -T::Inf()}, kPi<T>).FloatComp(),
-
-                   // If x is -INF and y is finite and negative, -PI is returned
-                   C({-T(0.0), -T::Inf()}, -kPi<T>).FloatComp(),
-
-                   // If x is +INF and y is finite and positive, +0 is returned
-                   C({T(0.0), T::Inf()}, T(0.0)),
-
-                   // If x is +INF and y is finite and negative, -0 is returned
-                   C({-T(0.0), T::Inf()}, -T(0.0)),
-
-                   // If either x is NaN or y is NaN, NaN is returned
-                   C({T::NaN(), T(0.0)}, T::NaN()),
-                   C({T(0.0), T::NaN()}, T::NaN()),
-                   C({T::NaN(), T::NaN()}, T::NaN()),
-
-                   // Vector tests
-                   C({Vec(T::Inf(), -T::Inf(), T::Inf(), -T::Inf()),  //
-                      Vec(T(0.0), T(0.0), -T::Inf(), -T::Inf())},     //
-                     Vec(kPiOver2<T>, kPiOver2<T>, k3PiOver4<T>, k3PiOver4<T>))
-                       .PosOrNeg()
-                       .FloatComp(),
-               });
-
-    return cases;
 }
 INSTANTIATE_TEST_SUITE_P(  //
     Atan2,
     ResolverConstEvalBuiltinTest,
     testing::Combine(testing::Values(sem::BuiltinType::kAtan2),
-                     testing::ValuesIn(Concat(Atan2Cases<AFloat, true>(),  //
-                                              Atan2Cases<f32, false>(),
-                                              Atan2Cases<f16, false>()))));
+                     testing::ValuesIn(Concat(Atan2Cases<AFloat>(),  //
+                                              Atan2Cases<f32>(),
+                                              Atan2Cases<f16>()))));
 
-template <typename T, bool finite_only>
+template <typename T>
 std::vector<Case> AtanCases() {
-    std::vector<Case> cases = {
+    return {
         C({T(1.0)}, kPiOver4<T>).FloatComp(),
         C({-T(1.0)}, -kPiOver4<T>).FloatComp(),
 
@@ -391,35 +336,18 @@
         // Vector tests
         C({Vec(T(0.0), T(1.0), -T(1.0))}, Vec(T(0.0), kPiOver4<T>, -kPiOver4<T>)).FloatComp(),
     };
-
-    ConcatIntoIf<!finite_only>(  //
-        cases, std::vector<Case>{
-                   // If i is +/-INF, +/-PI/2 is returned
-                   C({T::Inf()}, kPiOver2<T>).PosOrNeg().FloatComp(),
-                   C({-T::Inf()}, -kPiOver2<T>).FloatComp(),
-
-                   // If i is NaN, NaN is returned
-                   C({T::NaN()}, T::NaN()),
-
-                   // Vector tests
-                   C({Vec(T::Inf(), -T::Inf(), T::Inf(), -T::Inf())},  //
-                     Vec(kPiOver2<T>, -kPiOver2<T>, kPiOver2<T>, -kPiOver2<T>))
-                       .FloatComp(),
-               });
-
-    return cases;
 }
 INSTANTIATE_TEST_SUITE_P(  //
     Atan,
     ResolverConstEvalBuiltinTest,
     testing::Combine(testing::Values(sem::BuiltinType::kAtan),
-                     testing::ValuesIn(Concat(AtanCases<AFloat, true>(),  //
-                                              AtanCases<f32, false>(),
-                                              AtanCases<f16, false>()))));
+                     testing::ValuesIn(Concat(AtanCases<AFloat>(),  //
+                                              AtanCases<f32>(),
+                                              AtanCases<f16>()))));
 
-template <typename T, bool finite_only>
+template <typename T>
 std::vector<Case> AtanhCases() {
-    std::vector<Case> cases = {
+    return {
         // If i is +/-0, +/-0 is returned
         C({T(0.0)}, T(0.0)).PosOrNeg(),
 
@@ -427,43 +355,24 @@
 
         // Vector tests
         C({Vec(T(0.0), T(0.9), -T(0.9))}, Vec(T(0.0), T(1.4722193), -T(1.4722193))).FloatComp(),
+
+        E({T(1.1)},
+          "12:34 error: atanh must be called with a value in the range (-1 .. 1) (exclusive)"),
+        E({-T(1.1)},
+          "12:34 error: atanh must be called with a value in the range (-1 .. 1) (exclusive)"),
     };
-
-    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
-                   C({T::NaN()}, T::NaN()),
-
-                   // Vector tests
-                   C({Vec(T::NaN(), T::NaN())}, Vec(T::NaN(), T::NaN())).FloatComp(),
-               });
-
-    return cases;
 }
 INSTANTIATE_TEST_SUITE_P(  //
     Atanh,
     ResolverConstEvalBuiltinTest,
     testing::Combine(testing::Values(sem::BuiltinType::kAtanh),
-                     testing::ValuesIn(Concat(AtanhCases<AFloat, true>(),  //
-                                              AtanhCases<f32, false>(),
-                                              AtanhCases<f16, false>()))));
+                     testing::ValuesIn(Concat(AtanhCases<AFloat>(),  //
+                                              AtanhCases<f32>(),
+                                              AtanhCases<f16>()))));
 
-template <typename T, bool finite_only>
+template <typename T>
 std::vector<Case> AcosCases() {
-    std::vector<Case> cases = {
+    return {
         // If i is +/-0, +/-0 is returned
         C({T(0.87758256189)}, T(0.5)).FloatComp(),
 
@@ -472,43 +381,46 @@
 
         // Vector tests
         C({Vec(T(1.0), -T(1.0))}, Vec(T(0), kPi<T>)).FloatComp(),
+
+        E({T(1.1)},
+          "12:34 error: acos must be called with a value in the range [-1 .. 1] (inclusive)"),
+        E({-T(1.1)},
+          "12:34 error: acos must be called with a value in the range [-1 .. 1] (inclusive)"),
     };
-
-    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;
 }
 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>()))));
+                     testing::ValuesIn(Concat(AcosCases<AFloat>(),  //
+                                              AcosCases<f32>(),
+                                              AcosCases<f16>()))));
 
-template <typename T, bool finite_only>
+template <typename T>
+std::vector<Case> AcoshCases() {
+    return {
+        C({T(1.0)}, T(0.0)),
+        C({T(11.5919532755)}, kPi<T>).FloatComp(),
+
+        // Vector tests
+        C({Vec(T(1.0), T(11.5919532755))}, Vec(T(0), kPi<T>)).FloatComp(),
+
+        E({T::Smallest()}, "12:34 error: acosh must be called with a value >= 1.0"),
+        E({-T(1.1)}, "12:34 error: acosh must be called with a value >= 1.0"),
+        E({T(0)}, "12:34 error: acosh must be called with a value >= 1.0"),
+    };
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    Acosh,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kAcosh),
+                     testing::ValuesIn(Concat(AcoshCases<AFloat>(),  //
+                                              AcoshCases<f32>(),
+                                              AcoshCases<f16>()))));
+
+template <typename T>
 std::vector<Case> AsinCases() {
-    std::vector<Case> cases = {
+    return {
         // If i is +/-0, +/-0 is returned
         C({T(0.0)}, T(0.0)),
         C({-T(0.0)}, -T(0.0)),
@@ -518,43 +430,24 @@
 
         // Vector tests
         C({Vec(T(0.0), T(1.0), -T(1.0))}, Vec(T(0.0), kPiOver2<T>, -kPiOver2<T>)).FloatComp(),
+
+        E({T(1.1)},
+          "12:34 error: asin must be called with a value in the range [-1 .. 1] (inclusive)"),
+        E({-T(1.1)},
+          "12:34 error: asin must be called with a value in the range [-1 .. 1] (inclusive)"),
     };
-
-    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
-                   C({T::NaN()}, T::NaN()),
-
-                   // Vector tests
-                   C({Vec(T::NaN(), T::NaN())}, Vec(T::NaN(), T::NaN())).FloatComp(),
-               });
-
-    return cases;
 }
 INSTANTIATE_TEST_SUITE_P(  //
     Asin,
     ResolverConstEvalBuiltinTest,
     testing::Combine(testing::Values(sem::BuiltinType::kAsin),
-                     testing::ValuesIn(Concat(AsinCases<AFloat, true>(),  //
-                                              AsinCases<f32, false>(),
-                                              AsinCases<f16, false>()))));
+                     testing::ValuesIn(Concat(AsinCases<AFloat>(),  //
+                                              AsinCases<f32>(),
+                                              AsinCases<f16>()))));
 
-template <typename T, bool finite_only>
+template <typename T>
 std::vector<Case> AsinhCases() {
-    std::vector<Case> cases = {
+    return {
         // If i is +/-0, +/-0 is returned
         C({T(0.0)}, T(0.0)),
         C({-T(0.0)}, -T(0.0)),
@@ -567,34 +460,18 @@
           Vec(T(0.0), T(0.8088669356278), -T(1.4436354751788)))
             .FloatComp(),
     };
-
-    ConcatIntoIf<!finite_only>(  //
-        cases, std::vector<Case>{
-                   // If i is +/- INF, +/-INF is returned
-                   C({T::Inf()}, T::Inf()),
-                   C({-T::Inf()}, -T::Inf()),
-
-                   // If i is NaN, NaN is returned
-                   C({T::NaN()}, T::NaN()),
-
-                   // Vector tests
-                   C({Vec(T::Inf(), T::NaN(), -T::Inf())},  //
-                     Vec(T::Inf(), T::NaN(), -T::Inf())),
-               });
-
-    return cases;
 }
 INSTANTIATE_TEST_SUITE_P(  //
     Asinh,
     ResolverConstEvalBuiltinTest,
     testing::Combine(testing::Values(sem::BuiltinType::kAsinh),
-                     testing::ValuesIn(Concat(AsinhCases<AFloat, true>(),  //
-                                              AsinhCases<f32, false>(),
-                                              AsinhCases<f16, false>()))));
+                     testing::ValuesIn(Concat(AsinhCases<AFloat>(),  //
+                                              AsinhCases<f32>(),
+                                              AsinhCases<f16>()))));
 
-template <typename T, bool finite_only>
+template <typename T>
 std::vector<Case> CeilCases() {
-    std::vector<Case> cases = {
+    return {
         C({T(0)}, T(0)),
         C({-T(0)}, -T(0)),
         C({-T(1.5)}, -T(1.0)),
@@ -604,24 +481,13 @@
 
         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>()))));
+    testing::Combine(
+        testing::Values(sem::BuiltinType::kCeil),
+        testing::ValuesIn(Concat(CeilCases<AFloat>(), CeilCases<f32>(), CeilCases<f16>()))));
 
 template <typename T>
 std::vector<Case> ClampCases() {
@@ -659,6 +525,55 @@
                                               ClampCases<f16>()))));
 
 template <typename T>
+std::vector<Case> CosCases() {
+    std::vector<Case> cases = {
+        C({-T(0)}, T(1)),
+        C({T(0)}, T(1)),
+
+        C({T(0.75)}, T(0.7316888689)).FloatComp(),
+
+        // Vector test
+        C({Vec(T(0), -T(0), T(0.75))}, Vec(T(1), T(1), T(0.7316888689))).FloatComp(),
+    };
+
+    return cases;
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    Cos,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kCos),
+                     testing::ValuesIn(Concat(CosCases<AFloat>(),  //
+                                              CosCases<f32>(),
+                                              CosCases<f16>()))));
+
+template <typename T>
+std::vector<Case> CoshCases() {
+    auto error_msg = [](auto a) {
+        return "12:34 error: " + OverflowErrorMessage(a, FriendlyName<decltype(a)>());
+    };
+    std::vector<Case> cases = {
+        C({T(0)}, T(1)),
+        C({-T(0)}, T(1)),
+        C({T(1)}, T(1.5430806348)).FloatComp(),
+
+        C({T(.75)}, T(1.2946832847)).FloatComp(),
+
+        // Vector tests
+        C({Vec(T(0), -T(0), T(1))}, Vec(T(1), T(1), T(1.5430806348))).FloatComp(),
+
+        E({T(10000)}, error_msg(T::Inf())),
+    };
+    return cases;
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    Cosh,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kCosh),
+                     testing::ValuesIn(Concat(CoshCases<AFloat>(),  //
+                                              CoshCases<f32>(),
+                                              CoshCases<f16>()))));
+
+template <typename T>
 std::vector<Case> CountLeadingZerosCases() {
     using B = BitValues<T>;
     return {
@@ -787,7 +702,7 @@
                      testing::ValuesIn(Concat(CountOneBitsCases<i32>(),  //
                                               CountOneBitsCases<u32>()))));
 
-template <typename T, bool finite_only>
+template <typename T>
 std::vector<Case> CrossCases() {
     constexpr auto vec_x = [](T v) { return Vec(T(v), T(0), T(0)); };
     constexpr auto vec_y = [](T v) { return Vec(T(0), T(v), T(0)); };
@@ -809,12 +724,6 @@
     const auto lowest_x = vec_x(T::Lowest());
     const auto lowest_y = vec_y(T::Lowest());
     const auto lowest_z = vec_z(T::Lowest());
-    const auto inf_x = vec_x(T::Inf());
-    const auto inf_y = vec_y(T::Inf());
-    const auto inf_z = vec_z(T::Inf());
-    const auto neg_inf_x = vec_x(-T::Inf());
-    const auto neg_inf_y = vec_y(-T::Inf());
-    const auto neg_inf_z = vec_z(-T::Inf());
 
     std::vector<Case> r = {
         C({zero, zero}, zero),
@@ -863,28 +772,11 @@
           Vec(T(-10.75), T(-6.75), T(11.75))),
     };
 
-    ConcatIntoIf<!finite_only>(  //
-        r, std::vector<Case>{
-               C({highest_x, highest_y}, inf_z).PosOrNeg(),  //
-               C({highest_y, highest_x}, inf_z).PosOrNeg(),  //
-               C({highest_z, highest_x}, inf_y).PosOrNeg(),  //
-               C({highest_x, highest_z}, inf_y).PosOrNeg(),  //
-               C({highest_y, highest_z}, inf_x).PosOrNeg(),  //
-               C({highest_z, highest_y}, inf_x).PosOrNeg(),  //
-               C({lowest_x, lowest_y}, inf_z).PosOrNeg(),    //
-               C({lowest_y, lowest_x}, inf_z).PosOrNeg(),    //
-               C({lowest_z, lowest_x}, inf_y).PosOrNeg(),    //
-               C({lowest_x, lowest_z}, inf_y).PosOrNeg(),    //
-               C({lowest_y, lowest_z}, inf_x).PosOrNeg(),    //
-               C({lowest_z, lowest_y}, inf_x).PosOrNeg(),
-           });
-
     std::string pos_error_msg =
         "12:34 error: " + OverflowErrorMessage(T::Highest(), "*", T::Highest());
     std::string neg_error_msg =
         "12:34 error: " + OverflowErrorMessage(T::Lowest(), "*", T::Lowest());
-
-    ConcatIntoIf<finite_only>(  //
+    ConcatInto(  //
         r, std::vector<Case>{
                E({highest_x, highest_y}, pos_error_msg),
                E({highest_y, highest_x}, pos_error_msg),
@@ -906,10 +798,60 @@
     Cross,
     ResolverConstEvalBuiltinTest,
     testing::Combine(testing::Values(sem::BuiltinType::kCross),
-                     testing::ValuesIn(Concat(CrossCases<AFloat, true>(),  //
-                                              CrossCases<f32, false>(),
-                                              CrossCases<f32, false>(),  //
-                                              CrossCases<f16, false>()))));
+                     testing::ValuesIn(Concat(CrossCases<AFloat>(),  //
+                                              CrossCases<f32>(),     //
+                                              CrossCases<f16>()))));
+
+template <typename T>
+std::vector<Case> DotCases() {
+    auto r = std::vector<Case>{
+        C({Vec(T(0), T(0)), Vec(T(0), T(0))}, Val(T(0))),
+        C({Vec(T(0), T(0), T(0)), Vec(T(0), T(0), T(0))}, Val(T(0))),
+        C({Vec(T(0), T(0), T(0), T(0)), Vec(T(0), T(0), T(0), T(0))}, Val(T(0))),
+        C({Vec(T(1), T(2), T(3), T(4)), Vec(T(5), T(6), T(7), T(8))}, Val(T(70))),
+
+        C({Vec(T(1), T(1)), Vec(T(1), T(1))}, Val(T(2))),
+        C({Vec(T(1), T(2)), Vec(T(2), T(1))}, Val(T(4))),
+        C({Vec(T(2), T(2)), Vec(T(2), T(2))}, Val(T(8))),
+
+        C({Vec(T::Highest(), T::Highest()), Vec(T(1), T(0))}, Val(T::Highest())),
+        C({Vec(T::Lowest(), T::Lowest()), Vec(T(1), T(0))}, Val(T::Lowest())),
+    };
+
+    if constexpr (IsAbstract<T> || IsFloatingPoint<T>) {
+        auto error_msg = [](auto a, const char* op, auto b) {
+            return "12:34 error: " + OverflowErrorMessage(a, op, b) + R"(
+12:34 note: when calculating dot)";
+        };
+        ConcatInto(  //
+            r, std::vector<Case>{
+                   E({Vec(T::Highest(), T::Highest()), Vec(T(1), T(1))},
+                     error_msg(T::Highest(), "+", T::Highest())),
+                   E({Vec(T::Lowest(), T::Lowest()), Vec(T(1), T(1))},
+                     error_msg(T::Lowest(), "+", T::Lowest())),
+               });
+    } else {
+        // Overflow is not an error for concrete integrals
+        ConcatInto(  //
+            r, std::vector<Case>{
+                   C({Vec(T::Highest(), T::Highest()), Vec(T(1), T(1))},
+                     Val(Add(T::Highest(), T::Highest()))),
+                   C({Vec(T::Lowest(), T::Lowest()), Vec(T(1), T(1))},
+                     Val(Add(T::Lowest(), T::Lowest()))),
+               });
+    }
+    return r;
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    Dot,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kDot),
+                     testing::ValuesIn(Concat(DotCases<AInt>(),    //
+                                              DotCases<i32>(),     //
+                                              DotCases<u32>(),     //
+                                              DotCases<AFloat>(),  //
+                                              DotCases<f32>(),     //
+                                              DotCases<f16>()))));
 
 template <typename T>
 std::vector<Case> FirstLeadingBitCases() {
@@ -1006,9 +948,9 @@
                      testing::ValuesIn(Concat(FirstTrailingBitCases<i32>(),  //
                                               FirstTrailingBitCases<u32>()))));
 
-template <typename T, bool finite_only>
+template <typename T>
 std::vector<Case> FloorCases() {
-    std::vector<Case> cases = {
+    return {
         C({T(0)}, T(0)),
         C({-T(0)}, -T(0)),
         C({-T(1.5)}, -T(2.0)),
@@ -1018,24 +960,14 @@
 
         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>()))));
+                     testing::ValuesIn(Concat(FloorCases<AFloat>(),  //
+                                              FloorCases<f32>(),
+                                              FloorCases<f16>()))));
 
 template <typename T>
 std::vector<Case> InsertBitsCases() {
@@ -1089,6 +1021,26 @@
               T(0b1010'0101'1010'0101'1010'0111'1111'1101))),
     };
 
+    const char* error_msg =
+        "12:34 error: 'offset + 'count' must be less than or equal to the bit width of 'e'";
+    ConcatInto(  //
+        r, std::vector<Case>{
+               E({T(1), T(1), UT(33), UT(0)}, error_msg),         //
+               E({T(1), T(1), UT(34), UT(0)}, error_msg),         //
+               E({T(1), T(1), UT(1000), UT(0)}, error_msg),       //
+               E({T(1), T(1), UT::Highest(), UT()}, error_msg),   //
+               E({T(1), T(1), UT(0), UT(33)}, error_msg),         //
+               E({T(1), T(1), UT(0), UT(34)}, error_msg),         //
+               E({T(1), T(1), UT(0), UT(1000)}, error_msg),       //
+               E({T(1), T(1), UT(0), UT::Highest()}, error_msg),  //
+               E({T(1), T(1), UT(33), UT(33)}, error_msg),        //
+               E({T(1), T(1), UT(34), UT(34)}, error_msg),        //
+               E({T(1), T(1), UT(1000), UT(1000)}, error_msg),    //
+               E({T(1), T(1), UT::Highest(), UT(1)}, error_msg),
+               E({T(1), T(1), UT(1), UT::Highest()}, error_msg),
+               E({T(1), T(1), UT::Highest(), u32::Highest()}, error_msg),
+           });
+
     return r;
 }
 INSTANTIATE_TEST_SUITE_P(  //
@@ -1098,32 +1050,56 @@
                      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'");
+template <typename T>
+std::vector<Case> DegreesAFloatCases() {
+    return std::vector<Case>{
+        C({T(0)}, T(0)),                             //
+        C({-T(0)}, -T(0)),                           //
+        C({T(0.698132)}, T(40)).FloatComp(),         //
+        C({-T(1.5708)}, -T(90.000214)).FloatComp(),  //
+        C({T(1.5708)}, T(90.000214)).FloatComp(),    //
+        C({T(6.28319)}, T(360.00027)).FloatComp(),
+    };
 }
-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())));
+INSTANTIATE_TEST_SUITE_P(  //
+    DegreesAFloat,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kDegrees),
+                     testing::ValuesIn(DegreesAFloatCases<AFloat>())));
+
+template <typename T>
+std::vector<Case> DegreesF32Cases() {
+    return std::vector<Case>{
+        C({T(0)}, T(0)),                             //
+        C({-T(0)}, -T(0)),                           //
+        C({T(0.698132)}, T(40)).FloatComp(),         //
+        C({-T(1.5708)}, -T(90.000206)).FloatComp(),  //
+        C({T(1.5708)}, T(90.000206)).FloatComp(),    //
+        C({T(6.28319)}, T(360.00024)).FloatComp(),
+    };
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    DegreesF32,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kDegrees),
+                     testing::ValuesIn(DegreesF32Cases<f32>())));
+
+template <typename T>
+std::vector<Case> DegreesF16Cases() {
+    return std::vector<Case>{
+        C({T(0)}, T(0)),                            //
+        C({-T(0)}, -T(0)),                          //
+        C({T(0.698132)}, T(39.96875)).FloatComp(),  //
+        C({-T(1.5708)}, -T(89.9375)).FloatComp(),   //
+        C({T(1.5708)}, T(89.9375)).FloatComp(),     //
+        C({T(6.28319)}, T(359.75)).FloatComp(),
+    };
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    DegreesF16,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kDegrees),
+                     testing::ValuesIn(DegreesF16Cases<f16>())));
 
 template <typename T>
 std::vector<Case> ExtractBitsCases() {
@@ -1192,6 +1168,26 @@
               set_msbs_if_signed(T(0b11010001)))),
     };
 
+    const char* error_msg =
+        "12:34 error: 'offset + 'count' must be less than or equal to the bit width of 'e'";
+    ConcatInto(  //
+        r, std::vector<Case>{
+               E({T(1), UT(33), UT(0)}, error_msg),
+               E({T(1), UT(34), UT(0)}, error_msg),
+               E({T(1), UT(1000), UT(0)}, error_msg),
+               E({T(1), UT::Highest(), UT(0)}, error_msg),
+               E({T(1), UT(0), UT(33)}, error_msg),
+               E({T(1), UT(0), UT(34)}, error_msg),
+               E({T(1), UT(0), UT(1000)}, error_msg),
+               E({T(1), UT(0), UT::Highest()}, error_msg),
+               E({T(1), UT(33), UT(33)}, error_msg),
+               E({T(1), UT(34), UT(34)}, error_msg),
+               E({T(1), UT(1000), UT(1000)}, error_msg),
+               E({T(1), UT::Highest(), UT(1)}, error_msg),
+               E({T(1), UT(1), UT::Highest()}, error_msg),
+               E({T(1), UT::Highest(), UT::Highest()}, error_msg),
+           });
+
     return r;
 }
 INSTANTIATE_TEST_SUITE_P(  //
@@ -1201,34 +1197,150 @@
                      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'");
+template <typename T>
+std::vector<Case> LengthCases() {
+    const auto kSqrtOfHighest = T(std::sqrt(T::Highest()));
+    const auto kSqrtOfHighestSquared = T(kSqrtOfHighest * kSqrtOfHighest);
+
+    auto error_msg = [](auto a, const char* op, auto b) {
+        return "12:34 error: " + OverflowErrorMessage(a, op, b) + R"(
+12:34 note: when calculating length)";
+    };
+    return {
+        C({T(0)}, T(0)),
+        C({Vec(T(0), T(0))}, Val(T(0))),
+        C({Vec(T(0), T(0), T(0))}, Val(T(0))),
+        C({Vec(T(0), T(0), T(0), T(0))}, Val(T(0))),
+
+        C({T(1)}, T(1)),
+        C({Vec(T(1), T(1))}, Val(T(std::sqrt(2)))),
+        C({Vec(T(1), T(1), T(1))}, Val(T(std::sqrt(3)))),
+        C({Vec(T(1), T(1), T(1), T(1))}, Val(T(std::sqrt(4)))),
+
+        C({T(2)}, T(2)),
+        C({Vec(T(2), T(2))}, Val(T(std::sqrt(8)))),
+        C({Vec(T(2), T(2), T(2))}, Val(T(std::sqrt(12)))),
+        C({Vec(T(2), T(2), T(2), T(2))}, Val(T(std::sqrt(16)))),
+
+        C({Vec(T(2), T(3))}, Val(T(std::sqrt(13)))),
+        C({Vec(T(2), T(3), T(4))}, Val(T(std::sqrt(29)))),
+        C({Vec(T(2), T(3), T(4), T(5))}, Val(T(std::sqrt(54)))),
+
+        C({T(-5)}, T(5)),
+        C({T::Highest()}, T::Highest()),
+        C({T::Lowest()}, T::Highest()),
+
+        C({Vec(T(-2), T(-3), T(-4), T(-5))}, Val(T(std::sqrt(54)))),
+        C({Vec(T(2), T(-3), T(4), T(-5))}, Val(T(std::sqrt(54)))),
+        C({Vec(T(-2), T(3), T(-4), T(5))}, Val(T(std::sqrt(54)))),
+
+        C({Vec(kSqrtOfHighest, T(0))}, Val(kSqrtOfHighest)).FloatComp(0.2),
+        C({Vec(T(0), kSqrtOfHighest)}, Val(kSqrtOfHighest)).FloatComp(0.2),
+
+        C({Vec(-kSqrtOfHighest, T(0))}, Val(kSqrtOfHighest)).FloatComp(0.2),
+        C({Vec(T(0), -kSqrtOfHighest)}, Val(kSqrtOfHighest)).FloatComp(0.2),
+
+        // Overflow when squaring a term
+        E({Vec(T::Highest(), T(0))}, error_msg(T::Highest(), "*", T::Highest())),
+        E({Vec(T(0), T::Highest())}, error_msg(T::Highest(), "*", T::Highest())),
+        // Overflow when adding squared terms
+        E({Vec(kSqrtOfHighest, kSqrtOfHighest)},
+          error_msg(kSqrtOfHighestSquared, "+", kSqrtOfHighestSquared)),
+    };
 }
-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(), 1),  //
-                             std::make_tuple(1, u32::Highest()),  //
-                             std::make_tuple(u32::Highest(), u32::Highest())));
+INSTANTIATE_TEST_SUITE_P(  //
+    Length,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kLength),
+                     testing::ValuesIn(Concat(LengthCases<AFloat>(),  //
+                                              LengthCases<f32>(),
+                                              LengthCases<f16>()))));
+
+template <typename T>
+std::vector<Case> MaxCases() {
+    return {
+        C({T(0), T(0)}, T(0)),
+        C({T(0), T::Highest()}, T::Highest()),
+        C({T::Lowest(), T(0)}, T(0)),
+        C({T::Highest(), T::Lowest()}, T::Highest()),
+        C({T::Highest(), T::Highest()}, T::Highest()),
+        C({T::Lowest(), T::Lowest()}, T::Lowest()),
+
+        // Vector tests
+        C({Vec(T(0), T(0)), Vec(T(0), T(42))}, Vec(T(0), T(42))),
+        C({Vec(T::Lowest(), T(0)), Vec(T(0), T::Lowest())}, Vec(T(0), T(0))),
+        C({Vec(T::Lowest(), T::Highest()), Vec(T::Highest(), T::Lowest())},
+          Vec(T::Highest(), T::Highest())),
+    };
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    Max,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kMax),
+                     testing::ValuesIn(Concat(MaxCases<AInt>(),  //
+                                              MaxCases<i32>(),
+                                              MaxCases<u32>(),
+                                              MaxCases<AFloat>(),
+                                              MaxCases<f32>(),
+                                              MaxCases<f16>()))));
+
+template <typename T>
+std::vector<Case> MinCases() {
+    return {C({T(0), T(0)}, T(0)),                //
+            C({T(0), T(42)}, T(0)),               //
+            C({T::Lowest(), T(0)}, T::Lowest()),  //
+            C({T(0), T::Highest()}, T(0)),        //
+            C({T::Highest(), T::Lowest()}, T::Lowest()),
+            C({T::Highest(), T::Highest()}, T::Highest()),
+            C({T::Lowest(), T::Lowest()}, T::Lowest()),
+
+            // Vector tests
+            C({Vec(T(0), T(0)), Vec(T(0), T(42))}, Vec(T(0), T(0))),
+            C({Vec(T::Lowest(), T(0), T(1)), Vec(T(0), T(42), T::Highest())},
+              Vec(T::Lowest(), T(0), T(1)))};
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    Min,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kMin),
+                     testing::ValuesIn(Concat(MinCases<AInt>(),  //
+                                              MinCases<i32>(),
+                                              MinCases<u32>(),
+                                              MinCases<AFloat>(),
+                                              MinCases<f32>(),
+                                              MinCases<f16>()))));
+template <typename T>
+std::vector<Case> ModfCases() {
+    return {
+        // Scalar tests
+        //  in     fract    whole
+        C({T(0.0)}, {T(0.0), T(0.0)}),              //
+        C({T(1.0)}, {T(0.0), T(1.0)}),              //
+        C({T(2.0)}, {T(0.0), T(2.0)}),              //
+        C({T(1.5)}, {T(0.5), T(1.0)}),              //
+        C({T(4.25)}, {T(0.25), T(4.0)}),            //
+        C({T(-1.0)}, {T(0.0), T(-1.0)}),            //
+        C({T(-2.0)}, {T(0.0), T(-2.0)}),            //
+        C({T(-1.5)}, {T(-0.5), T(-1.0)}),           //
+        C({T(-4.25)}, {T(-0.25), T(-4.0)}),         //
+        C({T::Lowest()}, {T(0.0), T::Lowest()}),    //
+        C({T::Highest()}, {T(0.0), T::Highest()}),  //
+
+        // Vector tests
+        //         in                 fract                    whole
+        C({Vec(T(0.0), T(0.0))}, {Vec(T(0.0), T(0.0)), Vec(T(0.0), T(0.0))}),
+        C({Vec(T(1.0), T(2.0))}, {Vec(T(0.0), T(0.0)), Vec(T(1), T(2))}),
+        C({Vec(T(-2.0), T(1.0))}, {Vec(T(0.0), T(0.0)), Vec(T(-2), T(1))}),
+        C({Vec(T(1.5), T(-2.25))}, {Vec(T(0.5), T(-0.25)), Vec(T(1.0), T(-2.0))}),
+        C({Vec(T::Lowest(), T::Highest())}, {Vec(T(0.0), T(0.0)), Vec(T::Lowest(), T::Highest())}),
+    };
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    Modf,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kModf),
+                     testing::ValuesIn(Concat(ModfCases<f32>(),  //
+                                              ModfCases<f16>()))));
 
 std::vector<Case> Pack4x8snormCases() {
     return {
@@ -1265,6 +1377,25 @@
     testing::Combine(testing::Values(sem::BuiltinType::kPack4X8Unorm),
                      testing::ValuesIn(Pack4x8unormCases())));
 
+std::vector<Case> Pack2x16floatCases() {
+    return {
+        C({Vec(f32(f16::Lowest()), f32(f16::Highest()))}, Val(u32(0x7bff'fbff))),
+        C({Vec(f32(1), f32(-1))}, Val(u32(0xbc00'3c00))),
+        C({Vec(f32(0), f32(0))}, Val(u32(0x0000'0000))),
+        C({Vec(f32(10), f32(-10.5))}, Val(u32(0xc940'4900))),
+
+        E({Vec(f32(0), f32::Highest())},
+          "12:34 error: value 3.4028234663852885981e+38 cannot be represented as 'f16'"),
+        E({Vec(f32::Lowest(), f32(0))},
+          "12:34 error: value -3.4028234663852885981e+38 cannot be represented as 'f16'"),
+    };
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    Pack2x16float,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kPack2X16Float),
+                     testing::ValuesIn(Pack2x16floatCases())));
+
 std::vector<Case> Pack2x16snormCases() {
     return {
         C({Vec(f32(0), f32(0))}, Val(u32(0x0000'0000))),
@@ -1344,6 +1475,71 @@
                                               ReverseBitsCases<u32>()))));
 
 template <typename T>
+std::vector<Case> RadiansCases() {
+    return std::vector<Case>{
+        C({T(0)}, T(0)),                         //
+        C({-T(0)}, -T(0)),                       //
+        C({T(40)}, T(0.69813168)).FloatComp(),   //
+        C({-T(90)}, -T(1.5707964)).FloatComp(),  //
+        C({T(90)}, T(1.5707964)).FloatComp(),    //
+        C({T(360)}, T(6.2831855)).FloatComp(),
+    };
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    Radians,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kRadians),
+                     testing::ValuesIn(Concat(RadiansCases<AFloat>(),  //
+                                              RadiansCases<f32>()))));
+
+template <typename T>
+std::vector<Case> RadiansF16Cases() {
+    return std::vector<Case>{
+        C({T(0)}, T(0)),                         //
+        C({-T(0)}, -T(0)),                       //
+        C({T(40)}, T(0.69726562)).FloatComp(),   //
+        C({-T(90)}, -T(1.5693359)).FloatComp(),  //
+        C({T(90)}, T(1.5693359)).FloatComp(),    //
+        C({T(360)}, T(6.2773438)).FloatComp(),
+    };
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    RadiansF16,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kRadians),
+                     testing::ValuesIn(RadiansF16Cases<f16>())));
+
+template <typename T>
+std::vector<Case> RoundCases() {
+    std::vector<Case> cases = {
+        C({T(0.0)}, T(0.0)),      //
+        C({-T(0.0)}, -T(0.0)),    //
+        C({T(1.5)}, T(2.0)),      //
+        C({T(2.5)}, T(2.0)),      //
+        C({T(2.4)}, T(2.0)),      //
+        C({T(2.6)}, T(3.0)),      //
+        C({T(1.49999)}, T(1.0)),  //
+        C({T(1.50001)}, T(2.0)),  //
+        C({-T(1.5)}, -T(2.0)),    //
+        C({-T(2.5)}, -T(2.0)),    //
+        C({-T(2.6)}, -T(3.0)),    //
+        C({-T(2.4)}, -T(2.0)),    //
+
+        // Vector tests
+        C({Vec(T(0.0), T(1.5), T(2.5))}, Vec(T(0.0), T(2.0), T(2.0))),
+    };
+
+    return cases;
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    Round,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kRound),
+                     testing::ValuesIn(Concat(RoundCases<AFloat>(),  //
+                                              RoundCases<f32>(),
+                                              RoundCases<f16>()))));
+
+template <typename T>
 std::vector<Case> SaturateCases() {
     return {
         C({T(0)}, T(0)),
@@ -1441,6 +1637,87 @@
                                               SignCases<f16>()))));
 
 template <typename T>
+std::vector<Case> SinCases() {
+    std::vector<Case> cases = {
+        C({-T(0)}, -T(0)),
+        C({T(0)}, T(0)),
+        C({T(0.75)}, T(0.68163876)).FloatComp(),
+        C({-T(0.75)}, -T(0.68163876)).FloatComp(),
+
+        // Vector test
+        C({Vec(T(0), -T(0), T(0.75))}, Vec(T(0), -T(0), T(0.68163876))).FloatComp(),
+    };
+
+    return cases;
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    Sin,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kSin),
+                     testing::ValuesIn(Concat(SinCases<AFloat>(),  //
+                                              SinCases<f32>(),
+                                              SinCases<f16>()))));
+
+template <typename T>
+std::vector<Case> SinhCases() {
+    auto error_msg = [](auto a) {
+        return "12:34 error: " + OverflowErrorMessage(a, FriendlyName<decltype(a)>());
+    };
+    std::vector<Case> cases = {
+        C({T(0)}, T(0)),
+        C({-T(0)}, -T(0)),
+        C({T(1)}, T(1.1752012)).FloatComp(),
+        C({T(-1)}, -T(1.1752012)).FloatComp(),
+
+        // Vector tests
+        C({Vec(T(0), -T(0), T(1))}, Vec(T(0), -T(0), T(1.1752012))).FloatComp(),
+
+        E({T(10000)}, error_msg(T::Inf())),
+    };
+    return cases;
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    Sinh,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kSinh),
+                     testing::ValuesIn(Concat(SinhCases<AFloat>(),  //
+                                              SinhCases<f32>(),
+                                              SinhCases<f16>()))));
+
+template <typename T>
+std::vector<Case> SmoothstepCases() {
+    auto error_msg = [](auto a, const char* op, auto b) {
+        return "12:34 error: " + OverflowErrorMessage(a, op, b) + R"(
+12:34 note: when calculating smoothstep)";
+    };
+    return {
+        // t == 0
+        C({T(4), T(6), T(2)}, T(0)),
+        // t == 1
+        C({T(4), T(6), T(8)}, T(1)),
+        // t == .5
+        C({T(4), T(6), T(5)}, T(.5)),
+
+        // Vector tests
+        C({Vec(T(4), T(4)), Vec(T(6), T(6)), Vec(T(2), T(8))}, Vec(T(0), T(1))),
+
+        // `x - low` underflows
+        E({T::Highest(), T(1), T::Lowest()}, error_msg(T::Lowest(), "-", T::Highest())),
+        // `high - low` underflows
+        E({T::Highest(), T::Lowest(), T(0)}, error_msg(T::Lowest(), "-", T::Highest())),
+        // Divide by zero on `(x - low) / (high - low)`
+        E({T(0), T(0), T(0)}, error_msg(T(0), "/", T(0))),
+    };
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    Smoothstep,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kSmoothstep),
+                     testing::ValuesIn(Concat(SmoothstepCases<AFloat>(),  //
+                                              SmoothstepCases<f32>(),
+                                              SmoothstepCases<f16>()))));
+
+template <typename T>
 std::vector<Case> StepCases() {
     return {
         C({T(0), T(0)}, T(1.0)),
@@ -1472,6 +1749,91 @@
                                               StepCases<f32>(),
                                               StepCases<f16>()))));
 
+template <typename T>
+std::vector<Case> SqrtCases() {
+    std::vector<Case> cases = {
+        C({-T(0)}, -T(0)),  //
+        C({T(0)}, T(0)),    //
+        C({T(25)}, T(5)),
+
+        // Vector tests
+        C({Vec(T(25), T(100))}, Vec(T(5), T(10))),
+
+        E({-T(25)}, "12:34 error: sqrt must be called with a value >= 0"),
+    };
+    return cases;
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    Sqrt,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kSqrt),
+                     testing::ValuesIn(Concat(SqrtCases<AFloat>(),  //
+                                              SqrtCases<f32>(),
+                                              SqrtCases<f16>()))));
+
+template <typename T>
+std::vector<Case> TanCases() {
+    std::vector<Case> cases = {
+        C({-T(0)}, -T(0)),
+        C({T(0)}, T(0)),
+        C({T(.75)}, T(0.9315964599)).FloatComp(),
+
+        // Vector test
+        C({Vec(T(0), -T(0), T(.75))}, Vec(T(0), -T(0), T(0.9315964599))).FloatComp(),
+    };
+
+    return cases;
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    Tan,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kTan),
+                     testing::ValuesIn(Concat(TanCases<AFloat>(),  //
+                                              TanCases<f32>(),
+                                              TanCases<f16>()))));
+
+template <typename T>
+std::vector<Case> TanhCases() {
+    std::vector<Case> cases = {
+        C({T(0)}, T(0)),
+        C({-T(0)}, -T(0)),
+        C({T(1)}, T(0.761594156)).FloatComp(),
+        C({T(-1)}, -T(0.761594156)).FloatComp(),
+
+        // Vector tests
+        C({Vec(T(0), -T(0), T(1))}, Vec(T(0), -T(0), T(0.761594156))).FloatComp(),
+    };
+
+    return cases;
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    Tanh,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kTanh),
+                     testing::ValuesIn(Concat(TanhCases<AFloat>(),  //
+                                              TanhCases<f32>(),
+                                              TanhCases<f16>()))));
+
+template <typename T>
+std::vector<Case> TruncCases() {
+    std::vector<Case> cases = {C({T(0)}, T(0)),    //
+                               C({-T(0)}, -T(0)),  //
+                               C({T(1.5)}, T(1)),  //
+                               C({-T(1.5)}, -T(1)),
+
+                               // Vector tests
+                               C({Vec(T(0.0), T(1.5), -T(2.2))}, Vec(T(0), T(1), -T(2)))};
+
+    return cases;
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    Trunc,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kTrunc),
+                     testing::ValuesIn(Concat(TruncCases<AFloat>(),  //
+                                              TruncCases<f32>(),
+                                              TruncCases<f16>()))));
+
 std::vector<Case> Unpack4x8snormCases() {
     return {
         C({Val(u32(0x0000'0000))}, Vec(f32(0), f32(0), f32(0), f32(0))),
@@ -1508,6 +1870,20 @@
     testing::Combine(testing::Values(sem::BuiltinType::kUnpack4X8Unorm),
                      testing::ValuesIn(Unpack4x8unormCases())));
 
+std::vector<Case> Unpack2x16floatCases() {
+    return {
+        C({Val(u32(0x7bff'fbff))}, Vec(f32(f16::Lowest()), f32(f16::Highest()))),
+        C({Val(u32(0xbc00'3c00))}, Vec(f32(1), f32(-1))),
+        C({Val(u32(0x0000'0000))}, Vec(f32(0), f32(0))),
+        C({Val(u32(0xc940'4900))}, Vec(f32(10), f32(-10.5))),
+    };
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    Unpack2x16float,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kUnpack2X16Float),
+                     testing::ValuesIn(Unpack2x16floatCases())));
+
 std::vector<Case> Unpack2x16snormCases() {
     return {
         C({Val(u32(0x0000'0000))}, Vec(f32(0), f32(0))),
@@ -1541,7 +1917,6 @@
                      testing::ValuesIn(Unpack2x16unormCases())));
 
 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),  //
@@ -1570,12 +1945,6 @@
         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)),  //
@@ -1588,8 +1957,16 @@
         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)),
+        // Value out of f16 range
+        E({65504.003_f}, "12:34 error: value 65504.00390625 cannot be represented as 'f16'"),
+        E({-65504.003_f}, "12:34 error: value -65504.00390625 cannot be represented as 'f16'"),
+        E({0x1.234p56_f}, "12:34 error: value 81979586966978560 cannot be represented as 'f16'"),
+        E({0x4.321p65_f},
+          "12:34 error: value 1.5478871919272394752e+20 cannot be represented as 'f16'"),
+        E({Vec(65504.003_f, 0_f)},
+          "12:34 error: value 65504.00390625 cannot be represented as 'f16'"),
+        E({Vec(0_f, -0x4.321p65_f)},
+          "12:34 error: value -1.5478871919272394752e+20 cannot be represented as 'f16'"),
     };
 }
 INSTANTIATE_TEST_SUITE_P(  //
diff --git a/src/tint/resolver/const_eval_conversion_test.cc b/src/tint/resolver/const_eval_conversion_test.cc
index 7e6a6fc..ed68725 100644
--- a/src/tint/resolver/const_eval_conversion_test.cc
+++ b/src/tint/resolver/const_eval_conversion_test.cc
@@ -440,38 +440,11 @@
 TEST_F(ResolverConstEvalTest, Vec3_Convert_Large_f32_to_f16) {
     Enable(ast::Extension::kF16);
 
-    auto* expr = vec3<f16>(vec3<f32>(1e10_f, -1e20_f, 1e30_f));
+    auto* expr = vec3<f16>(Source{{12, 34}}, vec3<f32>(1e10_f, 0_f, 0_f));
     WrapInFunction(expr);
 
-    EXPECT_TRUE(r()->Resolve()) << r()->error();
-
-    constexpr auto kInfinity = std::numeric_limits<double>::infinity();
-
-    auto* sem = Sem().Get(expr);
-    ASSERT_NE(sem, nullptr);
-    auto* vec = sem->Type()->As<sem::Vector>();
-    ASSERT_NE(vec, nullptr);
-    EXPECT_TRUE(vec->type()->Is<sem::F16>());
-    EXPECT_EQ(vec->Width(), 3u);
-    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
-    EXPECT_FALSE(sem->ConstantValue()->AllEqual());
-    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
-    EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
-    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
-    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
-    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
-    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), kInfinity);
-
-    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
-    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
-    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
-    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), -kInfinity);
-
-    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
-    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
-    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
-    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), kInfinity);
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(), "12:34 error: value 10000000000 cannot be represented as 'f16'");
 }
 
 TEST_F(ResolverConstEvalTest, Vec3_Convert_Small_f32_to_f16) {
diff --git a/src/tint/resolver/const_eval_test.h b/src/tint/resolver/const_eval_test.h
index 2f000f8..bc85542 100644
--- a/src/tint/resolver/const_eval_test.h
+++ b/src/tint/resolver/const_eval_test.h
@@ -16,6 +16,8 @@
 #define SRC_TINT_RESOLVER_CONST_EVAL_TEST_H_
 
 #include <limits>
+#include <optional>
+#include <string>
 #include <utility>
 
 #include "gmock/gmock.h"
@@ -26,9 +28,6 @@
 namespace tint::resolver {
 
 template <typename T>
-inline const auto kPi = T(UnwrapNumber<T>(3.14159265358979323846));
-
-template <typename T>
 inline const auto kPiOver2 = T(UnwrapNumber<T>(1.57079632679489661923));
 
 template <typename T>
@@ -64,6 +63,82 @@
 }
 
 template <typename T>
+inline auto Abs(const Number<T>& v) {
+    if constexpr (std::is_integral_v<T> && std::is_unsigned_v<T>) {
+        return v;
+    } else {
+        return Number<T>(std::abs(v));
+    }
+}
+
+/// Flags that can be passed to CheckConstant()
+struct CheckConstantFlags {
+    /// Expected value may be positive or negative
+    bool pos_or_neg = false;
+    /// Expected value should be compared using EXPECT_FLOAT_EQ instead of EQ, or EXPECT_NEAR if
+    /// float_compare_epsilon is set.
+    bool float_compare = false;
+    /// Expected value should be compared using EXPECT_NEAR if float_compare is set.
+    std::optional<double> float_compare_epsilon;
+};
+
+/// CheckConstant checks that @p got_constant, the result value of
+/// constant-evaluation is equal to @p expected_value.
+/// @param got_constant the constant value evaluated by the resolver
+/// @param expected_value the expected value for the test
+/// @param flags optional flags for controlling the comparisons
+inline void CheckConstant(const sem::Constant* got_constant,
+                          const builder::ValueBase* expected_value,
+                          CheckConstantFlags flags = {}) {
+    auto values_flat = ScalarArgsFrom(got_constant);
+    auto expected_values_flat = expected_value->Args();
+    ASSERT_EQ(values_flat.values.Length(), expected_values_flat.values.Length());
+    for (size_t i = 0; i < values_flat.values.Length(); ++i) {
+        auto& got_scalar = values_flat.values[i];
+        auto& expected_scalar = expected_values_flat.values[i];
+        std::visit(
+            [&](auto&& expected) {
+                using T = std::decay_t<decltype(expected)>;
+
+                ASSERT_TRUE(std::holds_alternative<T>(got_scalar));
+                auto got = std::get<T>(got_scalar);
+
+                if constexpr (std::is_same_v<bool, T>) {
+                    EXPECT_EQ(got, expected);
+                } else if constexpr (IsFloatingPoint<T>) {
+                    if (std::isnan(expected)) {
+                        EXPECT_TRUE(std::isnan(got));
+                    } else {
+                        if (flags.pos_or_neg) {
+                            got = Abs(got);
+                        }
+                        if (flags.float_compare) {
+                            if (flags.float_compare_epsilon) {
+                                EXPECT_NEAR(got, expected, *flags.float_compare_epsilon);
+                            } else {
+                                EXPECT_FLOAT_EQ(got, expected);
+                            }
+                        } else {
+                            EXPECT_EQ(got, expected);
+                        }
+                    }
+                } else {
+                    if (flags.pos_or_neg) {
+                        auto got_abs = Abs(got);
+                        EXPECT_EQ(got_abs, expected);
+                    } else {
+                        EXPECT_EQ(got, expected);
+                    }
+                    // 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(AInt(got), AInt(expected));
+                }
+            },
+            expected_scalar);
+    }
+}
+
+template <typename T>
 inline constexpr auto Negate(const Number<T>& v) {
     if constexpr (std::is_integral_v<T>) {
         if constexpr (std::is_signed_v<T>) {
@@ -87,15 +162,6 @@
     }
 }
 
-template <typename T>
-inline auto Abs(const Number<T>& v) {
-    if constexpr (std::is_integral_v<T> && std::is_unsigned_v<T>) {
-        return v;
-    } else {
-        return Number<T>(std::abs(v));
-    }
-}
-
 TINT_BEGIN_DISABLE_WARNING(CONSTANT_OVERFLOW);
 template <typename T>
 inline constexpr Number<T> Mul(Number<T> v1, Number<T> v2) {
@@ -109,6 +175,19 @@
 }
 TINT_END_DISABLE_WARNING(CONSTANT_OVERFLOW);
 
+TINT_BEGIN_DISABLE_WARNING(CONSTANT_OVERFLOW);
+template <typename T>
+inline constexpr Number<T> Add(Number<T> v1, Number<T> v2) {
+    if constexpr (std::is_integral_v<T> && std::is_signed_v<T>) {
+        // For signed integrals, avoid C++ UB by adding as unsigned
+        using UT = std::make_unsigned_t<T>;
+        return static_cast<Number<T>>(static_cast<UT>(v1) + static_cast<UT>(v2));
+    } else {
+        return static_cast<Number<T>>(v1 + v2);
+    }
+}
+TINT_END_DISABLE_WARNING(CONSTANT_OVERFLOW);
+
 // Concats any number of std::vectors
 template <typename Vec, typename... Vecs>
 [[nodiscard]] inline auto Concat(Vec&& v1, Vecs&&... vs) {
@@ -134,6 +213,26 @@
     }
 }
 
+/// Returns the overflow error message for binary ops
+template <typename NumberT>
+inline 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();
+}
+
+/// Returns the overflow error message for converions
+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();
+}
+
 using builder::IsValue;
 using builder::Mat;
 using builder::Val;
diff --git a/src/tint/resolver/control_block_validation_test.cc b/src/tint/resolver/control_block_validation_test.cc
index 8cbf506..2104321 100644
--- a/src/tint/resolver/control_block_validation_test.cc
+++ b/src/tint/resolver/control_block_validation_test.cc
@@ -14,7 +14,6 @@
 
 #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/switch_statement.h"
 #include "src/tint/resolver/resolver_test_helper.h"
 
@@ -378,24 +377,6 @@
               "12:34 note: previous case declared here");
 }
 
-TEST_F(ResolverControlBlockValidationTest, LastClauseLastStatementIsFallthrough_Fail) {
-    // var a : i32 = 2;
-    // switch (a) {
-    //   default: { fallthrough; }
-    // }
-    auto* var = Var("a", ty.i32(), Expr(2_i));
-    auto* fallthrough = create<ast::FallthroughStatement>(Source{{12, 34}});
-    auto* block = Block(Decl(var),   //
-                        Switch("a",  //
-                               DefaultCase(Block(fallthrough))));
-    WrapInFunction(block);
-
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              "12:34 error: a fallthrough statement must not be used in the last "
-              "switch case");
-}
-
 TEST_F(ResolverControlBlockValidationTest, SwitchCase_Pass) {
     // var a : i32 = 2;
     // switch (a) {
diff --git a/src/tint/resolver/dependency_graph.cc b/src/tint/resolver/dependency_graph.cc
index 3238c8b..1fda54f 100644
--- a/src/tint/resolver/dependency_graph.cc
+++ b/src/tint/resolver/dependency_graph.cc
@@ -15,7 +15,6 @@
 #include "src/tint/resolver/dependency_graph.h"
 
 #include <string>
-#include <unordered_set>
 #include <utility>
 #include <vector>
 
@@ -36,7 +35,6 @@
 #include "src/tint/ast/external_texture.h"
 #include "src/tint/ast/f16.h"
 #include "src/tint/ast/f32.h"
-#include "src/tint/ast/fallthrough_statement.h"
 #include "src/tint/ast/for_loop_statement.h"
 #include "src/tint/ast/i32.h"
 #include "src/tint/ast/id_attribute.h"
@@ -117,7 +115,7 @@
 
 /// A map of DependencyEdge to DependencyInfo
 using DependencyEdges =
-    std::unordered_map<DependencyEdge, DependencyInfo, DependencyEdgeCmp, DependencyEdgeCmp>;
+    utils::Hashmap<DependencyEdge, DependencyInfo, 64, DependencyEdgeCmp, DependencyEdgeCmp>;
 
 /// Global describes a module-scope variable, type or function.
 struct Global {
@@ -126,11 +124,11 @@
     /// The declaration ast::Node
     const ast::Node* node;
     /// A list of dependencies that this global depends on
-    std::vector<Global*> deps;
+    utils::Vector<Global*, 8> deps;
 };
 
 /// A map of global name to Global
-using GlobalMap = std::unordered_map<Symbol, Global*>;
+using GlobalMap = utils::Hashmap<Symbol, Global*, 16>;
 
 /// Raises an ICE that a global ast::Node type was not handled by this system.
 void UnhandledNode(diag::List& diagnostics, const ast::Node* node) {
@@ -170,7 +168,7 @@
           dependency_edges_(edges) {
         // Register all the globals at global-scope
         for (auto it : globals_by_name) {
-            scope_stack_.Set(it.first, it.second->node);
+            scope_stack_.Set(it.key, it.value->node);
         }
     }
 
@@ -232,7 +230,7 @@
 
         for (auto* param : func->params) {
             if (auto* shadows = scope_stack_.Get(param->symbol)) {
-                graph_.shadows.emplace(param, shadows);
+                graph_.shadows.Add(param, shadows);
             }
             Declare(param->symbol, param);
         }
@@ -306,7 +304,7 @@
             },
             [&](const ast::VariableDeclStatement* v) {
                 if (auto* shadows = scope_stack_.Get(v->variable->symbol)) {
-                    graph_.shadows.emplace(v->variable, shadows);
+                    graph_.shadows.Add(v->variable, shadows);
                 }
                 TraverseType(v->variable->type);
                 TraverseExpression(v->variable->initializer);
@@ -321,7 +319,7 @@
             [&](const ast::StaticAssert* assertion) { TraverseExpression(assertion->condition); },
             [&](Default) {
                 if (!stmt->IsAnyOf<ast::BreakStatement, ast::ContinueStatement,
-                                   ast::DiscardStatement, ast::FallthroughStatement>()) {
+                                   ast::DiscardStatement>()) {
                     UnhandledNode(diagnostics_, stmt);
                 }
             });
@@ -473,16 +471,14 @@
             }
         }
 
-        if (auto* global = utils::Lookup(globals_, to); global && global->node == resolved) {
-            if (dependency_edges_
-                    .emplace(DependencyEdge{current_global_, global},
-                             DependencyInfo{from->source, action})
-                    .second) {
-                current_global_->deps.emplace_back(global);
+        if (auto* global = globals_.Find(to); global && (*global)->node == resolved) {
+            if (dependency_edges_.Add(DependencyEdge{current_global_, *global},
+                                      DependencyInfo{from->source, action})) {
+                current_global_->deps.Push(*global);
             }
         }
 
-        graph_.resolved_symbols.emplace(from, resolved);
+        graph_.resolved_symbols.Add(from, resolved);
     }
 
     /// @returns true if `name` is the name of a builtin function
@@ -497,7 +493,7 @@
                  source);
     }
 
-    using VariableMap = std::unordered_map<Symbol, const ast::Variable*>;
+    using VariableMap = utils::Hashmap<Symbol, const ast::Variable*, 32>;
     const SymbolTable& symbols_;
     const GlobalMap& globals_;
     diag::List& diagnostics_;
@@ -520,7 +516,7 @@
     /// @returns true if analysis found no errors, otherwise false.
     bool Run(const ast::Module& module) {
         // Reserve container memory
-        graph_.resolved_symbols.reserve(module.GlobalDeclarations().Length());
+        graph_.resolved_symbols.Reserve(module.GlobalDeclarations().Length());
         sorted_.Reserve(module.GlobalDeclarations().Length());
 
         // Collect all the named globals from the AST module
@@ -589,9 +585,9 @@
         for (auto* node : module.GlobalDeclarations()) {
             auto* global = allocator_.Create(node);
             if (auto symbol = SymbolOf(node); symbol.IsValid()) {
-                globals_.emplace(symbol, global);
+                globals_.Add(symbol, global);
             }
-            declaration_order_.emplace_back(global);
+            declaration_order_.Push(global);
         }
     }
 
@@ -625,16 +621,16 @@
             return;
         }
 
-        std::vector<Entry> stack{Entry{root, 0}};
+        utils::Vector<Entry, 16> stack{Entry{root, 0}};
         while (true) {
-            auto& entry = stack.back();
+            auto& entry = stack.Back();
             // Have we exhausted the dependencies of entry.global?
-            if (entry.dep_idx < entry.global->deps.size()) {
+            if (entry.dep_idx < entry.global->deps.Length()) {
                 // No, there's more dependencies to traverse.
                 auto& dep = entry.global->deps[entry.dep_idx];
                 // Does the caller want to enter this dependency?
-                if (enter(dep)) {                    // Yes.
-                    stack.push_back(Entry{dep, 0});  // Enter the dependency.
+                if (enter(dep)) {               // Yes.
+                    stack.Push(Entry{dep, 0});  // Enter the dependency.
                 } else {
                     entry.dep_idx++;  // No. Skip this node.
                 }
@@ -643,11 +639,11 @@
                 // Exit this global, pop the stack, and if there's another parent node,
                 // increment its dependency index, and loop again.
                 exit(entry.global);
-                stack.pop_back();
-                if (stack.empty()) {
+                stack.Pop();
+                if (stack.IsEmpty()) {
                     return;  // All done.
                 }
-                stack.back().dep_idx++;
+                stack.Back().dep_idx++;
             }
         }
     }
@@ -707,9 +703,8 @@
     /// of global `from` depending on `to`.
     /// @note will raise an ICE if the edge is not found.
     DependencyInfo DepInfoFor(const Global* from, const Global* to) const {
-        auto it = dependency_edges_.find(DependencyEdge{from, to});
-        if (it != dependency_edges_.end()) {
-            return it->second;
+        if (auto info = dependency_edges_.Find(DependencyEdge{from, to})) {
+            return *info;
         }
         TINT_ICE(Resolver, diagnostics_)
             << "failed to find dependency info for edge: '" << NameOf(from->node) << "' -> '"
@@ -762,7 +757,7 @@
         printf("------ dependencies ------ \n");
         for (auto* node : sorted_) {
             auto symbol = SymbolOf(node);
-            auto* global = globals_.at(symbol);
+            auto* global = *globals_.Find(symbol);
             printf("%s depends on:\n", symbols_.NameFor(symbol).c_str());
             for (auto* dep : global->deps) {
                 printf("  %s\n", NameOf(dep->node).c_str());
@@ -791,7 +786,7 @@
     DependencyEdges dependency_edges_;
 
     /// Globals in declaration order. Populated by GatherGlobals().
-    std::vector<Global*> declaration_order_;
+    utils::Vector<Global*, 64> declaration_order_;
 
     /// Globals in sorted dependency order. Populated by SortGlobals().
     utils::UniqueVector<const ast::Node*, 64> sorted_;
diff --git a/src/tint/resolver/dependency_graph.h b/src/tint/resolver/dependency_graph.h
index 9f5ddc5..bc849a0 100644
--- a/src/tint/resolver/dependency_graph.h
+++ b/src/tint/resolver/dependency_graph.h
@@ -15,11 +15,11 @@
 #ifndef SRC_TINT_RESOLVER_DEPENDENCY_GRAPH_H_
 #define SRC_TINT_RESOLVER_DEPENDENCY_GRAPH_H_
 
-#include <unordered_map>
 #include <vector>
 
 #include "src/tint/ast/module.h"
 #include "src/tint/diagnostic/diagnostic.h"
+#include "src/tint/utils/hashmap.h"
 
 namespace tint::resolver {
 
@@ -50,13 +50,13 @@
 
     /// Map of ast::IdentifierExpression or ast::TypeName to a type, function, or
     /// variable that declares the symbol.
-    std::unordered_map<const ast::Node*, const ast::Node*> resolved_symbols;
+    utils::Hashmap<const ast::Node*, const ast::Node*, 64> resolved_symbols;
 
     /// Map of ast::Variable to a type, function, or variable that is shadowed by
     /// the variable key. A declaration (X) shadows another (Y) if X and Y use
     /// the same symbol, and X is declared in a sub-scope of the scope that
     /// declares Y.
-    std::unordered_map<const ast::Variable*, const ast::Node*> shadows;
+    utils::Hashmap<const ast::Variable*, const ast::Node*, 16> shadows;
 };
 
 }  // namespace tint::resolver
diff --git a/src/tint/resolver/dependency_graph_test.cc b/src/tint/resolver/dependency_graph_test.cc
index 272357b..2cc4a3a 100644
--- a/src/tint/resolver/dependency_graph_test.cc
+++ b/src/tint/resolver/dependency_graph_test.cc
@@ -1128,9 +1128,10 @@
 
     if (expect_pass) {
         // Check that the use resolves to the declaration
-        auto* resolved_symbol = graph.resolved_symbols[use];
-        EXPECT_EQ(resolved_symbol, decl)
-            << "resolved: " << (resolved_symbol ? resolved_symbol->TypeInfo().name : "<null>")
+        auto* resolved_symbol = graph.resolved_symbols.Find(use);
+        ASSERT_NE(resolved_symbol, nullptr);
+        EXPECT_EQ(*resolved_symbol, decl)
+            << "resolved: " << (*resolved_symbol ? (*resolved_symbol)->TypeInfo().name : "<null>")
             << "\n"
             << "decl:     " << decl->TypeInfo().name;
     }
@@ -1177,7 +1178,10 @@
                           : helper.parameters[0];
     helper.Build();
 
-    EXPECT_EQ(Build().shadows[inner_var], outer);
+    auto shadows = Build().shadows;
+    auto* shadow = shadows.Find(inner_var);
+    ASSERT_NE(shadow, nullptr);
+    EXPECT_EQ(*shadow, outer);
 }
 
 INSTANTIATE_TEST_SUITE_P(LocalShadowGlobal,
@@ -1269,8 +1273,6 @@
              Switch(V,                                  //
                     Case(CaseSelector(1_i),             //
                          Block(Assign(V, V))),          //
-                    Case(CaseSelector(2_i),             //
-                         Block(Fallthrough())),         //
                     DefaultCase(Block(Assign(V, V)))),  //
              Return(V),                                 //
              Break(),                                   //
@@ -1308,8 +1310,9 @@
 
     auto graph = Build();
     for (auto use : symbol_uses) {
-        auto* resolved_symbol = graph.resolved_symbols[use.use];
-        EXPECT_EQ(resolved_symbol, use.decl) << use.where;
+        auto* resolved_symbol = graph.resolved_symbols.Find(use.use);
+        ASSERT_NE(resolved_symbol, nullptr) << use.where;
+        EXPECT_EQ(*resolved_symbol, use.decl) << use.where;
     }
 }
 
diff --git a/src/tint/resolver/function_validation_test.cc b/src/tint/resolver/function_validation_test.cc
index 49d37a1..808f556 100644
--- a/src/tint/resolver/function_validation_test.cc
+++ b/src/tint/resolver/function_validation_test.cc
@@ -161,7 +161,7 @@
     EXPECT_FALSE(Sem().Get(assign_a)->IsReachable());
 }
 
-TEST_F(ResolverFunctionValidationTest, UnreachableCode_discard) {
+TEST_F(ResolverFunctionValidationTest, UnreachableCode_discard_nowarning) {
     // fn func() -> {
     //  var a : i32;
     //  discard;
@@ -175,38 +175,17 @@
     Func("func", utils::Empty, ty.void_(), utils::Vector{decl_a, discard, assign_a});
 
     ASSERT_TRUE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
     EXPECT_TRUE(Sem().Get(decl_a)->IsReachable());
     EXPECT_TRUE(Sem().Get(discard)->IsReachable());
-    EXPECT_FALSE(Sem().Get(assign_a)->IsReachable());
-}
-
-TEST_F(ResolverFunctionValidationTest, UnreachableCode_discard_InBlocks) {
-    // fn func() -> {
-    //  var a : i32;
-    //  {{{discard;}}}
-    //  a = 2i;
-    //}
-
-    auto* decl_a = Decl(Var("a", ty.i32()));
-    auto* discard = Discard();
-    auto* assign_a = Assign(Source{{12, 34}}, "a", 2_i);
-
-    Func("func", utils::Empty, ty.void_(),
-         utils::Vector{decl_a, Block(Block(Block(discard))), assign_a});
-
-    ASSERT_TRUE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
-    EXPECT_TRUE(Sem().Get(decl_a)->IsReachable());
-    EXPECT_TRUE(Sem().Get(discard)->IsReachable());
-    EXPECT_FALSE(Sem().Get(assign_a)->IsReachable());
+    EXPECT_TRUE(Sem().Get(assign_a)->IsReachable());
 }
 
 TEST_F(ResolverFunctionValidationTest, DiscardCalledDirectlyFromVertexEntryPoint) {
-    // @vertex() fn func() -> @position(0) vec4<f32> { discard; }
+    // @vertex() fn func() -> @position(0) vec4<f32> { discard; return; }
     Func(Source{{1, 2}}, "func", utils::Empty, ty.vec4<f32>(),
          utils::Vector{
              Discard(Source{{12, 34}}),
+             Return(Construct(ty.vec4<f32>())),
          },
          utils::Vector{Stage(ast::PipelineStage::kVertex)},
          utils::Vector{Builtin(ast::BuiltinValue::kPosition)});
@@ -1092,7 +1071,7 @@
                                          TestParams{ast::AddressSpace::kIn, false},
                                          TestParams{ast::AddressSpace::kOut, false},
                                          TestParams{ast::AddressSpace::kUniform, false},
-                                         TestParams{ast::AddressSpace::kWorkgroup, true},
+                                         TestParams{ast::AddressSpace::kWorkgroup, false},
                                          TestParams{ast::AddressSpace::kHandle, false},
                                          TestParams{ast::AddressSpace::kStorage, false},
                                          TestParams{ast::AddressSpace::kPrivate, true},
diff --git a/src/tint/resolver/intrinsic_table.cc b/src/tint/resolver/intrinsic_table.cc
index 7545f48..9d11e08 100644
--- a/src/tint/resolver/intrinsic_table.cc
+++ b/src/tint/resolver/intrinsic_table.cc
@@ -16,7 +16,6 @@
 
 #include <algorithm>
 #include <limits>
-#include <unordered_map>
 #include <utility>
 
 #include "src/tint/ast/binary_expression.h"
@@ -36,7 +35,7 @@
 #include "src/tint/sem/type_conversion.h"
 #include "src/tint/sem/type_initializer.h"
 #include "src/tint/utils/hash.h"
-#include "src/tint/utils/map.h"
+#include "src/tint/utils/hashmap.h"
 #include "src/tint/utils/math.h"
 #include "src/tint/utils/scoped_assignment.h"
 
@@ -857,7 +856,7 @@
         display_name = "__frexp_result";
     }
     auto* i32 = state.builder.create<sem::I32>();
-    return build_struct(state, display_name, {{"sig", el}, {"exp", i32}});
+    return build_struct(state, display_name, {{"fract", el}, {"exp", i32}});
 }
 const sem::Struct* build_frexp_result_vec(MatchState& state, Number& n, const sem::Type* el) {
     std::string display_name;
@@ -868,7 +867,7 @@
     }
     auto* vec = state.builder.create<sem::Vector>(el, n.Value());
     auto* vec_i32 = state.builder.create<sem::Vector>(state.builder.create<sem::I32>(), n.Value());
-    return build_struct(state, display_name, {{"sig", vec}, {"exp", vec_i32}});
+    return build_struct(state, display_name, {{"fract", vec}, {"exp", vec_i32}});
 }
 const sem::Struct* build_atomic_compare_exchange_result(MatchState& state, const sem::Type* ty) {
     return build_struct(
@@ -1114,10 +1113,10 @@
 
     ProgramBuilder& builder;
     Matchers matchers;
-    std::unordered_map<IntrinsicPrototype, sem::Builtin*, IntrinsicPrototype::Hasher> builtins;
-    std::unordered_map<IntrinsicPrototype, sem::TypeInitializer*, IntrinsicPrototype::Hasher>
+    utils::Hashmap<IntrinsicPrototype, sem::Builtin*, 64, IntrinsicPrototype::Hasher> builtins;
+    utils::Hashmap<IntrinsicPrototype, sem::TypeInitializer*, 16, IntrinsicPrototype::Hasher>
         initializers;
-    std::unordered_map<IntrinsicPrototype, sem::TypeConversion*, IntrinsicPrototype::Hasher>
+    utils::Hashmap<IntrinsicPrototype, sem::TypeConversion*, 16, IntrinsicPrototype::Hasher>
         converters;
 };
 
@@ -1185,7 +1184,7 @@
     }
 
     // De-duplicate builtins that are identical.
-    auto* sem = utils::GetOrCreate(builtins, match, [&] {
+    auto* sem = builtins.GetOrCreate(match, [&] {
         utils::Vector<sem::Parameter*, kNumFixedParams> params;
         params.Reserve(match.parameters.Length());
         for (auto& p : match.parameters) {
@@ -1396,7 +1395,7 @@
         }
         auto eval_stage = match.overload->const_eval_fn ? sem::EvaluationStage::kConstant
                                                         : sem::EvaluationStage::kRuntime;
-        auto* target = utils::GetOrCreate(initializers, match, [&]() {
+        auto* target = initializers.GetOrCreate(match, [&]() {
             return builder.create<sem::TypeInitializer>(match.return_type, std::move(params),
                                                         eval_stage);
         });
@@ -1404,7 +1403,7 @@
     }
 
     // Conversion.
-    auto* target = utils::GetOrCreate(converters, match, [&]() {
+    auto* target = converters.GetOrCreate(match, [&]() {
         auto param = builder.create<sem::Parameter>(
             nullptr, 0u, match.parameters[0].type, ast::AddressSpace::kNone,
             ast::Access::kUndefined, match.parameters[0].usage);
diff --git a/src/tint/resolver/intrinsic_table.inl b/src/tint/resolver/intrinsic_table.inl
index dcba75b..be0e4bb 100644
--- a/src/tint/resolver/intrinsic_table.inl
+++ b/src/tint/resolver/intrinsic_table.inl
@@ -2742,30 +2742,30 @@
 
 constexpr MatcherIndex kMatcherIndices[] = {
   /* [0] */ 25,
-  /* [1] */ 0,
-  /* [2] */ 26,
+  /* [1] */ 10,
+  /* [2] */ 27,
   /* [3] */ 0,
-  /* [4] */ 7,
+  /* [4] */ 0,
   /* [5] */ 25,
-  /* [6] */ 10,
-  /* [7] */ 27,
+  /* [6] */ 0,
+  /* [7] */ 26,
   /* [8] */ 0,
-  /* [9] */ 0,
+  /* [9] */ 7,
   /* [10] */ 24,
   /* [11] */ 0,
-  /* [12] */ 1,
+  /* [12] */ 0,
   /* [13] */ 0,
   /* [14] */ 24,
   /* [15] */ 0,
-  /* [16] */ 0,
+  /* [16] */ 1,
   /* [17] */ 0,
   /* [18] */ 24,
-  /* [19] */ 0,
-  /* [20] */ 2,
+  /* [19] */ 1,
+  /* [20] */ 0,
   /* [21] */ 0,
   /* [22] */ 24,
-  /* [23] */ 1,
-  /* [24] */ 0,
+  /* [23] */ 0,
+  /* [24] */ 2,
   /* [25] */ 0,
   /* [26] */ 24,
   /* [27] */ 1,
@@ -2774,211 +2774,211 @@
   /* [30] */ 23,
   /* [31] */ 0,
   /* [32] */ 0,
-  /* [33] */ 45,
-  /* [34] */ 5,
-  /* [35] */ 6,
-  /* [36] */ 44,
-  /* [37] */ 5,
-  /* [38] */ 6,
-  /* [39] */ 23,
+  /* [33] */ 23,
+  /* [34] */ 0,
+  /* [35] */ 4,
+  /* [36] */ 23,
+  /* [37] */ 0,
+  /* [38] */ 9,
+  /* [39] */ 50,
   /* [40] */ 0,
-  /* [41] */ 4,
-  /* [42] */ 43,
-  /* [43] */ 5,
-  /* [44] */ 6,
-  /* [45] */ 44,
+  /* [41] */ 0,
+  /* [42] */ 23,
+  /* [43] */ 0,
+  /* [44] */ 7,
+  /* [45] */ 48,
   /* [46] */ 0,
-  /* [47] */ 1,
+  /* [47] */ 0,
   /* [48] */ 42,
-  /* [49] */ 5,
-  /* [50] */ 6,
-  /* [51] */ 45,
-  /* [52] */ 4,
-  /* [53] */ 6,
+  /* [49] */ 0,
+  /* [50] */ 1,
+  /* [51] */ 43,
+  /* [52] */ 0,
+  /* [53] */ 1,
   /* [54] */ 44,
-  /* [55] */ 4,
-  /* [56] */ 6,
-  /* [57] */ 43,
-  /* [58] */ 4,
-  /* [59] */ 6,
-  /* [60] */ 23,
-  /* [61] */ 0,
-  /* [62] */ 9,
-  /* [63] */ 42,
-  /* [64] */ 4,
+  /* [55] */ 0,
+  /* [56] */ 1,
+  /* [57] */ 45,
+  /* [58] */ 0,
+  /* [59] */ 1,
+  /* [60] */ 42,
+  /* [61] */ 3,
+  /* [62] */ 6,
+  /* [63] */ 43,
+  /* [64] */ 3,
   /* [65] */ 6,
-  /* [66] */ 45,
+  /* [66] */ 44,
   /* [67] */ 3,
   /* [68] */ 6,
-  /* [69] */ 23,
-  /* [70] */ 1,
-  /* [71] */ 0,
-  /* [72] */ 44,
-  /* [73] */ 3,
+  /* [69] */ 45,
+  /* [70] */ 3,
+  /* [71] */ 6,
+  /* [72] */ 42,
+  /* [73] */ 4,
   /* [74] */ 6,
   /* [75] */ 43,
-  /* [76] */ 3,
+  /* [76] */ 4,
   /* [77] */ 6,
-  /* [78] */ 50,
-  /* [79] */ 0,
-  /* [80] */ 0,
+  /* [78] */ 44,
+  /* [79] */ 4,
+  /* [80] */ 6,
   /* [81] */ 45,
-  /* [82] */ 0,
-  /* [83] */ 1,
-  /* [84] */ 43,
-  /* [85] */ 0,
-  /* [86] */ 1,
-  /* [87] */ 42,
-  /* [88] */ 0,
-  /* [89] */ 1,
-  /* [90] */ 23,
-  /* [91] */ 0,
-  /* [92] */ 7,
-  /* [93] */ 23,
-  /* [94] */ 0,
-  /* [95] */ 8,
-  /* [96] */ 48,
-  /* [97] */ 0,
+  /* [82] */ 4,
+  /* [83] */ 6,
+  /* [84] */ 42,
+  /* [85] */ 5,
+  /* [86] */ 6,
+  /* [87] */ 43,
+  /* [88] */ 5,
+  /* [89] */ 6,
+  /* [90] */ 44,
+  /* [91] */ 5,
+  /* [92] */ 6,
+  /* [93] */ 45,
+  /* [94] */ 5,
+  /* [95] */ 6,
+  /* [96] */ 23,
+  /* [97] */ 1,
   /* [98] */ 0,
-  /* [99] */ 42,
-  /* [100] */ 3,
-  /* [101] */ 6,
-  /* [102] */ 13,
-  /* [103] */ 9,
-  /* [104] */ 12,
+  /* [99] */ 23,
+  /* [100] */ 0,
+  /* [101] */ 8,
+  /* [102] */ 12,
+  /* [103] */ 0,
+  /* [104] */ 49,
   /* [105] */ 0,
-  /* [106] */ 12,
-  /* [107] */ 1,
-  /* [108] */ 12,
+  /* [106] */ 47,
+  /* [107] */ 0,
+  /* [108] */ 11,
   /* [109] */ 9,
-  /* [110] */ 12,
-  /* [111] */ 10,
-  /* [112] */ 11,
+  /* [110] */ 13,
+  /* [111] */ 9,
+  /* [112] */ 30,
   /* [113] */ 0,
-  /* [114] */ 12,
-  /* [115] */ 7,
-  /* [116] */ 12,
+  /* [114] */ 31,
+  /* [115] */ 0,
+  /* [116] */ 11,
   /* [117] */ 8,
-  /* [118] */ 12,
-  /* [119] */ 4,
-  /* [120] */ 11,
-  /* [121] */ 4,
-  /* [122] */ 11,
-  /* [123] */ 1,
-  /* [124] */ 11,
-  /* [125] */ 8,
-  /* [126] */ 13,
+  /* [118] */ 32,
+  /* [119] */ 0,
+  /* [120] */ 33,
+  /* [121] */ 0,
+  /* [122] */ 12,
+  /* [123] */ 8,
+  /* [124] */ 34,
+  /* [125] */ 0,
+  /* [126] */ 35,
   /* [127] */ 0,
-  /* [128] */ 11,
-  /* [129] */ 7,
-  /* [130] */ 11,
-  /* [131] */ 10,
+  /* [128] */ 36,
+  /* [129] */ 0,
+  /* [130] */ 13,
+  /* [131] */ 0,
   /* [132] */ 11,
-  /* [133] */ 9,
-  /* [134] */ 47,
-  /* [135] */ 0,
+  /* [133] */ 7,
+  /* [134] */ 12,
+  /* [135] */ 9,
   /* [136] */ 30,
-  /* [137] */ 0,
-  /* [138] */ 13,
-  /* [139] */ 1,
-  /* [140] */ 31,
-  /* [141] */ 0,
-  /* [142] */ 32,
-  /* [143] */ 0,
-  /* [144] */ 33,
-  /* [145] */ 0,
-  /* [146] */ 13,
-  /* [147] */ 10,
-  /* [148] */ 34,
-  /* [149] */ 0,
-  /* [150] */ 35,
+  /* [137] */ 9,
+  /* [138] */ 31,
+  /* [139] */ 9,
+  /* [140] */ 32,
+  /* [141] */ 9,
+  /* [142] */ 33,
+  /* [143] */ 9,
+  /* [144] */ 12,
+  /* [145] */ 7,
+  /* [146] */ 34,
+  /* [147] */ 9,
+  /* [148] */ 35,
+  /* [149] */ 9,
+  /* [150] */ 11,
   /* [151] */ 0,
   /* [152] */ 13,
   /* [153] */ 7,
-  /* [154] */ 36,
-  /* [155] */ 0,
-  /* [156] */ 13,
-  /* [157] */ 8,
-  /* [158] */ 13,
-  /* [159] */ 4,
-  /* [160] */ 14,
+  /* [154] */ 13,
+  /* [155] */ 8,
+  /* [156] */ 11,
+  /* [157] */ 1,
+  /* [158] */ 12,
+  /* [159] */ 1,
+  /* [160] */ 51,
   /* [161] */ 0,
-  /* [162] */ 22,
-  /* [163] */ 9,
-  /* [164] */ 14,
-  /* [165] */ 9,
-  /* [166] */ 14,
+  /* [162] */ 11,
+  /* [163] */ 10,
+  /* [164] */ 11,
+  /* [165] */ 4,
+  /* [166] */ 12,
   /* [167] */ 10,
-  /* [168] */ 15,
-  /* [169] */ 0,
-  /* [170] */ 15,
-  /* [171] */ 9,
-  /* [172] */ 49,
-  /* [173] */ 0,
-  /* [174] */ 15,
-  /* [175] */ 10,
-  /* [176] */ 16,
+  /* [168] */ 12,
+  /* [169] */ 4,
+  /* [170] */ 13,
+  /* [171] */ 1,
+  /* [172] */ 13,
+  /* [173] */ 10,
+  /* [174] */ 13,
+  /* [175] */ 4,
+  /* [176] */ 14,
   /* [177] */ 0,
-  /* [178] */ 16,
+  /* [178] */ 14,
   /* [179] */ 9,
-  /* [180] */ 16,
+  /* [180] */ 14,
   /* [181] */ 10,
-  /* [182] */ 17,
+  /* [182] */ 15,
   /* [183] */ 0,
-  /* [184] */ 17,
+  /* [184] */ 15,
   /* [185] */ 9,
-  /* [186] */ 17,
+  /* [186] */ 15,
   /* [187] */ 10,
-  /* [188] */ 18,
+  /* [188] */ 16,
   /* [189] */ 0,
-  /* [190] */ 18,
+  /* [190] */ 16,
   /* [191] */ 9,
-  /* [192] */ 18,
+  /* [192] */ 16,
   /* [193] */ 10,
-  /* [194] */ 19,
+  /* [194] */ 17,
   /* [195] */ 0,
-  /* [196] */ 19,
+  /* [196] */ 17,
   /* [197] */ 9,
-  /* [198] */ 19,
+  /* [198] */ 17,
   /* [199] */ 10,
-  /* [200] */ 20,
+  /* [200] */ 18,
   /* [201] */ 0,
-  /* [202] */ 20,
+  /* [202] */ 18,
   /* [203] */ 9,
-  /* [204] */ 20,
+  /* [204] */ 18,
   /* [205] */ 10,
-  /* [206] */ 21,
+  /* [206] */ 19,
   /* [207] */ 0,
-  /* [208] */ 51,
-  /* [209] */ 0,
-  /* [210] */ 30,
-  /* [211] */ 9,
-  /* [212] */ 31,
-  /* [213] */ 9,
-  /* [214] */ 32,
+  /* [208] */ 19,
+  /* [209] */ 9,
+  /* [210] */ 19,
+  /* [211] */ 10,
+  /* [212] */ 20,
+  /* [213] */ 0,
+  /* [214] */ 20,
   /* [215] */ 9,
-  /* [216] */ 21,
-  /* [217] */ 9,
-  /* [218] */ 33,
-  /* [219] */ 9,
+  /* [216] */ 20,
+  /* [217] */ 10,
+  /* [218] */ 21,
+  /* [219] */ 0,
   /* [220] */ 21,
-  /* [221] */ 10,
-  /* [222] */ 34,
-  /* [223] */ 9,
-  /* [224] */ 35,
-  /* [225] */ 9,
+  /* [221] */ 9,
+  /* [222] */ 21,
+  /* [223] */ 10,
+  /* [224] */ 22,
+  /* [225] */ 0,
   /* [226] */ 22,
-  /* [227] */ 0,
+  /* [227] */ 9,
   /* [228] */ 22,
   /* [229] */ 10,
-  /* [230] */ 28,
-  /* [231] */ 29,
-  /* [232] */ 40,
-  /* [233] */ 39,
-  /* [234] */ 38,
-  /* [235] */ 37,
-  /* [236] */ 46,
-  /* [237] */ 41,
+  /* [230] */ 37,
+  /* [231] */ 38,
+  /* [232] */ 39,
+  /* [233] */ 40,
+  /* [234] */ 41,
+  /* [235] */ 46,
+  /* [236] */ 28,
+  /* [237] */ 29,
 };
 
 // Assert that the MatcherIndex is big enough to index all the matchers, plus
@@ -2991,862 +2991,862 @@
   {
     /* [0] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [1] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [2] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [3] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [4] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [5] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [6] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [7] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [8] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [9] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [10] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [11] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [12] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [13] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [14] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [15] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [16] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [17] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [18] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [19] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [20] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [21] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [22] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [23] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [24] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [25] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [26] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [27] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [28] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [29] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [30] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [31] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [32] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [33] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [34] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [35] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [36] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [37] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [38] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [39] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [40] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [41] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [42] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [43] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [44] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [45] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [46] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [47] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [48] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [49] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [50] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [51] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [52] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [53] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [54] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [55] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [56] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [57] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [58] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [59] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [60] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [61] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [62] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [63] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [64] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [65] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[214],
+    /* matcher indices */ &kMatcherIndices[140],
   },
   {
     /* [66] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [67] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [68] */
     /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [69] */
     /* usage */ ParameterUsage::kDdx,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [70] */
     /* usage */ ParameterUsage::kDdy,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [71] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[128],
-  },
-  {
-    /* [72] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[214],
-  },
-  {
-    /* [73] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
-  },
-  {
-    /* [74] */
-    /* usage */ ParameterUsage::kCoords,
     /* matcher indices */ &kMatcherIndices[132],
   },
   {
+    /* [72] */
+    /* usage */ ParameterUsage::kComponent,
+    /* matcher indices */ &kMatcherIndices[16],
+  },
+  {
+    /* [73] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[118],
+  },
+  {
+    /* [74] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[236],
+  },
+  {
     /* [75] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [76] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[24],
   },
   {
     /* [77] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[128],
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [78] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[212],
+    /* matcher indices */ &kMatcherIndices[231],
   },
   {
     /* [79] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* matcher indices */ &kMatcherIndices[237],
   },
   {
     /* [80] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [81] */
-    /* usage */ ParameterUsage::kDdx,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [82] */
-    /* usage */ ParameterUsage::kDdy,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [83] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[128],
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [84] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[234],
+    /* matcher indices */ &kMatcherIndices[140],
   },
   {
     /* [85] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[231],
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [86] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [87] */
     /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [88] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kBias,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [89] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[128],
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [90] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[214],
+    /* matcher indices */ &kMatcherIndices[231],
   },
   {
     /* [91] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* matcher indices */ &kMatcherIndices[237],
   },
   {
     /* [92] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [93] */
     /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [94] */
-    /* usage */ ParameterUsage::kDdx,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [95] */
-    /* usage */ ParameterUsage::kDdy,
+    /* usage */ ParameterUsage::kOffset,
     /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [96] */
-    /* usage */ ParameterUsage::kComponent,
-    /* matcher indices */ &kMatcherIndices[12],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[231],
   },
   {
     /* [97] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[142],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[237],
   },
   {
     /* [98] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [99] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [100] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[20],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [101] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[128],
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [102] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[138],
   },
   {
     /* [103] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [104] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [105] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kDdx,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [106] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kDdy,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [107] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [108] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[234],
+    /* matcher indices */ &kMatcherIndices[140],
   },
   {
     /* [109] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[231],
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [110] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [111] */
     /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [112] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kDdx,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [113] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[128],
+    /* usage */ ParameterUsage::kDdy,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [114] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[234],
+    /* matcher indices */ &kMatcherIndices[142],
   },
   {
     /* [115] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[231],
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [116] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
     /* [117] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kDdx,
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
     /* [118] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kDdy,
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
     /* [119] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[128],
+    /* matcher indices */ &kMatcherIndices[144],
   },
   {
     /* [120] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[218],
+    /* matcher indices */ &kMatcherIndices[148],
   },
   {
     /* [121] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [122] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[108],
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
     /* [123] */
-    /* usage */ ParameterUsage::kDdx,
-    /* matcher indices */ &kMatcherIndices[108],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [124] */
-    /* usage */ ParameterUsage::kDdy,
-    /* matcher indices */ &kMatcherIndices[108],
+    /* usage */ ParameterUsage::kDdx,
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
     /* [125] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[114],
+    /* usage */ ParameterUsage::kDdy,
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
     /* [126] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [127] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [128] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [129] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [130] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [131] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [132] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[214],
-  },
-  {
-    /* [133] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
-  },
-  {
-    /* [134] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
-  },
-  {
-    /* [135] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [136] */
-    /* usage */ ParameterUsage::kBias,
-    /* matcher indices */ &kMatcherIndices[62],
-  },
-  {
-    /* [137] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[128],
-  },
-  {
-    /* [138] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[234],
-  },
-  {
-    /* [139] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
-  },
-  {
-    /* [140] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
-  },
-  {
-    /* [141] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [142] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[12],
-  },
-  {
-    /* [143] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[128],
-  },
-  {
-    /* [144] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[224],
-  },
-  {
-    /* [145] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
-  },
-  {
-    /* [146] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[108],
-  },
-  {
-    /* [147] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [148] */
-    /* usage */ ParameterUsage::kDdx,
-    /* matcher indices */ &kMatcherIndices[108],
-  },
-  {
-    /* [149] */
-    /* usage */ ParameterUsage::kDdy,
-    /* matcher indices */ &kMatcherIndices[108],
-  },
-  {
-    /* [150] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[224],
-  },
-  {
-    /* [151] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
-  },
-  {
-    /* [152] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[108],
-  },
-  {
-    /* [153] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [154] */
-    /* usage */ ParameterUsage::kBias,
-    /* matcher indices */ &kMatcherIndices[62],
-  },
-  {
-    /* [155] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[218],
-  },
-  {
-    /* [156] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
-  },
-  {
-    /* [157] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[108],
-  },
-  {
-    /* [158] */
-    /* usage */ ParameterUsage::kBias,
-    /* matcher indices */ &kMatcherIndices[62],
-  },
-  {
-    /* [159] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[114],
-  },
-  {
-    /* [160] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[214],
-  },
-  {
-    /* [161] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
-  },
-  {
-    /* [162] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
-  },
-  {
-    /* [163] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [164] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[62],
-  },
-  {
-    /* [165] */
-    /* usage */ ParameterUsage::kComponent,
-    /* matcher indices */ &kMatcherIndices[12],
-  },
-  {
-    /* [166] */
     /* usage */ ParameterUsage::kTexture,
     /* matcher indices */ &kMatcherIndices[140],
   },
   {
-    /* [167] */
+    /* [127] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* matcher indices */ &kMatcherIndices[236],
+  },
+  {
+    /* [128] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[108],
+  },
+  {
+    /* [129] */
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [130] */
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[38],
+  },
+  {
+    /* [131] */
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[132],
+  },
+  {
+    /* [132] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[231],
+  },
+  {
+    /* [133] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[236],
+  },
+  {
+    /* [134] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[108],
+  },
+  {
+    /* [135] */
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [136] */
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[16],
+  },
+  {
+    /* [137] */
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[132],
+  },
+  {
+    /* [138] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [139] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [140] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [141] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [142] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [143] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [144] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [145] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [146] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [147] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [148] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [149] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [150] */
+    /* usage */ ParameterUsage::kComponent,
+    /* matcher indices */ &kMatcherIndices[16],
+  },
+  {
+    /* [151] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[114],
+  },
+  {
+    /* [152] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[236],
+  },
+  {
+    /* [153] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[108],
+  },
+  {
+    /* [154] */
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[132],
+  },
+  {
+    /* [155] */
+    /* usage */ ParameterUsage::kComponent,
+    /* matcher indices */ &kMatcherIndices[16],
+  },
+  {
+    /* [156] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[118],
+  },
+  {
+    /* [157] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[236],
+  },
+  {
+    /* [158] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[108],
+  },
+  {
+    /* [159] */
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[24],
+  },
+  {
+    /* [160] */
+    /* usage */ ParameterUsage::kComponent,
+    /* matcher indices */ &kMatcherIndices[16],
+  },
+  {
+    /* [161] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[126],
+  },
+  {
+    /* [162] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[236],
+  },
+  {
+    /* [163] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[134],
+  },
+  {
+    /* [164] */
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[24],
+  },
+  {
+    /* [165] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[231],
+  },
+  {
+    /* [166] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[236],
+  },
+  {
+    /* [167] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [168] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [169] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[128],
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [170] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[232],
+    /* matcher indices */ &kMatcherIndices[230],
   },
   {
     /* [171] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[231],
+    /* matcher indices */ &kMatcherIndices[237],
   },
   {
     /* [172] */
@@ -3855,123 +3855,123 @@
   },
   {
     /* [173] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [174] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [175] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[235],
+    /* matcher indices */ &kMatcherIndices[231],
   },
   {
     /* [176] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[231],
+    /* matcher indices */ &kMatcherIndices[237],
   },
   {
     /* [177] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [178] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [179] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[128],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [180] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[232],
+    /* matcher indices */ &kMatcherIndices[233],
   },
   {
     /* [181] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* matcher indices */ &kMatcherIndices[237],
   },
   {
     /* [182] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[108],
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
     /* [183] */
     /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [184] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[12],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [185] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[234],
+    /* matcher indices */ &kMatcherIndices[140],
   },
   {
     /* [186] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[231],
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [187] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [188] */
     /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [189] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [190] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[212],
+    /* matcher indices */ &kMatcherIndices[231],
   },
   {
     /* [191] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [192] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [193] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [194] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[128],
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [195] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[218],
+    /* matcher indices */ &kMatcherIndices[138],
   },
   {
     /* [196] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [197] */
@@ -3980,23 +3980,23 @@
   },
   {
     /* [198] */
-    /* usage */ ParameterUsage::kDdx,
-    /* matcher indices */ &kMatcherIndices[108],
+    /* usage */ ParameterUsage::kBias,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [199] */
-    /* usage */ ParameterUsage::kDdy,
-    /* matcher indices */ &kMatcherIndices[108],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [200] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[232],
+    /* matcher indices */ &kMatcherIndices[140],
   },
   {
     /* [201] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[231],
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [202] */
@@ -4006,147 +4006,147 @@
   {
     /* [203] */
     /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [204] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kBias,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [205] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[214],
+    /* matcher indices */ &kMatcherIndices[142],
   },
   {
     /* [206] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [207] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
     /* [208] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kBias,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [209] */
-    /* usage */ ParameterUsage::kBias,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[144],
   },
   {
     /* [210] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[234],
+    /* matcher indices */ &kMatcherIndices[148],
   },
   {
     /* [211] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [212] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
     /* [213] */
     /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [214] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[12],
+    /* usage */ ParameterUsage::kBias,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [215] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[212],
+    /* matcher indices */ &kMatcherIndices[230],
   },
   {
     /* [216] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* matcher indices */ &kMatcherIndices[237],
   },
   {
     /* [217] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [218] */
-    /* usage */ ParameterUsage::kBias,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [219] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[128],
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [220] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[235],
+    /* matcher indices */ &kMatcherIndices[231],
   },
   {
     /* [221] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* matcher indices */ &kMatcherIndices[237],
   },
   {
     /* [222] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [223] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [224] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[128],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [225] */
-    /* usage */ ParameterUsage::kComponent,
-    /* matcher indices */ &kMatcherIndices[12],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[233],
   },
   {
     /* [226] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[142],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[237],
   },
   {
     /* [227] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
     /* [228] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [229] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[20],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [230] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[224],
+    /* matcher indices */ &kMatcherIndices[230],
   },
   {
     /* [231] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* matcher indices */ &kMatcherIndices[237],
   },
   {
     /* [232] */
@@ -4155,148 +4155,148 @@
   },
   {
     /* [233] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [234] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [235] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[234],
+    /* matcher indices */ &kMatcherIndices[231],
   },
   {
     /* [236] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* matcher indices */ &kMatcherIndices[237],
   },
   {
     /* [237] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [238] */
     /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [239] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[128],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [240] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[235],
+    /* matcher indices */ &kMatcherIndices[233],
   },
   {
     /* [241] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[231],
+    /* matcher indices */ &kMatcherIndices[237],
   },
   {
     /* [242] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
     /* [243] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [244] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[128],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [245] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[234],
+    /* matcher indices */ &kMatcherIndices[138],
   },
   {
     /* [246] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[231],
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [247] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [248] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kDdx,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [249] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kDdy,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [250] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[212],
+    /* matcher indices */ &kMatcherIndices[142],
   },
   {
     /* [251] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [252] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
     /* [253] */
     /* usage */ ParameterUsage::kDdx,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
     /* [254] */
     /* usage */ ParameterUsage::kDdy,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
     /* [255] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[232],
+    /* matcher indices */ &kMatcherIndices[146],
   },
   {
     /* [256] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[231],
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [257] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[108],
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
     /* [258] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kDdx,
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
     /* [259] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kDdy,
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
     /* [260] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[218],
+    /* matcher indices */ &kMatcherIndices[138],
   },
   {
     /* [261] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [262] */
@@ -4306,252 +4306,252 @@
   {
     /* [263] */
     /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [264] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[114],
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [265] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[234],
+    /* matcher indices */ &kMatcherIndices[140],
   },
   {
     /* [266] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [267] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [268] */
     /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [269] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[128],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [270] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[234],
+    /* matcher indices */ &kMatcherIndices[142],
   },
   {
     /* [271] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[231],
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [272] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
     /* [273] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [274] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[144],
   },
   {
     /* [275] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[214],
+    /* matcher indices */ &kMatcherIndices[148],
   },
   {
     /* [276] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [277] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
     /* [278] */
     /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [279] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[128],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [280] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[235],
+    /* matcher indices */ &kMatcherIndices[230],
   },
   {
     /* [281] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[231],
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [282] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [283] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [284] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[128],
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [285] */
-    /* usage */ ParameterUsage::kComponent,
-    /* matcher indices */ &kMatcherIndices[12],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[231],
   },
   {
     /* [286] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[150],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [287] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
-  },
-  {
-    /* [288] */
     /* usage */ ParameterUsage::kCoords,
     /* matcher indices */ &kMatcherIndices[108],
   },
   {
-    /* [289] */
+    /* [288] */
     /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[20],
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [289] */
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[16],
   },
   {
     /* [290] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[222],
+    /* matcher indices */ &kMatcherIndices[233],
   },
   {
     /* [291] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [292] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[108],
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
     /* [293] */
-    /* usage */ ParameterUsage::kDdx,
-    /* matcher indices */ &kMatcherIndices[108],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [294] */
-    /* usage */ ParameterUsage::kDdy,
-    /* matcher indices */ &kMatcherIndices[108],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[16],
   },
   {
     /* [295] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[212],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [296] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [297] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[101],
   },
   {
     /* [298] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[101],
   },
   {
     /* [299] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[235],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [300] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[231],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [301] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[101],
   },
   {
     /* [302] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[101],
   },
   {
     /* [303] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[214],
+    /* usage */ ParameterUsage::kComponent,
+    /* matcher indices */ &kMatcherIndices[16],
   },
   {
     /* [304] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[114],
   },
   {
     /* [305] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [306] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [307] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[218],
-  },
-  {
-    /* [308] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
-  },
-  {
-    /* [309] */
     /* usage */ ParameterUsage::kCoords,
     /* matcher indices */ &kMatcherIndices[108],
   },
   {
+    /* [307] */
+    /* usage */ ParameterUsage::kComponent,
+    /* matcher indices */ &kMatcherIndices[16],
+  },
+  {
+    /* [308] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[124],
+  },
+  {
+    /* [309] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[236],
+  },
+  {
     /* [310] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
     /* [311] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[218],
+    /* matcher indices */ &kMatcherIndices[230],
   },
   {
     /* [312] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [313] */
@@ -4561,17 +4561,17 @@
   {
     /* [314] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[114],
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [315] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[233],
+    /* matcher indices */ &kMatcherIndices[231],
   },
   {
     /* [316] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[231],
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [317] */
@@ -4580,38 +4580,38 @@
   },
   {
     /* [318] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [319] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[224],
+    /* matcher indices */ &kMatcherIndices[233],
   },
   {
     /* [320] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [321] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[108],
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
     /* [322] */
     /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [323] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[222],
+    /* matcher indices */ &kMatcherIndices[230],
   },
   {
     /* [324] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* matcher indices */ &kMatcherIndices[237],
   },
   {
     /* [325] */
@@ -4620,58 +4620,58 @@
   },
   {
     /* [326] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [327] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[235],
+    /* matcher indices */ &kMatcherIndices[232],
   },
   {
     /* [328] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* matcher indices */ &kMatcherIndices[237],
   },
   {
     /* [329] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
     /* [330] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[128],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [331] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[234],
+    /* matcher indices */ &kMatcherIndices[138],
   },
   {
     /* [332] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [333] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [334] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [335] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[232],
+    /* matcher indices */ &kMatcherIndices[140],
   },
   {
     /* [336] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [337] */
@@ -4681,77 +4681,77 @@
   {
     /* [338] */
     /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [339] */
-    /* usage */ ParameterUsage::kX,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[142],
   },
   {
     /* [340] */
-    /* usage */ ParameterUsage::kY,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [341] */
-    /* usage */ ParameterUsage::kZ,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
     /* [342] */
-    /* usage */ ParameterUsage::kW,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[144],
   },
   {
     /* [343] */
-    /* usage */ ParameterUsage::kComponent,
-    /* matcher indices */ &kMatcherIndices[12],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[148],
   },
   {
     /* [344] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[140],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [345] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
     /* [346] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [347] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[72],
+    /* matcher indices */ &kMatcherIndices[230],
   },
   {
     /* [348] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[112],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [349] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[12],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [350] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[102],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [351] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[233],
+    /* matcher indices */ &kMatcherIndices[231],
   },
   {
     /* [352] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [353] */
@@ -4760,98 +4760,98 @@
   },
   {
     /* [354] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [355] */
-    /* usage */ ParameterUsage::kComponent,
-    /* matcher indices */ &kMatcherIndices[12],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[233],
   },
   {
     /* [356] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[148],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [357] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
     /* [358] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[108],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [359] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[54],
+    /* matcher indices */ &kMatcherIndices[138],
   },
   {
     /* [360] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[112],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [361] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[12],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [362] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[152],
+    /* usage */ ParameterUsage::kBias,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [363] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[235],
+    /* matcher indices */ &kMatcherIndices[142],
   },
   {
     /* [364] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[231],
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [365] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
     /* [366] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kBias,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [367] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[142],
+    /* matcher indices */ &kMatcherIndices[146],
   },
   {
     /* [368] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[122],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [369] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[20],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
     /* [370] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[67],
+    /* usage */ ParameterUsage::kBias,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [371] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[218],
+    /* matcher indices */ &kMatcherIndices[230],
   },
   {
     /* [372] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* matcher indices */ &kMatcherIndices[237],
   },
   {
     /* [373] */
@@ -4860,38 +4860,38 @@
   },
   {
     /* [374] */
-    /* usage */ ParameterUsage::kBias,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [375] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[232],
   },
   {
     /* [376] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[237],
   },
   {
     /* [377] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
     /* [378] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [379] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[233],
+    /* matcher indices */ &kMatcherIndices[230],
   },
   {
     /* [380] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[231],
+    /* matcher indices */ &kMatcherIndices[237],
   },
   {
     /* [381] */
@@ -4901,37 +4901,37 @@
   {
     /* [382] */
     /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [383] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[222],
+    /* matcher indices */ &kMatcherIndices[232],
   },
   {
     /* [384] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* matcher indices */ &kMatcherIndices[237],
   },
   {
     /* [385] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[108],
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
     /* [386] */
-    /* usage */ ParameterUsage::kBias,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [387] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[233],
+    /* matcher indices */ &kMatcherIndices[138],
   },
   {
     /* [388] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[231],
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [389] */
@@ -4940,303 +4940,303 @@
   },
   {
     /* [390] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [391] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[36],
+    /* matcher indices */ &kMatcherIndices[142],
   },
   {
     /* [392] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[112],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [393] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[12],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
     /* [394] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[156],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [395] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[234],
+    /* matcher indices */ &kMatcherIndices[146],
   },
   {
     /* [396] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[112],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [397] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[12],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
     /* [398] */
     /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[20],
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [399] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[112],
-  },
-  {
-    /* [400] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[112],
-  },
-  {
-    /* [401] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[112],
-  },
-  {
-    /* [402] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[112],
-  },
-  {
-    /* [403] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[235],
-  },
-  {
-    /* [404] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[231],
-  },
-  {
-    /* [405] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
-  },
-  {
-    /* [406] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[62],
-  },
-  {
-    /* [407] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[232],
-  },
-  {
-    /* [408] */
-    /* usage */ ParameterUsage::kSampler,
     /* matcher indices */ &kMatcherIndices[230],
   },
   {
-    /* [409] */
+    /* [400] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[236],
+  },
+  {
+    /* [401] */
     /* usage */ ParameterUsage::kCoords,
     /* matcher indices */ &kMatcherIndices[108],
   },
   {
-    /* [410] */
+    /* [402] */
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [403] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[232],
+  },
+  {
+    /* [404] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[236],
+  },
+  {
+    /* [405] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[134],
+  },
+  {
+    /* [406] */
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [407] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[66],
+  },
+  {
+    /* [408] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[150],
+  },
+  {
+    /* [409] */
     /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[16],
+  },
+  {
+    /* [410] */
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[110],
   },
   {
     /* [411] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[104],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[78],
   },
   {
     /* [412] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[104],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[150],
   },
   {
     /* [413] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[104],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[16],
   },
   {
     /* [414] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[104],
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[152],
   },
   {
     /* [415] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[126],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[90],
   },
   {
     /* [416] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[126],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[150],
   },
   {
     /* [417] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[126],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[16],
   },
   {
     /* [418] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[126],
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[154],
   },
   {
     /* [419] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[234],
+    /* matcher indices */ &kMatcherIndices[118],
   },
   {
     /* [420] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[156],
   },
   {
     /* [421] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[24],
   },
   {
     /* [422] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[61],
   },
   {
     /* [423] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[231],
   },
   {
     /* [424] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[150],
   },
   {
     /* [425] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[95],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[16],
   },
   {
     /* [426] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[95],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[24],
   },
   {
     /* [427] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* usage */ ParameterUsage::kX,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [428] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* usage */ ParameterUsage::kY,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [429] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[95],
+    /* usage */ ParameterUsage::kZ,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [430] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[95],
+    /* usage */ ParameterUsage::kW,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [431] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[235],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [432] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [433] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [434] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[128],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [435] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[212],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[150],
   },
   {
     /* [436] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[150],
   },
   {
     /* [437] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[150],
   },
   {
     /* [438] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[128],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[150],
   },
   {
     /* [439] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[212],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[102],
   },
   {
     /* [440] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[102],
   },
   {
     /* [441] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[102],
   },
   {
     /* [442] */
-    /* usage */ ParameterUsage::kBias,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[102],
   },
   {
     /* [443] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[235],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[130],
   },
   {
     /* [444] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[130],
   },
   {
     /* [445] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[130],
   },
   {
     /* [446] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[130],
   },
   {
     /* [447] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[235],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [448] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [449] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [450] */
@@ -5256,17 +5256,17 @@
   {
     /* [453] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [454] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[101],
   },
   {
     /* [455] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[101],
   },
   {
     /* [456] */
@@ -5276,12 +5276,12 @@
   {
     /* [457] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[101],
   },
   {
     /* [458] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[101],
   },
   {
     /* [459] */
@@ -5291,117 +5291,117 @@
   {
     /* [460] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[95],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [461] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[95],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [462] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[210],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [463] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [464] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [465] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[212],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [466] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [467] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [468] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[218],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [469] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [470] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[108],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [471] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[222],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [472] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [473] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[108],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [474] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[235],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [475] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [476] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [477] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [478] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [479] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [480] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [481] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [482] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[35],
   },
   {
     /* [483] */
@@ -5416,562 +5416,562 @@
   {
     /* [485] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[35],
   },
   {
     /* [486] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[233],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [487] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [488] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[33],
+  },
+  {
+    /* [489] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [490] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [491] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [492] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
+  },
+  {
+    /* [493] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
+  },
+  {
+    /* [494] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
+  },
+  {
+    /* [495] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[230],
+  },
+  {
+    /* [496] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[236],
+  },
+  {
+    /* [497] */
     /* usage */ ParameterUsage::kCoords,
     /* matcher indices */ &kMatcherIndices[108],
   },
   {
-    /* [489] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[51],
-  },
-  {
-    /* [490] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[104],
-  },
-  {
-    /* [491] */
-    /* 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],
-  },
-  {
-    /* [496] */
-    /* usage */ ParameterUsage::kY,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [497] */
-    /* usage */ ParameterUsage::kZ,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
     /* [498] */
-    /* usage */ ParameterUsage::kXy,
-    /* matcher indices */ &kMatcherIndices[112],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[232],
   },
   {
     /* [499] */
-    /* usage */ ParameterUsage::kZ,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [500] */
-    /* usage */ ParameterUsage::kW,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
     /* [501] */
-    /* usage */ ParameterUsage::kX,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [502] */
-    /* usage */ ParameterUsage::kYz,
-    /* matcher indices */ &kMatcherIndices[112],
-  },
-  {
-    /* [503] */
-    /* usage */ ParameterUsage::kW,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [504] */
-    /* usage */ ParameterUsage::kX,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [505] */
-    /* usage */ ParameterUsage::kY,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [506] */
-    /* usage */ ParameterUsage::kZw,
-    /* matcher indices */ &kMatcherIndices[112],
-  },
-  {
-    /* [507] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[236],
-  },
-  {
-    /* [508] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
-  },
-  {
-    /* [509] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
-  },
-  {
-    /* [510] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[212],
-  },
-  {
-    /* [511] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
-  },
-  {
-    /* [512] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
-  },
-  {
-    /* [513] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[236],
-  },
-  {
-    /* [514] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
-  },
-  {
-    /* [515] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
-  },
-  {
-    /* [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],
-  },
-  {
-    /* [545] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[156],
-  },
-  {
-    /* [546] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[112],
-  },
-  {
-    /* [547] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[112],
-  },
-  {
-    /* [548] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[112],
-  },
-  {
-    /* [549] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[42],
-  },
-  {
-    /* [550] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[112],
-  },
-  {
-    /* [551] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[156],
-  },
-  {
-    /* [552] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [553] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [554] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[41],
-  },
-  {
-    /* [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],
-  },
-  {
-    /* [565] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[114],
-  },
-  {
-    /* [566] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[156],
-  },
-  {
-    /* [567] */
     /* usage */ ParameterUsage::kTexture,
     /* matcher indices */ &kMatcherIndices[136],
   },
   {
-    /* [568] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[12],
-  },
-  {
-    /* [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] */
+    /* [502] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
-    /* [575] */
+    /* [503] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[38],
+  },
+  {
+    /* [504] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[138],
+  },
+  {
+    /* [505] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[236],
+  },
+  {
+    /* [506] */
     /* usage */ ParameterUsage::kCoords,
     /* matcher indices */ &kMatcherIndices[108],
   },
   {
-    /* [576] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[126],
+    /* [507] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[142],
   },
   {
-    /* [577] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[126],
+    /* [508] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
-    /* [578] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[126],
+    /* [509] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
-    /* [579] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* [510] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[146],
   },
   {
-    /* [580] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* [511] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
-    /* [581] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* [512] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
-    /* [582] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* [513] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[230],
   },
   {
-    /* [583] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* [514] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
-    /* [584] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* [515] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
-    /* [585] */
+    /* [516] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[232],
+  },
+  {
+    /* [517] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[236],
+  },
+  {
+    /* [518] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[134],
+  },
+  {
+    /* [519] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[138],
+  },
+  {
+    /* [520] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[236],
+  },
+  {
+    /* [521] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[108],
+  },
+  {
+    /* [522] */
     /* usage */ ParameterUsage::kTexture,
     /* matcher indices */ &kMatcherIndices[235],
   },
   {
-    /* [586] */
+    /* [523] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[236],
+  },
+  {
+    /* [524] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[112],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
-    /* [587] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[12],
-  },
-  {
-    /* [588] */
+    /* [525] */
     /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[60],
+  },
+  {
+    /* [526] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [527] */
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[110],
+  },
+  {
+    /* [528] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[63],
+  },
+  {
+    /* [529] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[150],
+  },
+  {
+    /* [530] */
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[110],
+  },
+  {
+    /* [531] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[69],
+  },
+  {
+    /* [532] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[102],
+  },
+  {
+    /* [533] */
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[110],
+  },
+  {
+    /* [534] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[72],
+  },
+  {
+    /* [535] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [536] */
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[152],
+  },
+  {
+    /* [537] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[75],
+  },
+  {
+    /* [538] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[150],
+  },
+  {
+    /* [539] */
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[152],
+  },
+  {
+    /* [540] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[81],
+  },
+  {
+    /* [541] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[102],
+  },
+  {
+    /* [542] */
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[152],
+  },
+  {
+    /* [543] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[84],
+  },
+  {
+    /* [544] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [545] */
+    /* usage */ ParameterUsage::kValue,
     /* matcher indices */ &kMatcherIndices[154],
   },
   {
-    /* [589] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[122],
-  },
-  {
-    /* [590] */
-    /* usage */ ParameterUsage::kSampleIndex,
-    /* matcher indices */ &kMatcherIndices[20],
-  },
-  {
-    /* [591] */
+    /* [546] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[237],
+    /* matcher indices */ &kMatcherIndices[87],
   },
   {
-    /* [592] */
+    /* [547] */
     /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[150],
+  },
+  {
+    /* [548] */
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[154],
+  },
+  {
+    /* [549] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[93],
+  },
+  {
+    /* [550] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[144],
+  },
+  {
+    /* [551] */
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[154],
+  },
+  {
+    /* [552] */
+    /* usage */ ParameterUsage::kTexture,
     /* matcher indices */ &kMatcherIndices[112],
   },
   {
-    /* [593] */
+    /* [553] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[16],
+  },
+  {
+    /* [554] */
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[24],
+  },
+  {
+    /* [555] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[114],
+  },
+  {
+    /* [556] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[156],
+  },
+  {
+    /* [557] */
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[24],
+  },
+  {
+    /* [558] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[120],
+  },
+  {
+    /* [559] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[158],
+  },
+  {
+    /* [560] */
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[24],
+  },
+  {
+    /* [561] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[128],
+  },
+  {
+    /* [562] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[156],
+  },
+  {
+    /* [563] */
     /* usage */ ParameterUsage::kSampleIndex,
-    /* matcher indices */ &kMatcherIndices[12],
+    /* matcher indices */ &kMatcherIndices[24],
+  },
+  {
+    /* [564] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[230],
+  },
+  {
+    /* [565] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[150],
+  },
+  {
+    /* [566] */
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[16],
+  },
+  {
+    /* [567] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[234],
+  },
+  {
+    /* [568] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[150],
+  },
+  {
+    /* [569] */
+    /* usage */ ParameterUsage::kSampleIndex,
+    /* matcher indices */ &kMatcherIndices[16],
+  },
+  {
+    /* [570] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [571] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [572] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [573] */
+    /* usage */ ParameterUsage::kX,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [574] */
+    /* usage */ ParameterUsage::kY,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [575] */
+    /* usage */ ParameterUsage::kZ,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [576] */
+    /* usage */ ParameterUsage::kXy,
+    /* matcher indices */ &kMatcherIndices[150],
+  },
+  {
+    /* [577] */
+    /* usage */ ParameterUsage::kZ,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [578] */
+    /* usage */ ParameterUsage::kW,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [579] */
+    /* usage */ ParameterUsage::kX,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [580] */
+    /* usage */ ParameterUsage::kYz,
+    /* matcher indices */ &kMatcherIndices[150],
+  },
+  {
+    /* [581] */
+    /* usage */ ParameterUsage::kW,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [582] */
+    /* usage */ ParameterUsage::kX,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [583] */
+    /* usage */ ParameterUsage::kY,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [584] */
+    /* usage */ ParameterUsage::kZw,
+    /* matcher indices */ &kMatcherIndices[150],
+  },
+  {
+    /* [585] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[150],
+  },
+  {
+    /* [586] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[150],
+  },
+  {
+    /* [587] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[150],
+  },
+  {
+    /* [588] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[102],
+  },
+  {
+    /* [589] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[102],
+  },
+  {
+    /* [590] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[102],
+  },
+  {
+    /* [591] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[130],
+  },
+  {
+    /* [592] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[130],
+  },
+  {
+    /* [593] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[130],
   },
   {
     /* [594] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [595] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[95],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [596] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[95],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [597] */
@@ -5981,97 +5981,97 @@
   {
     /* [598] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[102],
   },
   {
     /* [599] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[102],
   },
   {
     /* [600] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [601] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[236],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [602] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[112],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [603] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[0],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [604] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [605] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[0],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [606] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[101],
   },
   {
     /* [607] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[0],
+    /* matcher indices */ &kMatcherIndices[101],
   },
   {
     /* [608] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[101],
   },
   {
     /* [609] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[0],
+    /* matcher indices */ &kMatcherIndices[101],
   },
   {
     /* [610] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [611] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[0],
+    /* matcher indices */ &kMatcherIndices[9],
   },
   {
     /* [612] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [613] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[0],
+    /* matcher indices */ &kMatcherIndices[42],
   },
   {
     /* [614] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [615] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[0],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [616] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [617] */
@@ -6081,17 +6081,17 @@
   {
     /* [618] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [619] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [620] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [621] */
@@ -6101,17 +6101,17 @@
   {
     /* [622] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [623] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[0],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [624] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [625] */
@@ -6121,22 +6121,22 @@
   {
     /* [626] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [627] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[136],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [628] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[12],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [629] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [630] */
@@ -6145,218 +6145,218 @@
   },
   {
     /* [631] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[140],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [632] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[12],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[112],
   },
   {
     /* [633] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[16],
   },
   {
     /* [634] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[114],
   },
   {
     /* [635] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[142],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[16],
   },
   {
     /* [636] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[12],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[118],
   },
   {
     /* [637] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[16],
   },
   {
     /* [638] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[120],
   },
   {
     /* [639] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[144],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[16],
   },
   {
     /* [640] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[12],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[124],
   },
   {
     /* [641] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[10],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[16],
   },
   {
     /* [642] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[10],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[126],
   },
   {
     /* [643] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[148],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[16],
   },
   {
     /* [644] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[12],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[230],
   },
   {
     /* [645] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [646] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[231],
   },
   {
     /* [647] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[150],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [648] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[12],
-  },
-  {
-    /* [649] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
-  },
-  {
-    /* [650] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
-  },
-  {
-    /* [651] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
-  },
-  {
-    /* [652] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [653] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[235],
-  },
-  {
-    /* [654] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [655] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [656] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
-  },
-  {
-    /* [657] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[234],
-  },
-  {
-    /* [658] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [659] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[10],
-  },
-  {
-    /* [660] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[10],
-  },
-  {
-    /* [661] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[233],
-  },
-  {
-    /* [662] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [663] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [664] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [665] */
     /* usage */ ParameterUsage::kTexture,
     /* matcher indices */ &kMatcherIndices[232],
   },
   {
-    /* [666] */
+    /* [649] */
     /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [650] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[233],
+  },
+  {
+    /* [651] */
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [652] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[235],
+  },
+  {
+    /* [653] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[150],
+  },
+  {
+    /* [654] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [655] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [656] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [657] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [658] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [659] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [660] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [661] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [662] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [663] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [664] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [665] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [666] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [667] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [668] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [669] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [670] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [671] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [672] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [673] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [674] */
@@ -6366,47 +6366,47 @@
   {
     /* [675] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [676] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [677] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [678] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [679] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [680] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [681] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[10],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [682] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [683] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[10],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [684] */
@@ -6416,52 +6416,52 @@
   {
     /* [685] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[69],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [686] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[10],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [687] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [688] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[90],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [689] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [690] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[4],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [691] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[18],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [692] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[22],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [693] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [694] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [695] */
@@ -6476,42 +6476,42 @@
   {
     /* [697] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [698] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [699] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [700] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [701] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [702] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [703] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [704] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [705] */
@@ -6521,32 +6521,32 @@
   {
     /* [706] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[96],
   },
   {
     /* [707] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [708] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[22],
   },
   {
     /* [709] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[18],
   },
   {
     /* [710] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [711] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [712] */
@@ -6556,37 +6556,37 @@
   {
     /* [713] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [714] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [715] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[39],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [716] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[39],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [717] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [718] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [719] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [720] */
@@ -6596,42 +6596,42 @@
   {
     /* [721] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [722] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [723] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[39],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [724] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[39],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [725] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [726] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [727] */
-    /* usage */ ParameterUsage::kX,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [728] */
-    /* usage */ ParameterUsage::kYz,
-    /* matcher indices */ &kMatcherIndices[112],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [729] */
@@ -6641,37 +6641,37 @@
   {
     /* [730] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[35],
   },
   {
     /* [731] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* matcher indices */ &kMatcherIndices[35],
   },
   {
     /* [732] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* matcher indices */ &kMatcherIndices[33],
   },
   {
     /* [733] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* matcher indices */ &kMatcherIndices[33],
   },
   {
     /* [734] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [735] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [736] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [737] */
@@ -6681,32 +6681,32 @@
   {
     /* [738] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[35],
   },
   {
     /* [739] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[35],
   },
   {
     /* [740] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[33],
   },
   {
     /* [741] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[33],
   },
   {
     /* [742] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [743] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [744] */
@@ -6716,37 +6716,37 @@
   {
     /* [745] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [746] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[35],
   },
   {
     /* [747] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[35],
   },
   {
     /* [748] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[35],
   },
   {
     /* [749] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[35],
   },
   {
     /* [750] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [751] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [752] */
@@ -6756,17 +6756,17 @@
   {
     /* [753] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [754] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [755] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [756] */
@@ -6776,42 +6776,42 @@
   {
     /* [757] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [758] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[95],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [759] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [760] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[93],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [761] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[95],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [762] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[95],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [763] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[95],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [764] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[95],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [765] */
@@ -6821,12 +6821,12 @@
   {
     /* [766] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [767] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [768] */
@@ -6836,22 +6836,22 @@
   {
     /* [769] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [770] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [771] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [772] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[95],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [773] */
@@ -6861,157 +6861,157 @@
   {
     /* [774] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[93],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [775] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[101],
   },
   {
     /* [776] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[95],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [777] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[104],
+    /* matcher indices */ &kMatcherIndices[99],
   },
   {
     /* [778] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[104],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [779] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[101],
   },
   {
     /* [780] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[93],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [781] */
-    /* usage */ ParameterUsage::kX,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[99],
   },
   {
     /* [782] */
-    /* usage */ ParameterUsage::kY,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [783] */
-    /* usage */ ParameterUsage::kXy,
-    /* matcher indices */ &kMatcherIndices[112],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[101],
   },
   {
     /* [784] */
-    /* usage */ ParameterUsage::kZ,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [785] */
-    /* usage */ ParameterUsage::kXy,
-    /* matcher indices */ &kMatcherIndices[112],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[99],
   },
   {
     /* [786] */
-    /* usage */ ParameterUsage::kZw,
-    /* matcher indices */ &kMatcherIndices[112],
+    /* usage */ ParameterUsage::kX,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [787] */
-    /* usage */ ParameterUsage::kXyz,
-    /* matcher indices */ &kMatcherIndices[104],
+    /* usage */ ParameterUsage::kY,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [788] */
-    /* usage */ ParameterUsage::kW,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kXy,
+    /* matcher indices */ &kMatcherIndices[150],
   },
   {
     /* [789] */
-    /* usage */ ParameterUsage::kX,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kZ,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [790] */
-    /* usage */ ParameterUsage::kZyw,
-    /* matcher indices */ &kMatcherIndices[104],
+    /* usage */ ParameterUsage::kX,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [791] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[112],
+    /* usage */ ParameterUsage::kYz,
+    /* matcher indices */ &kMatcherIndices[150],
   },
   {
     /* [792] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[112],
+    /* usage */ ParameterUsage::kXy,
+    /* matcher indices */ &kMatcherIndices[150],
   },
   {
     /* [793] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* usage */ ParameterUsage::kZw,
+    /* matcher indices */ &kMatcherIndices[150],
   },
   {
     /* [794] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* usage */ ParameterUsage::kXyz,
+    /* matcher indices */ &kMatcherIndices[102],
   },
   {
     /* [795] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[0],
+    /* usage */ ParameterUsage::kW,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [796] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kX,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [797] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kZyw,
+    /* matcher indices */ &kMatcherIndices[102],
   },
   {
     /* [798] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[150],
   },
   {
     /* [799] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[104],
+    /* matcher indices */ &kMatcherIndices[150],
   },
   {
     /* [800] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[104],
+    /* matcher indices */ &kMatcherIndices[102],
   },
   {
     /* [801] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[126],
+    /* matcher indices */ &kMatcherIndices[102],
   },
   {
     /* [802] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[126],
+    /* matcher indices */ &kMatcherIndices[130],
   },
   {
     /* [803] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[130],
   },
   {
     /* [804] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[10],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [805] */
@@ -7021,227 +7021,227 @@
   {
     /* [806] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [807] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[237],
-  },
-  {
-    /* [808] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[154],
-  },
-  {
-    /* [809] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[232],
-  },
-  {
-    /* [810] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[233],
-  },
-  {
-    /* [811] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[234],
-  },
-  {
-    /* [812] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[235],
-  },
-  {
-    /* [813] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[150],
-  },
-  {
-    /* [814] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[148],
-  },
-  {
-    /* [815] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[144],
-  },
-  {
-    /* [816] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[142],
-  },
-  {
-    /* [817] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[140],
-  },
-  {
-    /* [818] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[136],
-  },
-  {
-    /* [819] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[45],
-  },
-  {
-    /* [820] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[232],
-  },
-  {
-    /* [821] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[234],
-  },
-  {
-    /* [822] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[150],
-  },
-  {
-    /* [823] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[142],
-  },
-  {
-    /* [824] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[81],
-  },
-  {
-    /* [825] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[45],
-  },
-  {
-    /* [826] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[84],
-  },
-  {
-    /* [827] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[87],
-  },
-  {
-    /* [828] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[237],
-  },
-  {
-    /* [829] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[232],
-  },
-  {
-    /* [830] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[233],
-  },
-  {
-    /* [831] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[234],
-  },
-  {
-    /* [832] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[235],
-  },
-  {
-    /* [833] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[154],
-  },
-  {
-    /* [834] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[150],
-  },
-  {
-    /* [835] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[148],
-  },
-  {
-    /* [836] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[144],
-  },
-  {
-    /* [837] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[142],
-  },
-  {
-    /* [838] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[140],
-  },
-  {
-    /* [839] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[136],
-  },
-  {
-    /* [840] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[162],
-  },
-  {
-    /* [841] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[95],
-  },
-  {
-    /* [842] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[95],
-  },
-  {
-    /* [843] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[95],
-  },
-  {
-    /* [844] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[95],
-  },
-  {
-    /* [845] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[95],
-  },
-  {
-    /* [846] */
     /* usage */ ParameterUsage::kNone,
     /* matcher indices */ &kMatcherIndices[30],
   },
   {
-    /* [847] */
+    /* [808] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
-    /* [848] */
+    /* [809] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
+  },
+  {
+    /* [810] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[35],
+  },
+  {
+    /* [811] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[33],
+  },
+  {
+    /* [812] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[35],
+  },
+  {
+    /* [813] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[33],
+  },
+  {
+    /* [814] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[0],
+  },
+  {
+    /* [815] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [816] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
+  },
+  {
+    /* [817] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [818] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
+  },
+  {
+    /* [819] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [820] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
+  },
+  {
+    /* [821] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [822] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
+  },
+  {
+    /* [823] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [824] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
+  },
+  {
+    /* [825] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [826] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
+  },
+  {
+    /* [827] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [828] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
+  },
+  {
+    /* [829] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [830] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
+  },
+  {
+    /* [831] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [832] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
+  },
+  {
+    /* [833] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [834] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
+  },
+  {
+    /* [835] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
+  },
+  {
+    /* [836] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
+  },
+  {
+    /* [837] */
     /* usage */ ParameterUsage::kNone,
     /* matcher indices */ &kMatcherIndices[10],
   },
   {
+    /* [838] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[38],
+  },
+  {
+    /* [839] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[36],
+  },
+  {
+    /* [840] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[38],
+  },
+  {
+    /* [841] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[36],
+  },
+  {
+    /* [842] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[38],
+  },
+  {
+    /* [843] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[36],
+  },
+  {
+    /* [844] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[38],
+  },
+  {
+    /* [845] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[36],
+  },
+  {
+    /* [846] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[38],
+  },
+  {
+    /* [847] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[36],
+  },
+  {
+    /* [848] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[38],
+  },
+  {
     /* [849] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[36],
   },
   {
     /* [850] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [851] */
@@ -7251,7 +7251,7 @@
   {
     /* [852] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [853] */
@@ -7261,117 +7261,117 @@
   {
     /* [854] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [855] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [856] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [857] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [858] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [859] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[0],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [860] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[236],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [861] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [862] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [863] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [864] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [865] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[36],
   },
   {
     /* [866] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [867] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[36],
   },
   {
     /* [868] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [869] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[36],
   },
   {
     /* [870] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [871] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [872] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[39],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [873] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [874] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [875] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [876] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [877] */
@@ -7381,57 +7381,57 @@
   {
     /* [878] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [879] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[60],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [880] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [881] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[102],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [882] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[102],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [883] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [884] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[110],
   },
   {
     /* [885] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[110],
   },
   {
     /* [886] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [887] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[36],
   },
   {
     /* [888] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [889] */
@@ -7441,7 +7441,7 @@
   {
     /* [890] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [891] */
@@ -7451,7 +7451,7 @@
   {
     /* [892] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [893] */
@@ -7461,7 +7461,7 @@
   {
     /* [894] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [895] */
@@ -7471,37 +7471,37 @@
   {
     /* [896] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [897] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[60],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [898] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [899] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[60],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [900] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [901] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[60],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [902] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [903] */
@@ -7511,7 +7511,7 @@
   {
     /* [904] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [905] */
@@ -7521,7 +7521,7 @@
   {
     /* [906] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [907] */
@@ -7531,557 +7531,542 @@
   {
     /* [908] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [909] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [910] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [911] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[101],
   },
   {
     /* [912] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[101],
   },
   {
     /* [913] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[101],
   },
   {
     /* [914] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[101],
   },
   {
     /* [915] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[101],
   },
   {
     /* [916] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[112],
   },
   {
     /* [917] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[60],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[114],
   },
   {
     /* [918] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[118],
   },
   {
     /* [919] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[228],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[120],
   },
   {
     /* [920] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[60],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[124],
   },
   {
     /* [921] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[126],
   },
   {
     /* [922] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[128],
   },
   {
     /* [923] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[230],
   },
   {
     /* [924] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[60],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[231],
   },
   {
     /* [925] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[232],
   },
   {
     /* [926] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[60],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[233],
   },
   {
     /* [927] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[234],
   },
   {
     /* [928] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[60],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[48],
   },
   {
     /* [929] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[51],
   },
   {
     /* [930] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[14],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[54],
   },
   {
     /* [931] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[57],
   },
   {
     /* [932] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[235],
   },
   {
     /* [933] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[118],
   },
   {
     /* [934] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[126],
   },
   {
     /* [935] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[4],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[231],
   },
   {
     /* [936] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[233],
   },
   {
     /* [937] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[226],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[54],
   },
   {
     /* [938] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[95],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[112],
   },
   {
     /* [939] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[114],
   },
   {
     /* [940] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[220],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[118],
   },
   {
     /* [941] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[120],
   },
   {
     /* [942] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[124],
   },
   {
     /* [943] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[216],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[126],
   },
   {
     /* [944] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[6],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[230],
   },
   {
     /* [945] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[231],
   },
   {
     /* [946] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[232],
   },
   {
     /* [947] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[233],
   },
   {
     /* [948] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[128],
   },
   {
     /* [949] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[234],
   },
   {
     /* [950] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[112],
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [951] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [952] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[35],
   },
   {
     /* [953] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[122],
+    /* matcher indices */ &kMatcherIndices[33],
   },
   {
     /* [954] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[122],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [955] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[122],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [956] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[122],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [957] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[122],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [958] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[206],
+    /* matcher indices */ &kMatcherIndices[9],
   },
   {
     /* [959] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[104],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [960] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[101],
   },
   {
     /* [961] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [962] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [963] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [964] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[106],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [965] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[106],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [966] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[106],
+    /* matcher indices */ &kMatcherIndices[35],
   },
   {
     /* [967] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[106],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [968] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[106],
+    /* matcher indices */ &kMatcherIndices[150],
   },
   {
     /* [969] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[204],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [970] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[126],
+    /* matcher indices */ &kMatcherIndices[156],
   },
   {
     /* [971] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[156],
   },
   {
     /* [972] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[156],
   },
   {
     /* [973] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[156],
   },
   {
     /* [974] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[156],
   },
   {
     /* [975] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[102],
   },
   {
     /* [976] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [977] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[158],
   },
   {
     /* [978] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[158],
   },
   {
     /* [979] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[138],
+    /* matcher indices */ &kMatcherIndices[158],
   },
   {
     /* [980] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[138],
+    /* matcher indices */ &kMatcherIndices[158],
   },
   {
     /* [981] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[138],
+    /* matcher indices */ &kMatcherIndices[158],
   },
   {
     /* [982] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[138],
+    /* matcher indices */ &kMatcherIndices[130],
   },
   {
     /* [983] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[138],
+    /* matcher indices */ &kMatcherIndices[3],
   },
   {
     /* [984] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[202],
+    /* matcher indices */ &kMatcherIndices[170],
   },
   {
     /* [985] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[160],
+    /* matcher indices */ &kMatcherIndices[170],
   },
   {
     /* [986] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[170],
   },
   {
     /* [987] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[170],
   },
   {
     /* [988] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[164],
+    /* matcher indices */ &kMatcherIndices[170],
   },
   {
     /* [989] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[166],
+    /* matcher indices */ &kMatcherIndices[176],
   },
   {
     /* [990] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[178],
   },
   {
     /* [991] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[168],
+    /* matcher indices */ &kMatcherIndices[180],
   },
   {
     /* [992] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[182],
   },
   {
     /* [993] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[184],
   },
   {
     /* [994] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[170],
+    /* matcher indices */ &kMatcherIndices[186],
   },
   {
     /* [995] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[174],
+    /* matcher indices */ &kMatcherIndices[188],
   },
   {
     /* [996] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* matcher indices */ &kMatcherIndices[190],
   },
   {
     /* [997] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[176],
+    /* matcher indices */ &kMatcherIndices[192],
   },
   {
     /* [998] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[194],
   },
   {
     /* [999] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[196],
   },
   {
     /* [1000] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[178],
+    /* matcher indices */ &kMatcherIndices[198],
   },
   {
     /* [1001] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[180],
+    /* matcher indices */ &kMatcherIndices[200],
   },
   {
     /* [1002] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[200],
+    /* matcher indices */ &kMatcherIndices[202],
   },
   {
     /* [1003] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[182],
+    /* matcher indices */ &kMatcherIndices[204],
   },
   {
     /* [1004] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[206],
   },
   {
     /* [1005] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[208],
   },
   {
     /* [1006] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[184],
+    /* matcher indices */ &kMatcherIndices[210],
   },
   {
     /* [1007] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[186],
+    /* matcher indices */ &kMatcherIndices[212],
   },
   {
     /* [1008] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[198],
+    /* matcher indices */ &kMatcherIndices[214],
   },
   {
     /* [1009] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[188],
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [1010] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[218],
   },
   {
     /* [1011] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[39],
+    /* matcher indices */ &kMatcherIndices[220],
   },
   {
     /* [1012] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[190],
+    /* matcher indices */ &kMatcherIndices[222],
   },
   {
     /* [1013] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[192],
+    /* matcher indices */ &kMatcherIndices[224],
   },
   {
     /* [1014] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[196],
+    /* matcher indices */ &kMatcherIndices[226],
   },
   {
     /* [1015] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[194],
-  },
-  {
-    /* [1016] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[60],
-  },
-  {
-    /* [1017] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[39],
-  },
-  {
-    /* [1018] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[228],
   },
 };
 
@@ -8118,7 +8103,7 @@
   },
   {
     /* [6] */
-    /* name */ "S",
+    /* name */ "L",
     /* matcher index */ 70,
   },
   {
@@ -8133,7 +8118,7 @@
   },
   {
     /* [9] */
-    /* name */ "L",
+    /* name */ "S",
     /* matcher index */ 70,
   },
   {
@@ -8199,7 +8184,7 @@
   {
     /* [22] */
     /* name */ "T",
-    /* matcher index */ 69,
+    /* matcher index */ 59,
   },
   {
     /* [23] */
@@ -8209,72 +8194,72 @@
   {
     /* [24] */
     /* name */ "T",
-    /* matcher index */ 70,
+    /* matcher index */ kNoMatcher,
   },
   {
     /* [25] */
     /* name */ "T",
-    /* matcher index */ 65,
+    /* matcher index */ 70,
   },
   {
     /* [26] */
     /* name */ "T",
-    /* matcher index */ 52,
+    /* matcher index */ 69,
   },
   {
     /* [27] */
     /* name */ "T",
-    /* matcher index */ 53,
+    /* matcher index */ 52,
   },
   {
     /* [28] */
     /* name */ "T",
-    /* matcher index */ 59,
+    /* matcher index */ 64,
   },
   {
     /* [29] */
     /* name */ "T",
-    /* matcher index */ 58,
+    /* matcher index */ 60,
   },
   {
     /* [30] */
     /* name */ "T",
-    /* matcher index */ 55,
+    /* matcher index */ 65,
   },
   {
     /* [31] */
     /* name */ "T",
-    /* matcher index */ 54,
+    /* matcher index */ 5,
   },
   {
     /* [32] */
     /* name */ "T",
-    /* matcher index */ 57,
+    /* matcher index */ 56,
   },
   {
     /* [33] */
     /* name */ "T",
-    /* matcher index */ 56,
+    /* matcher index */ 57,
   },
   {
     /* [34] */
     /* name */ "T",
-    /* matcher index */ kNoMatcher,
+    /* matcher index */ 54,
   },
   {
     /* [35] */
     /* name */ "T",
-    /* matcher index */ 5,
+    /* matcher index */ 55,
   },
   {
     /* [36] */
     /* name */ "T",
-    /* matcher index */ 64,
+    /* matcher index */ 58,
   },
   {
     /* [37] */
     /* name */ "T",
-    /* matcher index */ 60,
+    /* matcher index */ 53,
   },
 };
 
@@ -8296,13 +8281,13 @@
   },
   {
     /* [3] */
-    /* name */ "F",
+    /* name */ "M",
     /* matcher index */ kNoMatcher,
   },
   {
     /* [4] */
-    /* name */ "A",
-    /* matcher index */ 6,
+    /* name */ "N",
+    /* matcher index */ kNoMatcher,
   },
   {
     /* [5] */
@@ -8311,13 +8296,13 @@
   },
   {
     /* [6] */
-    /* name */ "N",
+    /* name */ "F",
     /* matcher index */ kNoMatcher,
   },
   {
     /* [7] */
-    /* name */ "M",
-    /* matcher index */ kNoMatcher,
+    /* name */ "A",
+    /* matcher index */ 6,
   },
   {
     /* [8] */
@@ -8339,8 +8324,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[0],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[839],
-    /* return matcher indices */ &kMatcherIndices[95],
+    /* parameters */ &kParameters[916],
+    /* return matcher indices */ &kMatcherIndices[101],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8351,8 +8336,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[627],
-    /* return matcher indices */ &kMatcherIndices[95],
+    /* parameters */ &kParameters[632],
+    /* return matcher indices */ &kMatcherIndices[101],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8363,8 +8348,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[0],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[838],
-    /* return matcher indices */ &kMatcherIndices[124],
+    /* parameters */ &kParameters[917],
+    /* return matcher indices */ &kMatcherIndices[116],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8375,8 +8360,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[631],
-    /* return matcher indices */ &kMatcherIndices[124],
+    /* parameters */ &kParameters[634],
+    /* return matcher indices */ &kMatcherIndices[116],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8387,8 +8372,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[0],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[837],
-    /* return matcher indices */ &kMatcherIndices[124],
+    /* parameters */ &kParameters[918],
+    /* return matcher indices */ &kMatcherIndices[116],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8399,8 +8384,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[635],
-    /* return matcher indices */ &kMatcherIndices[124],
+    /* parameters */ &kParameters[636],
+    /* return matcher indices */ &kMatcherIndices[116],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8411,8 +8396,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[0],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[836],
-    /* return matcher indices */ &kMatcherIndices[116],
+    /* parameters */ &kParameters[919],
+    /* return matcher indices */ &kMatcherIndices[122],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8423,8 +8408,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[639],
-    /* return matcher indices */ &kMatcherIndices[116],
+    /* parameters */ &kParameters[638],
+    /* return matcher indices */ &kMatcherIndices[122],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8435,8 +8420,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[0],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[835],
-    /* return matcher indices */ &kMatcherIndices[124],
+    /* parameters */ &kParameters[920],
+    /* return matcher indices */ &kMatcherIndices[116],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8447,8 +8432,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[643],
-    /* return matcher indices */ &kMatcherIndices[124],
+    /* parameters */ &kParameters[640],
+    /* return matcher indices */ &kMatcherIndices[116],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8459,8 +8444,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[0],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[834],
-    /* return matcher indices */ &kMatcherIndices[124],
+    /* parameters */ &kParameters[921],
+    /* return matcher indices */ &kMatcherIndices[116],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8471,8 +8456,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[647],
-    /* return matcher indices */ &kMatcherIndices[124],
+    /* parameters */ &kParameters[642],
+    /* return matcher indices */ &kMatcherIndices[116],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8483,8 +8468,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[0],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[833],
-    /* return matcher indices */ &kMatcherIndices[124],
+    /* parameters */ &kParameters[922],
+    /* return matcher indices */ &kMatcherIndices[116],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8495,8 +8480,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[832],
-    /* return matcher indices */ &kMatcherIndices[124],
+    /* parameters */ &kParameters[923],
+    /* return matcher indices */ &kMatcherIndices[116],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8507,8 +8492,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[3],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[653],
-    /* return matcher indices */ &kMatcherIndices[124],
+    /* parameters */ &kParameters[644],
+    /* return matcher indices */ &kMatcherIndices[116],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8519,8 +8504,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[831],
-    /* return matcher indices */ &kMatcherIndices[124],
+    /* parameters */ &kParameters[924],
+    /* return matcher indices */ &kMatcherIndices[116],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8531,8 +8516,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[3],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[657],
-    /* return matcher indices */ &kMatcherIndices[124],
+    /* parameters */ &kParameters[646],
+    /* return matcher indices */ &kMatcherIndices[116],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8543,8 +8528,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[830],
-    /* return matcher indices */ &kMatcherIndices[124],
+    /* parameters */ &kParameters[925],
+    /* return matcher indices */ &kMatcherIndices[116],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8555,8 +8540,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[3],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[661],
-    /* return matcher indices */ &kMatcherIndices[124],
+    /* parameters */ &kParameters[648],
+    /* return matcher indices */ &kMatcherIndices[116],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8567,8 +8552,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[829],
-    /* return matcher indices */ &kMatcherIndices[124],
+    /* parameters */ &kParameters[926],
+    /* return matcher indices */ &kMatcherIndices[116],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8579,8 +8564,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[3],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[665],
-    /* return matcher indices */ &kMatcherIndices[124],
+    /* parameters */ &kParameters[650],
+    /* return matcher indices */ &kMatcherIndices[116],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8591,8 +8576,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[828],
-    /* return matcher indices */ &kMatcherIndices[124],
+    /* parameters */ &kParameters[927],
+    /* return matcher indices */ &kMatcherIndices[116],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8602,9 +8587,9 @@
     /* num template types */ 0,
     /* num template numbers */ 2,
     /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[3],
-    /* parameters */ &kParameters[827],
-    /* return matcher indices */ &kMatcherIndices[95],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[928],
+    /* return matcher indices */ &kMatcherIndices[101],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8614,9 +8599,9 @@
     /* num template types */ 0,
     /* num template numbers */ 2,
     /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[3],
-    /* parameters */ &kParameters[826],
-    /* return matcher indices */ &kMatcherIndices[124],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[929],
+    /* return matcher indices */ &kMatcherIndices[116],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8626,9 +8611,9 @@
     /* num template types */ 0,
     /* num template numbers */ 2,
     /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[3],
-    /* parameters */ &kParameters[825],
-    /* return matcher indices */ &kMatcherIndices[124],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[930],
+    /* return matcher indices */ &kMatcherIndices[116],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8638,9 +8623,9 @@
     /* num template types */ 0,
     /* num template numbers */ 2,
     /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[3],
-    /* parameters */ &kParameters[824],
-    /* return matcher indices */ &kMatcherIndices[116],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[931],
+    /* return matcher indices */ &kMatcherIndices[122],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8651,81 +8636,81 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[860],
-    /* return matcher indices */ &kMatcherIndices[124],
+    /* parameters */ &kParameters[932],
+    /* return matcher indices */ &kMatcherIndices[116],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [27] */
-    /* num parameters */ 4,
+    /* num parameters */ 3,
     /* num template types */ 0,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[295],
-    /* return matcher indices */ &kMatcherIndices[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[501],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [28] */
-    /* num parameters */ 5,
+    /* num parameters */ 3,
     /* num template types */ 0,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[190],
-    /* return matcher indices */ &kMatcherIndices[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[504],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [29] */
-    /* num parameters */ 5,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[2],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[160],
-    /* return matcher indices */ &kMatcherIndices[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [30] */
-    /* num parameters */ 6,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[2],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[72],
-    /* return matcher indices */ &kMatcherIndices[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [31] */
     /* num parameters */ 4,
     /* num template types */ 0,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[307],
-    /* return matcher indices */ &kMatcherIndices[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[331],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [30] */
+    /* num parameters */ 4,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[2],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[335],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [31] */
+    /* num parameters */ 5,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[2],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[185],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [32] */
-    /* num parameters */ 5,
+    /* num parameters */ 3,
     /* num template types */ 0,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[260],
-    /* return matcher indices */ &kMatcherIndices[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[507],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
@@ -8735,21 +8720,21 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[323],
-    /* return matcher indices */ &kMatcherIndices[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[339],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [34] */
-    /* num parameters */ 5,
-    /* num template types */ 1,
+    /* num parameters */ 3,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[2],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[230],
-    /* return matcher indices */ &kMatcherIndices[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[510],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
@@ -8757,83 +8742,83 @@
     /* num parameters */ 4,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[3],
+    /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[443],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[343],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [36] */
-    /* num parameters */ 5,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[3],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[220],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [37] */
-    /* num parameters */ 5,
-    /* num template types */ 2,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[2],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[210],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [38] */
-    /* num parameters */ 6,
-    /* num template types */ 2,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[2],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[138],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [39] */
-    /* num parameters */ 4,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[3],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[351],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [40] */
-    /* num parameters */ 5,
-    /* num template types */ 2,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[2],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[180],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [41] */
     /* 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[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline, OverloadFlag::kIsDeprecated),
+    /* parameters */ &kParameters[513],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [37] */
+    /* num parameters */ 4,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[347],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [38] */
+    /* num parameters */ 4,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[2],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[351],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [39] */
+    /* num parameters */ 5,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[2],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[190],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [40] */
+    /* num parameters */ 3,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[516],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [41] */
+    /* num parameters */ 4,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[2],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[355],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
@@ -8841,10 +8826,10 @@
     /* num parameters */ 0,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[27],
+    /* template types */ &kTemplateTypes[37],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1019],
-    /* return matcher indices */ &kMatcherIndices[126],
+    /* parameters */ &kParameters[1016],
+    /* return matcher indices */ &kMatcherIndices[130],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Zero,
   },
@@ -8853,10 +8838,10 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[27],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[970],
-    /* return matcher indices */ &kMatcherIndices[126],
+    /* parameters */ &kParameters[982],
+    /* return matcher indices */ &kMatcherIndices[130],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Identity,
   },
@@ -8865,10 +8850,10 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[27],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[971],
-    /* return matcher indices */ &kMatcherIndices[126],
+    /* parameters */ &kParameters[983],
+    /* return matcher indices */ &kMatcherIndices[130],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::VecSplat,
   },
@@ -8877,10 +8862,10 @@
     /* num parameters */ 4,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[27],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[339],
-    /* return matcher indices */ &kMatcherIndices[126],
+    /* parameters */ &kParameters[427],
+    /* return matcher indices */ &kMatcherIndices[130],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::VecInitS,
   },
@@ -8889,10 +8874,10 @@
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[27],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[498],
-    /* return matcher indices */ &kMatcherIndices[126],
+    /* parameters */ &kParameters[576],
+    /* return matcher indices */ &kMatcherIndices[130],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::VecInitM,
   },
@@ -8901,10 +8886,10 @@
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[27],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[501],
-    /* return matcher indices */ &kMatcherIndices[126],
+    /* parameters */ &kParameters[579],
+    /* return matcher indices */ &kMatcherIndices[130],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::VecInitM,
   },
@@ -8913,10 +8898,10 @@
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[27],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[504],
-    /* return matcher indices */ &kMatcherIndices[126],
+    /* parameters */ &kParameters[582],
+    /* return matcher indices */ &kMatcherIndices[130],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::VecInitM,
   },
@@ -8925,10 +8910,10 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[27],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[785],
-    /* return matcher indices */ &kMatcherIndices[126],
+    /* parameters */ &kParameters[792],
+    /* return matcher indices */ &kMatcherIndices[130],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::VecInitM,
   },
@@ -8937,10 +8922,10 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[27],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[787],
-    /* return matcher indices */ &kMatcherIndices[126],
+    /* parameters */ &kParameters[794],
+    /* return matcher indices */ &kMatcherIndices[130],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::VecInitM,
   },
@@ -8949,10 +8934,10 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[27],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[789],
-    /* return matcher indices */ &kMatcherIndices[126],
+    /* parameters */ &kParameters[796],
+    /* return matcher indices */ &kMatcherIndices[130],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::VecInitM,
   },
@@ -8963,8 +8948,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[12],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[979],
-    /* return matcher indices */ &kMatcherIndices[102],
+    /* parameters */ &kParameters[984],
+    /* return matcher indices */ &kMatcherIndices[110],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
   },
@@ -8975,8 +8960,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[980],
-    /* return matcher indices */ &kMatcherIndices[146],
+    /* parameters */ &kParameters[985],
+    /* return matcher indices */ &kMatcherIndices[172],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
   },
@@ -8987,7 +8972,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[981],
+    /* parameters */ &kParameters[986],
     /* return matcher indices */ &kMatcherIndices[152],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
@@ -8999,8 +8984,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[18],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[982],
-    /* return matcher indices */ &kMatcherIndices[156],
+    /* parameters */ &kParameters[987],
+    /* return matcher indices */ &kMatcherIndices[154],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
   },
@@ -9011,81 +8996,81 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[20],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[983],
-    /* return matcher indices */ &kMatcherIndices[158],
+    /* parameters */ &kParameters[988],
+    /* return matcher indices */ &kMatcherIndices[174],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
   },
   {
     /* [57] */
-    /* num parameters */ 3,
+    /* num parameters */ 4,
     /* num template types */ 0,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[462],
-    /* return matcher indices */ &kMatcherIndices[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[387],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [58] */
-    /* num parameters */ 3,
+    /* num parameters */ 5,
     /* num template types */ 0,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[465],
-    /* return matcher indices */ &kMatcherIndices[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[260],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [59] */
-    /* num parameters */ 4,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[435],
-    /* return matcher indices */ &kMatcherIndices[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [60] */
-    /* num parameters */ 4,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[2],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[303],
-    /* return matcher indices */ &kMatcherIndices[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [61] */
     /* num parameters */ 5,
     /* num template types */ 1,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[275],
-    /* return matcher indices */ &kMatcherIndices[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[265],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
-    /* [62] */
-    /* num parameters */ 3,
+    /* [60] */
+    /* num parameters */ 6,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[2],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[126],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [61] */
+    /* num parameters */ 4,
     /* num template types */ 0,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[468],
-    /* return matcher indices */ &kMatcherIndices[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[391],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [62] */
+    /* num parameters */ 5,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[270],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
@@ -9095,21 +9080,21 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[311],
-    /* return matcher indices */ &kMatcherIndices[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[395],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [64] */
-    /* num parameters */ 3,
-    /* num template types */ 0,
+    /* num parameters */ 5,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
+    /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[471],
-    /* return matcher indices */ &kMatcherIndices[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[275],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
@@ -9117,135 +9102,219 @@
     /* num parameters */ 4,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[2],
+    /* template types */ &kTemplateTypes[3],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[319],
-    /* return matcher indices */ &kMatcherIndices[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[399],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [66] */
-    /* num parameters */ 3,
-    /* num template types */ 0,
+    /* num parameters */ 5,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
+    /* template types */ &kTemplateTypes[3],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[474],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[280],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [67] */
-    /* num parameters */ 4,
-    /* num template types */ 0,
+    /* num parameters */ 5,
+    /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
+    /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[327],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[285],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [68] */
-    /* num parameters */ 4,
-    /* num template types */ 1,
+    /* num parameters */ 6,
+    /* num template types */ 2,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[331],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[132],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [69] */
-    /* num parameters */ 5,
+    /* num parameters */ 4,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[2],
+    /* template types */ &kTemplateTypes[3],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[235],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[403],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [70] */
-    /* num parameters */ 3,
-    /* num template types */ 0,
+    /* num parameters */ 5,
+    /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
+    /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[486],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[290],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [71] */
     /* num parameters */ 4,
-    /* num template types */ 1,
+    /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[2],
+    /* template types */ &kTemplateTypes[0],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[335],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[303],
+    /* return matcher indices */ &kMatcherIndices[130],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [72] */
-    /* num parameters */ 3,
-    /* num template types */ 1,
+    /* num parameters */ 5,
+    /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[1],
+    /* template types */ &kTemplateTypes[0],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[516],
-    /* return matcher indices */ nullptr,
+    /* parameters */ &kParameters[150],
+    /* return matcher indices */ &kMatcherIndices[130],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [73] */
-    /* num parameters */ 3,
-    /* num template types */ 1,
+    /* num parameters */ 5,
+    /* num template types */ 3,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[1],
+    /* template types */ &kTemplateTypes[0],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[519],
-    /* return matcher indices */ nullptr,
+    /* parameters */ &kParameters[155],
+    /* return matcher indices */ &kMatcherIndices[130],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [74] */
-    /* num parameters */ 4,
-    /* num template types */ 2,
+    /* num parameters */ 6,
+    /* num template types */ 3,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[1],
+    /* template types */ &kTemplateTypes[0],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[347],
-    /* return matcher indices */ nullptr,
+    /* parameters */ &kParameters[72],
+    /* return matcher indices */ &kMatcherIndices[130],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [75] */
-    /* num parameters */ 3,
-    /* num template types */ 1,
+    /* 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[522],
-    /* return matcher indices */ nullptr,
+    /* parameters */ &kParameters[307],
+    /* return matcher indices */ &kMatcherIndices[130],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [76] */
+    /* num parameters */ 5,
+    /* num template types */ 3,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[0],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[160],
+    /* return matcher indices */ &kMatcherIndices[130],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [77] */
+    /* num parameters */ 3,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[495],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [78] */
+    /* num parameters */ 4,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[311],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [79] */
+    /* num parameters */ 4,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[2],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[315],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [80] */
+    /* num parameters */ 5,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[2],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[165],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [81] */
+    /* num parameters */ 3,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[498],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [82] */
+    /* num parameters */ 4,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[2],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[319],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [83] */
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 0,
@@ -9257,7 +9326,7 @@
     /* const eval */ nullptr,
   },
   {
-    /* [77] */
+    /* [84] */
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 0,
@@ -9269,31 +9338,79 @@
     /* const eval */ nullptr,
   },
   {
-    /* [78] */
+    /* [85] */
     /* num parameters */ 4,
     /* num template types */ 2,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[1],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[359],
+    /* parameters */ &kParameters[407],
     /* return matcher indices */ nullptr,
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
-    /* [79] */
+    /* [86] */
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[1],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[489],
+    /* parameters */ &kParameters[531],
     /* return matcher indices */ nullptr,
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
-    /* [80] */
+    /* [87] */
+    /* num parameters */ 3,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[1],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[534],
+    /* 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[537],
+    /* return matcher indices */ nullptr,
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [89] */
+    /* num parameters */ 4,
+    /* num template types */ 2,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[1],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[411],
+    /* return matcher indices */ nullptr,
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [90] */
+    /* num parameters */ 3,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[1],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[540],
+    /* 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,
@@ -9305,158 +9422,26 @@
     /* const eval */ nullptr,
   },
   {
-    /* [81] */
-    /* num parameters */ 3,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[1],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[549],
-    /* return matcher indices */ nullptr,
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [82] */
-    /* num parameters */ 4,
-    /* num template types */ 2,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[1],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[391],
-    /* return matcher indices */ nullptr,
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [83] */
-    /* num parameters */ 3,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[564],
-    /* return matcher indices */ nullptr,
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [84] */
-    /* num parameters */ 4,
-    /* num template types */ 2,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[0],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[343],
-    /* return matcher indices */ &kMatcherIndices[126],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [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[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 parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[2],
+    /* template types */ &kTemplateTypes[1],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[419],
-    /* return matcher indices */ &kMatcherIndices[102],
+    /* parameters */ &kParameters[546],
+    /* return matcher indices */ nullptr,
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [93] */
-    /* num parameters */ 5,
-    /* num template types */ 1,
+    /* num parameters */ 4,
+    /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[2],
+    /* template types */ &kTemplateTypes[1],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[265],
-    /* return matcher indices */ &kMatcherIndices[102],
+    /* parameters */ &kParameters[415],
+    /* return matcher indices */ nullptr,
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -9467,104 +9452,104 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[573],
-    /* return matcher indices */ &kMatcherIndices[102],
+    /* parameters */ &kParameters[549],
+    /* return matcher indices */ nullptr,
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [95] */
-    /* num parameters */ 4,
+    /* num parameters */ 0,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[2],
+    /* template types */ &kTemplateTypes[37],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[407],
+    /* parameters */ &kParameters[1016],
     /* return matcher indices */ &kMatcherIndices[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Zero,
   },
   {
     /* [96] */
-    /* num parameters */ 0,
+    /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[27],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1019],
-    /* return matcher indices */ &kMatcherIndices[104],
+    /* parameters */ &kParameters[975],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Zero,
+    /* const eval */ &ConstEval::Identity,
   },
   {
     /* [97] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[27],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[959],
-    /* return matcher indices */ &kMatcherIndices[104],
-    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Identity,
-  },
-  {
-    /* [98] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[960],
-    /* return matcher indices */ &kMatcherIndices[104],
+    /* parameters */ &kParameters[976],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::VecSplat,
   },
   {
-    /* [99] */
+    /* [98] */
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[27],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[495],
-    /* return matcher indices */ &kMatcherIndices[104],
+    /* parameters */ &kParameters[573],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::VecInitS,
   },
   {
+    /* [99] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[27],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[788],
+    /* return matcher indices */ &kMatcherIndices[102],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::VecInitM,
+  },
+  {
     /* [100] */
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[27],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[783],
-    /* return matcher indices */ &kMatcherIndices[104],
+    /* parameters */ &kParameters[790],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::VecInitM,
   },
   {
     /* [101] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
+    /* num parameters */ 1,
+    /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[12],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[727],
-    /* return matcher indices */ &kMatcherIndices[104],
-    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::VecInitM,
+    /* parameters */ &kParameters[977],
+    /* return matcher indices */ &kMatcherIndices[134],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Conv,
   },
   {
     /* [102] */
     /* num parameters */ 1,
     /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[12],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[964],
-    /* return matcher indices */ &kMatcherIndices[108],
+    /* parameters */ &kParameters[978],
+    /* return matcher indices */ &kMatcherIndices[166],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
   },
@@ -9573,10 +9558,10 @@
     /* num parameters */ 1,
     /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[14],
+    /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[965],
-    /* return matcher indices */ &kMatcherIndices[110],
+    /* parameters */ &kParameters[979],
+    /* return matcher indices */ &kMatcherIndices[144],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
   },
@@ -9585,10 +9570,10 @@
     /* num parameters */ 1,
     /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[16],
+    /* template types */ &kTemplateTypes[18],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[966],
-    /* return matcher indices */ &kMatcherIndices[114],
+    /* parameters */ &kParameters[980],
+    /* return matcher indices */ &kMatcherIndices[122],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
   },
@@ -9597,24 +9582,24 @@
     /* num parameters */ 1,
     /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[18],
+    /* template types */ &kTemplateTypes[20],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[967],
-    /* return matcher indices */ &kMatcherIndices[116],
+    /* parameters */ &kParameters[981],
+    /* return matcher indices */ &kMatcherIndices[168],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
   },
   {
     /* [106] */
     /* num parameters */ 1,
-    /* num template types */ 2,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[0],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[968],
-    /* return matcher indices */ &kMatcherIndices[118],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Conv,
+    /* parameters */ &kParameters[938],
+    /* return matcher indices */ &kMatcherIndices[101],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
   },
   {
     /* [107] */
@@ -9623,8 +9608,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[0],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[818],
-    /* return matcher indices */ &kMatcherIndices[95],
+    /* parameters */ &kParameters[939],
+    /* return matcher indices */ &kMatcherIndices[101],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -9635,8 +9620,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[0],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[817],
-    /* return matcher indices */ &kMatcherIndices[95],
+    /* parameters */ &kParameters[940],
+    /* return matcher indices */ &kMatcherIndices[101],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -9647,8 +9632,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[0],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[816],
-    /* return matcher indices */ &kMatcherIndices[95],
+    /* parameters */ &kParameters[941],
+    /* return matcher indices */ &kMatcherIndices[101],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -9659,8 +9644,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[0],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[815],
-    /* return matcher indices */ &kMatcherIndices[95],
+    /* parameters */ &kParameters[942],
+    /* return matcher indices */ &kMatcherIndices[101],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -9671,20 +9656,20 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[0],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[814],
-    /* return matcher indices */ &kMatcherIndices[95],
+    /* parameters */ &kParameters[943],
+    /* return matcher indices */ &kMatcherIndices[101],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [112] */
     /* num parameters */ 1,
-    /* num template types */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[0],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[813],
-    /* return matcher indices */ &kMatcherIndices[95],
+    /* parameters */ &kParameters[944],
+    /* return matcher indices */ &kMatcherIndices[101],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -9695,8 +9680,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[812],
-    /* return matcher indices */ &kMatcherIndices[95],
+    /* parameters */ &kParameters[945],
+    /* return matcher indices */ &kMatcherIndices[101],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -9707,8 +9692,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[811],
-    /* return matcher indices */ &kMatcherIndices[95],
+    /* parameters */ &kParameters[946],
+    /* return matcher indices */ &kMatcherIndices[101],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -9719,1388 +9704,1388 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[810],
-    /* return matcher indices */ &kMatcherIndices[95],
+    /* parameters */ &kParameters[947],
+    /* return matcher indices */ &kMatcherIndices[101],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [116] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[809],
-    /* return matcher indices */ &kMatcherIndices[95],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [117] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[28],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[663],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpMultiply,
-  },
-  {
-    /* [118] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[28],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[667],
-    /* return matcher indices */ &kMatcherIndices[30],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpMultiply,
-  },
-  {
-    /* [119] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[28],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[677],
-    /* return matcher indices */ &kMatcherIndices[30],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpMultiply,
-  },
-  {
-    /* [120] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[28],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[679],
-    /* return matcher indices */ &kMatcherIndices[30],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpMultiply,
-  },
-  {
-    /* [121] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[803],
-    /* return matcher indices */ &kMatcherIndices[10],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpMultiply,
-  },
-  {
-    /* [122] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[681],
-    /* return matcher indices */ &kMatcherIndices[10],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpMultiply,
-  },
-  {
-    /* [123] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[1],
-    /* parameters */ &kParameters[683],
-    /* return matcher indices */ &kMatcherIndices[69],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpMultiplyMatVec,
-  },
-  {
-    /* [124] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[1],
-    /* parameters */ &kParameters[685],
-    /* return matcher indices */ &kMatcherIndices[30],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpMultiplyVecMat,
-  },
-  {
-    /* [125] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 3,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[0],
-    /* parameters */ &kParameters[691],
-    /* return matcher indices */ &kMatcherIndices[26],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpMultiplyMatMat,
-  },
-  {
-    /* [126] */
-    /* num parameters */ 0,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[27],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1019],
-    /* return matcher indices */ &kMatcherIndices[112],
-    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Zero,
-  },
-  {
-    /* [127] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[950],
-    /* return matcher indices */ &kMatcherIndices[112],
-    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Identity,
-  },
-  {
-    /* [128] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[951],
-    /* return matcher indices */ &kMatcherIndices[112],
-    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::VecSplat,
-  },
-  {
-    /* [129] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[781],
-    /* return matcher indices */ &kMatcherIndices[112],
-    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::VecInitS,
-  },
-  {
-    /* [130] */
-    /* num parameters */ 1,
-    /* num template types */ 2,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[12],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[953],
-    /* return matcher indices */ &kMatcherIndices[132],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Conv,
-  },
-  {
-    /* [131] */
-    /* num parameters */ 1,
-    /* num template types */ 2,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[14],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[954],
-    /* return matcher indices */ &kMatcherIndices[130],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Conv,
-  },
-  {
-    /* [132] */
-    /* num parameters */ 1,
-    /* num template types */ 2,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[16],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[955],
-    /* return matcher indices */ &kMatcherIndices[128],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Conv,
-  },
-  {
-    /* [133] */
-    /* num parameters */ 1,
-    /* num template types */ 2,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[18],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[956],
-    /* return matcher indices */ &kMatcherIndices[124],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Conv,
-  },
-  {
-    /* [134] */
-    /* num parameters */ 1,
-    /* num template types */ 2,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[957],
-    /* return matcher indices */ &kMatcherIndices[120],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Conv,
-  },
-  {
-    /* [135] */
-    /* num parameters */ 3,
-    /* num template types */ 3,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[7],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[567],
-    /* return matcher indices */ &kMatcherIndices[126],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [136] */
-    /* num parameters */ 3,
-    /* num template types */ 3,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[7],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[570],
-    /* return matcher indices */ &kMatcherIndices[126],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [137] */
-    /* num parameters */ 4,
-    /* num template types */ 4,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[0],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[367],
-    /* return matcher indices */ &kMatcherIndices[126],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [138] */
-    /* num parameters */ 3,
-    /* num template types */ 3,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[7],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[492],
-    /* return matcher indices */ &kMatcherIndices[126],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [139] */
     /* num parameters */ 3,
     /* num template types */ 3,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[4],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[588],
-    /* return matcher indices */ &kMatcherIndices[126],
+    /* parameters */ &kParameters[552],
+    /* return matcher indices */ &kMatcherIndices[130],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
-    /* [140] */
+    /* [117] */
     /* num parameters */ 3,
-    /* num template types */ 2,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[585],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [141] */
-    /* num parameters */ 4,
     /* num template types */ 3,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[1],
+    /* template types */ &kTemplateTypes[4],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[395],
-    /* return matcher indices */ &kMatcherIndices[62],
+    /* parameters */ &kParameters[555],
+    /* return matcher indices */ &kMatcherIndices[130],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
-    /* [142] */
+    /* [118] */
+    /* num parameters */ 4,
+    /* num template types */ 4,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[0],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[419],
+    /* return matcher indices */ &kMatcherIndices[130],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [119] */
+    /* num parameters */ 3,
+    /* num template types */ 3,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[4],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[558],
+    /* return matcher indices */ &kMatcherIndices[130],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [120] */
+    /* num parameters */ 3,
+    /* num template types */ 3,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[7],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[561],
+    /* return matcher indices */ &kMatcherIndices[130],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [121] */
     /* num parameters */ 3,
     /* num template types */ 2,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[5],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[591],
-    /* return matcher indices */ &kMatcherIndices[62],
+    /* parameters */ &kParameters[564],
+    /* return matcher indices */ &kMatcherIndices[38],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
-    /* [143] */
+    /* [122] */
+    /* num parameters */ 4,
+    /* num template types */ 3,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[1],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[423],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [123] */
+    /* num parameters */ 3,
+    /* num template types */ 2,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[567],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [124] */
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[1],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[601],
-    /* return matcher indices */ &kMatcherIndices[102],
+    /* parameters */ &kParameters[652],
+    /* return matcher indices */ &kMatcherIndices[110],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
+    /* [125] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[692],
+    /* return matcher indices */ &kMatcherIndices[3],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpMultiply,
+  },
+  {
+    /* [126] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[694],
+    /* return matcher indices */ &kMatcherIndices[30],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpMultiply,
+  },
+  {
+    /* [127] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[696],
+    /* return matcher indices */ &kMatcherIndices[30],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpMultiply,
+  },
+  {
+    /* [128] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[698],
+    /* return matcher indices */ &kMatcherIndices[30],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpMultiply,
+  },
+  {
+    /* [129] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 2,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[700],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpMultiply,
+  },
+  {
+    /* [130] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 2,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[702],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpMultiply,
+  },
+  {
+    /* [131] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 2,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[1],
+    /* parameters */ &kParameters[704],
+    /* return matcher indices */ &kMatcherIndices[96],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpMultiplyMatVec,
+  },
+  {
+    /* [132] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 2,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[1],
+    /* parameters */ &kParameters[706],
+    /* return matcher indices */ &kMatcherIndices[30],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpMultiplyVecMat,
+  },
+  {
+    /* [133] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 3,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[0],
+    /* parameters */ &kParameters[708],
+    /* return matcher indices */ &kMatcherIndices[26],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpMultiplyMatMat,
+  },
+  {
+    /* [134] */
+    /* num parameters */ 0,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[37],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[1016],
+    /* return matcher indices */ &kMatcherIndices[150],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Zero,
+  },
+  {
+    /* [135] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[27],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[968],
+    /* return matcher indices */ &kMatcherIndices[150],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Identity,
+  },
+  {
+    /* [136] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[27],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[969],
+    /* return matcher indices */ &kMatcherIndices[150],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::VecSplat,
+  },
+  {
+    /* [137] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[27],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[786],
+    /* return matcher indices */ &kMatcherIndices[150],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::VecInitS,
+  },
+  {
+    /* [138] */
+    /* num parameters */ 1,
+    /* num template types */ 2,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[12],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[970],
+    /* return matcher indices */ &kMatcherIndices[108],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Conv,
+  },
+  {
+    /* [139] */
+    /* num parameters */ 1,
+    /* num template types */ 2,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[14],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[971],
+    /* return matcher indices */ &kMatcherIndices[162],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Conv,
+  },
+  {
+    /* [140] */
+    /* num parameters */ 1,
+    /* num template types */ 2,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[16],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[972],
+    /* return matcher indices */ &kMatcherIndices[132],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Conv,
+  },
+  {
+    /* [141] */
+    /* num parameters */ 1,
+    /* num template types */ 2,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[18],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[973],
+    /* return matcher indices */ &kMatcherIndices[116],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Conv,
+  },
+  {
+    /* [142] */
+    /* num parameters */ 1,
+    /* num template types */ 2,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[974],
+    /* return matcher indices */ &kMatcherIndices[164],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Conv,
+  },
+  {
+    /* [143] */
+    /* num parameters */ 4,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[359],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* const eval */ nullptr,
+  },
+  {
     /* [144] */
-    /* num parameters */ 4,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[439],
-    /* return matcher indices */ &kMatcherIndices[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [145] */
-    /* num parameters */ 5,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[215],
-    /* return matcher indices */ &kMatcherIndices[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [146] */
-    /* num parameters */ 5,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[2],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[205],
-    /* return matcher indices */ &kMatcherIndices[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [147] */
-    /* num parameters */ 6,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[2],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[132],
-    /* return matcher indices */ &kMatcherIndices[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [148] */
-    /* num parameters */ 4,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[371],
-    /* return matcher indices */ &kMatcherIndices[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [149] */
-    /* num parameters */ 5,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[155],
-    /* return matcher indices */ &kMatcherIndices[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [150] */
-    /* num parameters */ 4,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[383],
-    /* return matcher indices */ &kMatcherIndices[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [151] */
-    /* num parameters */ 5,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[2],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[150],
-    /* return matcher indices */ &kMatcherIndices[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [152] */
-    /* num parameters */ 5,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[250],
-    /* return matcher indices */ &kMatcherIndices[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [153] */
-    /* num parameters */ 6,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[78],
-    /* return matcher indices */ &kMatcherIndices[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [154] */
-    /* num parameters */ 6,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[2],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[90],
-    /* return matcher indices */ &kMatcherIndices[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [155] */
-    /* num parameters */ 7,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[2],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[65],
-    /* return matcher indices */ &kMatcherIndices[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [156] */
     /* num parameters */ 5,
     /* num template types */ 0,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[195],
-    /* return matcher indices */ &kMatcherIndices[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [157] */
-    /* num parameters */ 6,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[120],
-    /* return matcher indices */ &kMatcherIndices[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [158] */
-    /* num parameters */ 5,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[290],
-    /* return matcher indices */ &kMatcherIndices[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [159] */
-    /* num parameters */ 6,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[2],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[144],
-    /* return matcher indices */ &kMatcherIndices[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [160] */
-    /* 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[182],
-    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Zero,
-  },
-  {
-    /* [161] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1003],
-    /* return matcher indices */ &kMatcherIndices[182],
-    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Identity,
-  },
-  {
-    /* [162] */
-    /* num parameters */ 6,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[102],
-    /* return matcher indices */ &kMatcherIndices[182],
-    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::MatInitS,
-  },
-  {
-    /* [163] */
-    /* num parameters */ 3,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[546],
-    /* return matcher indices */ &kMatcherIndices[182],
-    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::MatInitV,
-  },
-  {
-    /* [164] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[14],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1006],
-    /* return matcher indices */ &kMatcherIndices[186],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Conv,
-  },
-  {
-    /* [165] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[12],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1007],
-    /* return matcher indices */ &kMatcherIndices[184],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Conv,
-  },
-  {
-    /* [166] */
-    /* num parameters */ 4,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[363],
-    /* return matcher indices */ &kMatcherIndices[62],
+    /* return matcher indices */ &kMatcherIndices[110],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
-    /* [167] */
-    /* num parameters */ 5,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[175],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [168] */
-    /* num parameters */ 5,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[2],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[185],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [169] */
-    /* num parameters */ 6,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[2],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[108],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [170] */
-    /* num parameters */ 4,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[379],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [171] */
+    /* [145] */
     /* num parameters */ 5,
     /* num template types */ 1,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[200],
-    /* return matcher indices */ &kMatcherIndices[62],
+    /* return matcher indices */ &kMatcherIndices[110],
     /* 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,
-  },
-  {
-    /* [173] */
-    /* num parameters */ 5,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[280],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [174] */
-    /* num parameters */ 5,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[2],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[270],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [175] */
-    /* num parameters */ 6,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[2],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[114],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [176] */
-    /* num parameters */ 4,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[315],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [177] */
-    /* num parameters */ 5,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[2],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[255],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* 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[22],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1019],
-    /* return matcher indices */ &kMatcherIndices[226],
-    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Zero,
-  },
-  {
-    /* [185] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[937],
-    /* return matcher indices */ &kMatcherIndices[226],
-    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Identity,
-  },
-  {
-    /* [186] */
-    /* num parameters */ 16,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[0],
-    /* return matcher indices */ &kMatcherIndices[226],
-    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::MatInitS,
-  },
-  {
-    /* [187] */
-    /* num parameters */ 4,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[415],
-    /* return matcher indices */ &kMatcherIndices[226],
-    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::MatInitV,
-  },
-  {
-    /* [188] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[14],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[840],
-    /* return matcher indices */ &kMatcherIndices[228],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Conv,
-  },
-  {
-    /* [189] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[12],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[919],
-    /* return matcher indices */ &kMatcherIndices[162],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Conv,
-  },
-  {
-    /* [190] */
-    /* 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[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [191] */
-    /* num parameters */ 5,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[240],
-    /* return matcher indices */ &kMatcherIndices[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [192] */
-    /* num parameters */ 5,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[2],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[245],
-    /* return matcher indices */ &kMatcherIndices[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [193] */
+    /* [146] */
     /* num parameters */ 6,
     /* num template types */ 1,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[84],
-    /* return matcher indices */ &kMatcherIndices[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
-    /* [194] */
+    /* [147] */
     /* num parameters */ 4,
     /* num template types */ 0,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[387],
-    /* return matcher indices */ &kMatcherIndices[102],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[363],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
-    /* [195] */
+    /* [148] */
+    /* num parameters */ 5,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[205],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [149] */
+    /* num parameters */ 4,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[367],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [150] */
     /* num parameters */ 5,
     /* num template types */ 1,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[170],
-    /* return matcher indices */ &kMatcherIndices[102],
+    /* parameters */ &kParameters[210],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [151] */
+    /* num parameters */ 5,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[245],
+    /* return matcher indices */ &kMatcherIndices[110],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
-    /* [196] */
+    /* [152] */
+    /* num parameters */ 6,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[102],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [153] */
+    /* num parameters */ 6,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[2],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[108],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [154] */
+    /* num parameters */ 7,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[2],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[65],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [155] */
+    /* num parameters */ 5,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[250],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [156] */
+    /* num parameters */ 6,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[114],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [157] */
+    /* num parameters */ 5,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[255],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [158] */
+    /* num parameters */ 6,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[2],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[120],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [159] */
+    /* num parameters */ 4,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[323],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [160] */
+    /* num parameters */ 5,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[170],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [161] */
+    /* 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[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [162] */
+    /* num parameters */ 6,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[2],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[78],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [163] */
+    /* num parameters */ 4,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[327],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [164] */
+    /* num parameters */ 5,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[2],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[180],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [165] */
+    /* num parameters */ 4,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[371],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [166] */
+    /* num parameters */ 5,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[215],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [167] */
+    /* num parameters */ 5,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[2],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[220],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [168] */
+    /* num parameters */ 6,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[2],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[90],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [169] */
+    /* num parameters */ 4,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[375],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [170] */
+    /* num parameters */ 5,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[2],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[225],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [171] */
+    /* num parameters */ 4,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[379],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [172] */
+    /* num parameters */ 5,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[230],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [173] */
+    /* num parameters */ 5,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[2],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[235],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [174] */
+    /* num parameters */ 6,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[2],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[96],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [175] */
+    /* num parameters */ 4,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[383],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [176] */
+    /* num parameters */ 5,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[2],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[240],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [177] */
     /* num parameters */ 0,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1019],
-    /* return matcher indices */ &kMatcherIndices[206],
+    /* parameters */ &kParameters[1016],
+    /* return matcher indices */ &kMatcherIndices[176],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Zero,
   },
   {
-    /* [197] */
+    /* [178] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[958],
-    /* return matcher indices */ &kMatcherIndices[206],
+    /* parameters */ &kParameters[989],
+    /* return matcher indices */ &kMatcherIndices[176],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Identity,
   },
   {
-    /* [198] */
-    /* num parameters */ 12,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[28],
-    /* return matcher indices */ &kMatcherIndices[206],
-    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::MatInitS,
-  },
-  {
-    /* [199] */
+    /* [179] */
     /* num parameters */ 4,
     /* num template types */ 1,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[411],
-    /* return matcher indices */ &kMatcherIndices[206],
+    /* parameters */ &kParameters[431],
+    /* return matcher indices */ &kMatcherIndices[176],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::MatInitS,
+  },
+  {
+    /* [180] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[798],
+    /* return matcher indices */ &kMatcherIndices[176],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::MatInitV,
   },
   {
+    /* [181] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[14],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[990],
+    /* return matcher indices */ &kMatcherIndices[180],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Conv,
+  },
+  {
+    /* [182] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[12],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[991],
+    /* return matcher indices */ &kMatcherIndices[178],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Conv,
+  },
+  {
+    /* [183] */
+    /* num parameters */ 0,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[1016],
+    /* return matcher indices */ &kMatcherIndices[182],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Zero,
+  },
+  {
+    /* [184] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[992],
+    /* return matcher indices */ &kMatcherIndices[182],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Identity,
+  },
+  {
+    /* [185] */
+    /* num parameters */ 6,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[138],
+    /* return matcher indices */ &kMatcherIndices[182],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::MatInitS,
+  },
+  {
+    /* [186] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[800],
+    /* return matcher indices */ &kMatcherIndices[182],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::MatInitV,
+  },
+  {
+    /* [187] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[14],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[993],
+    /* return matcher indices */ &kMatcherIndices[186],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Conv,
+  },
+  {
+    /* [188] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[12],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[994],
+    /* return matcher indices */ &kMatcherIndices[184],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Conv,
+  },
+  {
+    /* [189] */
+    /* num parameters */ 0,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[1016],
+    /* return matcher indices */ &kMatcherIndices[188],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Zero,
+  },
+  {
+    /* [190] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[995],
+    /* return matcher indices */ &kMatcherIndices[188],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Identity,
+  },
+  {
+    /* [191] */
+    /* 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[188],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::MatInitS,
+  },
+  {
+    /* [192] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[802],
+    /* return matcher indices */ &kMatcherIndices[188],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::MatInitV,
+  },
+  {
+    /* [193] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[14],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[996],
+    /* return matcher indices */ &kMatcherIndices[192],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Conv,
+  },
+  {
+    /* [194] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[12],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[997],
+    /* return matcher indices */ &kMatcherIndices[190],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Conv,
+  },
+  {
+    /* [195] */
+    /* num parameters */ 0,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[1016],
+    /* return matcher indices */ &kMatcherIndices[194],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Zero,
+  },
+  {
+    /* [196] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[998],
+    /* return matcher indices */ &kMatcherIndices[194],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Identity,
+  },
+  {
+    /* [197] */
+    /* num parameters */ 6,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[144],
+    /* return matcher indices */ &kMatcherIndices[194],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::MatInitS,
+  },
+  {
+    /* [198] */
+    /* num parameters */ 3,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[585],
+    /* return matcher indices */ &kMatcherIndices[194],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::MatInitV,
+  },
+  {
+    /* [199] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[14],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[999],
+    /* return matcher indices */ &kMatcherIndices[198],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Conv,
+  },
+  {
     /* [200] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[14],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[943],
-    /* return matcher indices */ &kMatcherIndices[220],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Conv,
-  },
-  {
-    /* [201] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[12],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[940],
-    /* return matcher indices */ &kMatcherIndices[216],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Conv,
-  },
-  {
-    /* [202] */
-    /* 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[168],
-    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Zero,
-  },
-  {
-    /* [203] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[991],
-    /* return matcher indices */ &kMatcherIndices[168],
-    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Identity,
-  },
-  {
-    /* [204] */
-    /* num parameters */ 6,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[126],
-    /* return matcher indices */ &kMatcherIndices[168],
-    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::MatInitS,
-  },
-  {
-    /* [205] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[799],
-    /* return matcher indices */ &kMatcherIndices[168],
-    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::MatInitV,
-  },
-  {
-    /* [206] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[14],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[994],
-    /* return matcher indices */ &kMatcherIndices[174],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Conv,
-  },
-  {
-    /* [207] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[12],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[995],
-    /* return matcher indices */ &kMatcherIndices[170],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Conv,
-  },
-  {
-    /* [208] */
-    /* 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[160],
-    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Zero,
-  },
-  {
-    /* [209] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[985],
-    /* return matcher indices */ &kMatcherIndices[160],
-    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Identity,
-  },
-  {
-    /* [210] */
-    /* num parameters */ 4,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[375],
-    /* return matcher indices */ &kMatcherIndices[160],
-    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::MatInitS,
-  },
-  {
-    /* [211] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[791],
-    /* return matcher indices */ &kMatcherIndices[160],
-    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::MatInitV,
-  },
-  {
-    /* [212] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[14],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[988],
-    /* return matcher indices */ &kMatcherIndices[166],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Conv,
-  },
-  {
-    /* [213] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[12],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[989],
-    /* return matcher indices */ &kMatcherIndices[164],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Conv,
-  },
-  {
-    /* [214] */
-    /* 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[194],
-    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Zero,
-  },
-  {
-    /* [215] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1015],
-    /* return matcher indices */ &kMatcherIndices[194],
-    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Identity,
-  },
-  {
-    /* [216] */
-    /* num parameters */ 12,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[16],
-    /* return matcher indices */ &kMatcherIndices[194],
-    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::MatInitS,
-  },
-  {
-    /* [217] */
-    /* num parameters */ 3,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[576],
-    /* return matcher indices */ &kMatcherIndices[194],
-    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::MatInitV,
-  },
-  {
-    /* [218] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[14],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1014],
-    /* return matcher indices */ &kMatcherIndices[198],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Conv,
-  },
-  {
-    /* [219] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[12],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1008],
+    /* parameters */ &kParameters[1000],
     /* return matcher indices */ &kMatcherIndices[196],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
   },
   {
-    /* [220] */
+    /* [201] */
     /* num parameters */ 0,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1019],
-    /* return matcher indices */ &kMatcherIndices[188],
+    /* parameters */ &kParameters[1016],
+    /* return matcher indices */ &kMatcherIndices[200],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Zero,
   },
   {
-    /* [221] */
+    /* [202] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1009],
-    /* return matcher indices */ &kMatcherIndices[188],
+    /* parameters */ &kParameters[1001],
+    /* return matcher indices */ &kMatcherIndices[200],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Identity,
   },
   {
-    /* [222] */
+    /* [203] */
     /* num parameters */ 9,
     /* num template types */ 1,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[40],
-    /* return matcher indices */ &kMatcherIndices[188],
+    /* return matcher indices */ &kMatcherIndices[200],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::MatInitS,
   },
   {
-    /* [223] */
+    /* [204] */
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[561],
-    /* return matcher indices */ &kMatcherIndices[188],
+    /* parameters */ &kParameters[588],
+    /* return matcher indices */ &kMatcherIndices[200],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::MatInitV,
   },
   {
-    /* [224] */
+    /* [205] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1012],
-    /* return matcher indices */ &kMatcherIndices[192],
+    /* parameters */ &kParameters[1002],
+    /* return matcher indices */ &kMatcherIndices[204],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
   },
   {
-    /* [225] */
+    /* [206] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[12],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1013],
-    /* return matcher indices */ &kMatcherIndices[190],
+    /* parameters */ &kParameters[1003],
+    /* return matcher indices */ &kMatcherIndices[202],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
   },
   {
-    /* [226] */
+    /* [207] */
     /* num parameters */ 0,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1019],
-    /* return matcher indices */ &kMatcherIndices[200],
+    /* parameters */ &kParameters[1016],
+    /* return matcher indices */ &kMatcherIndices[206],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Zero,
   },
   {
-    /* [227] */
+    /* [208] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1002],
-    /* return matcher indices */ &kMatcherIndices[200],
+    /* parameters */ &kParameters[1004],
+    /* return matcher indices */ &kMatcherIndices[206],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Identity,
   },
   {
-    /* [228] */
+    /* [209] */
+    /* num parameters */ 12,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[16],
+    /* return matcher indices */ &kMatcherIndices[206],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::MatInitS,
+  },
+  {
+    /* [210] */
+    /* num parameters */ 3,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[591],
+    /* return matcher indices */ &kMatcherIndices[206],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::MatInitV,
+  },
+  {
+    /* [211] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[14],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[1005],
+    /* return matcher indices */ &kMatcherIndices[210],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Conv,
+  },
+  {
+    /* [212] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[12],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[1006],
+    /* return matcher indices */ &kMatcherIndices[208],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Conv,
+  },
+  {
+    /* [213] */
+    /* num parameters */ 0,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[1016],
+    /* return matcher indices */ &kMatcherIndices[212],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Zero,
+  },
+  {
+    /* [214] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[1007],
+    /* return matcher indices */ &kMatcherIndices[212],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Identity,
+  },
+  {
+    /* [215] */
     /* num parameters */ 8,
     /* num template types */ 1,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[57],
-    /* return matcher indices */ &kMatcherIndices[200],
+    /* return matcher indices */ &kMatcherIndices[212],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::MatInitS,
   },
   {
-    /* [229] */
+    /* [216] */
     /* num parameters */ 4,
     /* num template types */ 1,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[399],
-    /* return matcher indices */ &kMatcherIndices[200],
+    /* parameters */ &kParameters[435],
+    /* return matcher indices */ &kMatcherIndices[212],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::MatInitV,
   },
   {
+    /* [217] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[14],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[1008],
+    /* return matcher indices */ &kMatcherIndices[216],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Conv,
+  },
+  {
+    /* [218] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[12],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[1009],
+    /* return matcher indices */ &kMatcherIndices[214],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Conv,
+  },
+  {
+    /* [219] */
+    /* num parameters */ 0,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[1016],
+    /* return matcher indices */ &kMatcherIndices[218],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Zero,
+  },
+  {
+    /* [220] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[1010],
+    /* return matcher indices */ &kMatcherIndices[218],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Identity,
+  },
+  {
+    /* [221] */
+    /* num parameters */ 12,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[28],
+    /* return matcher indices */ &kMatcherIndices[218],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::MatInitS,
+  },
+  {
+    /* [222] */
+    /* num parameters */ 4,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[439],
+    /* return matcher indices */ &kMatcherIndices[218],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::MatInitV,
+  },
+  {
+    /* [223] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[14],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[1011],
+    /* return matcher indices */ &kMatcherIndices[222],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Conv,
+  },
+  {
+    /* [224] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[12],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[1012],
+    /* return matcher indices */ &kMatcherIndices[220],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Conv,
+  },
+  {
+    /* [225] */
+    /* num parameters */ 0,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[1016],
+    /* return matcher indices */ &kMatcherIndices[224],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Zero,
+  },
+  {
+    /* [226] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[1013],
+    /* return matcher indices */ &kMatcherIndices[224],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Identity,
+  },
+  {
+    /* [227] */
+    /* num parameters */ 16,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[0],
+    /* return matcher indices */ &kMatcherIndices[224],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::MatInitS,
+  },
+  {
+    /* [228] */
+    /* num parameters */ 4,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[443],
+    /* return matcher indices */ &kMatcherIndices[224],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::MatInitV,
+  },
+  {
+    /* [229] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[14],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[1014],
+    /* return matcher indices */ &kMatcherIndices[228],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Conv,
+  },
+  {
     /* [230] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[14],
+    /* template types */ &kTemplateTypes[12],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[984],
-    /* return matcher indices */ &kMatcherIndices[204],
+    /* parameters */ &kParameters[1015],
+    /* return matcher indices */ &kMatcherIndices[226],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
   },
@@ -11109,70 +11094,70 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[12],
+    /* template types */ &kTemplateTypes[0],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[969],
-    /* return matcher indices */ &kMatcherIndices[202],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Conv,
+    /* parameters */ &kParameters[933],
+    /* return matcher indices */ &kMatcherIndices[101],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
   },
   {
     /* [232] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[28],
+    /* template types */ &kTemplateTypes[0],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[619],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpPlus,
+    /* parameters */ &kParameters[934],
+    /* return matcher indices */ &kMatcherIndices[101],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
   },
   {
     /* [233] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[28],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[621],
-    /* return matcher indices */ &kMatcherIndices[30],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpPlus,
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[935],
+    /* return matcher indices */ &kMatcherIndices[101],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
   },
   {
     /* [234] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[28],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[625],
-    /* return matcher indices */ &kMatcherIndices[30],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpPlus,
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[936],
+    /* return matcher indices */ &kMatcherIndices[101],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
   },
   {
     /* [235] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[28],
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 2,
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[629],
-    /* return matcher indices */ &kMatcherIndices[30],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpPlus,
+    /* parameters */ &kParameters[937],
+    /* return matcher indices */ &kMatcherIndices[101],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
   },
   {
     /* [236] */
     /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[641],
-    /* return matcher indices */ &kMatcherIndices[10],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[672],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpPlus,
   },
@@ -11180,241 +11165,241 @@
     /* [237] */
     /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[28],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[645],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[674],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpMinus,
+    /* const eval */ &ConstEval::OpPlus,
   },
   {
     /* [238] */
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[28],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[649],
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[676],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpMinus,
+    /* const eval */ &ConstEval::OpPlus,
   },
   {
     /* [239] */
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[28],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[651],
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[678],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpMinus,
+    /* const eval */ &ConstEval::OpPlus,
   },
   {
     /* [240] */
     /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[28],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[655],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* num template numbers */ 2,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[680],
+    /* return matcher indices */ &kMatcherIndices[14],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpMinus,
+    /* const eval */ &ConstEval::OpPlus,
   },
   {
     /* [241] */
     /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[659],
-    /* return matcher indices */ &kMatcherIndices[10],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[682],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpMinus,
   },
   {
     /* [242] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[0],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[823],
-    /* return matcher indices */ &kMatcherIndices[95],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[684],
+    /* return matcher indices */ &kMatcherIndices[30],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpMinus,
   },
   {
     /* [243] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[0],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[822],
-    /* return matcher indices */ &kMatcherIndices[95],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[686],
+    /* return matcher indices */ &kMatcherIndices[30],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpMinus,
   },
   {
     /* [244] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[821],
-    /* return matcher indices */ &kMatcherIndices[95],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[688],
+    /* return matcher indices */ &kMatcherIndices[30],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpMinus,
   },
   {
     /* [245] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[820],
-    /* return matcher indices */ &kMatcherIndices[95],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 2,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[690],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpMinus,
   },
   {
     /* [246] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[3],
-    /* parameters */ &kParameters[819],
-    /* return matcher indices */ &kMatcherIndices[95],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[710],
+    /* return matcher indices */ &kMatcherIndices[3],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpDivide,
   },
   {
     /* [247] */
     /* num parameters */ 2,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[721],
-    /* return matcher indices */ &kMatcherIndices[41],
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[712],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpOr,
+    /* const eval */ &ConstEval::OpDivide,
   },
   {
     /* [248] */
     /* num parameters */ 2,
-    /* num template types */ 0,
+    /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[723],
-    /* return matcher indices */ &kMatcherIndices[39],
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[714],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpOr,
+    /* const eval */ &ConstEval::OpDivide,
   },
   {
     /* [249] */
     /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[36],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[725],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[716],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpOr,
+    /* const eval */ &ConstEval::OpDivide,
   },
   {
     /* [250] */
     /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[36],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[729],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[30],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[718],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpOr,
+    /* const eval */ nullptr,
   },
   {
     /* [251] */
     /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[24],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[757],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[30],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[720],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpShiftLeft,
+    /* const eval */ nullptr,
   },
   {
     /* [252] */
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[24],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[759],
+    /* template types */ &kTemplateTypes[30],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[722],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpShiftLeft,
+    /* const eval */ nullptr,
   },
   {
     /* [253] */
     /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[35],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[771],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[30],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[724],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpShiftLeft,
+    /* const eval */ nullptr,
   },
   {
     /* [254] */
     /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[35],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[773],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[730],
+    /* return matcher indices */ &kMatcherIndices[35],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpShiftLeft,
+    /* const eval */ &ConstEval::OpAnd,
   },
   {
     /* [255] */
     /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[28],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[693],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[732],
+    /* return matcher indices */ &kMatcherIndices[33],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpDivide,
+    /* const eval */ &ConstEval::OpAnd,
   },
   {
     /* [256] */
     /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 1,
+    /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[28],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[695],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[734],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpDivide,
+    /* const eval */ &ConstEval::OpAnd,
   },
   {
     /* [257] */
@@ -11422,1125 +11407,1125 @@
     /* num template types */ 1,
     /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[28],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[697],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[736],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpDivide,
+    /* const eval */ &ConstEval::OpAnd,
   },
   {
     /* [258] */
     /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[28],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[699],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[738],
+    /* return matcher indices */ &kMatcherIndices[35],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpDivide,
+    /* const eval */ &ConstEval::OpOr,
   },
   {
     /* [259] */
     /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[25],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[701],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[740],
+    /* return matcher indices */ &kMatcherIndices[33],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::OpOr,
   },
   {
     /* [260] */
     /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[703],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[28],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[742],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::OpOr,
   },
   {
     /* [261] */
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[705],
+    /* template types */ &kTemplateTypes[28],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[744],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::OpOr,
   },
   {
     /* [262] */
     /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 1,
+    /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[25],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[707],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[774],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::OpShiftLeft,
   },
   {
     /* [263] */
     /* num parameters */ 2,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[713],
-    /* return matcher indices */ &kMatcherIndices[41],
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[25],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[776],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpAnd,
+    /* const eval */ &ConstEval::OpShiftLeft,
   },
   {
     /* [264] */
     /* num parameters */ 2,
-    /* num template types */ 0,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[715],
-    /* return matcher indices */ &kMatcherIndices[39],
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[31],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[778],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpAnd,
+    /* const eval */ &ConstEval::OpShiftLeft,
   },
   {
     /* [265] */
     /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[36],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[717],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[31],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[780],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpAnd,
+    /* const eval */ &ConstEval::OpShiftLeft,
   },
   {
     /* [266] */
-    /* num parameters */ 2,
+    /* num parameters */ 3,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[36],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[719],
-    /* return matcher indices */ &kMatcherIndices[30],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpAnd,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[468],
+    /* return matcher indices */ &kMatcherIndices[3],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
   },
   {
     /* [267] */
-    /* 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 */ &kMatcherIndices[41],
-    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Zero,
-  },
-  {
-    /* [268] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[947],
-    /* return matcher indices */ &kMatcherIndices[41],
-    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Identity,
-  },
-  {
-    /* [269] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[29],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[948],
-    /* return matcher indices */ &kMatcherIndices[41],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Conv,
-  },
-  {
-    /* [270] */
-    /* 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 */ &kMatcherIndices[6],
-    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Zero,
-  },
-  {
-    /* [271] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[944],
-    /* return matcher indices */ &kMatcherIndices[6],
-    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Identity,
-  },
-  {
-    /* [272] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[30],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[945],
-    /* return matcher indices */ &kMatcherIndices[6],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Conv,
-  },
-  {
-    /* [273] */
-    /* 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 */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Zero,
-  },
-  {
-    /* [274] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[941],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Identity,
-  },
-  {
-    /* [275] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[31],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[942],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Conv,
-  },
-  {
-    /* [276] */
-    /* num parameters */ 3,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[552],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::select_bool,
-  },
-  {
-    /* [277] */
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[26],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[555],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[471],
+    /* return matcher indices */ &kMatcherIndices[30],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [268] */
+    /* num parameters */ 3,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[474],
+    /* return matcher indices */ &kMatcherIndices[30],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [269] */
+    /* num parameters */ 3,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[27],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[480],
+    /* return matcher indices */ &kMatcherIndices[3],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::select_bool,
+  },
+  {
+    /* [270] */
+    /* num parameters */ 3,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[27],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[483],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::select_bool,
   },
   {
-    /* [278] */
+    /* [271] */
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[26],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[558],
+    /* template types */ &kTemplateTypes[27],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[486],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::select_boolvec,
   },
   {
-    /* [279] */
+    /* [272] */
     /* 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 */ &kMatcherIndices[95],
+    /* parameters */ &kParameters[1016],
+    /* return matcher indices */ &kMatcherIndices[9],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Zero,
   },
   {
-    /* [280] */
+    /* [273] */
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[938],
-    /* return matcher indices */ &kMatcherIndices[95],
+    /* parameters */ &kParameters[958],
+    /* return matcher indices */ &kMatcherIndices[9],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Identity,
   },
   {
-    /* [281] */
+    /* [274] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[32],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[939],
-    /* return matcher indices */ &kMatcherIndices[95],
+    /* parameters */ &kParameters[959],
+    /* return matcher indices */ &kMatcherIndices[9],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
   },
   {
-    /* [282] */
+    /* [275] */
     /* 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 */ &kMatcherIndices[4],
+    /* parameters */ &kParameters[1016],
+    /* return matcher indices */ &kMatcherIndices[101],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Zero,
   },
   {
-    /* [283] */
+    /* [276] */
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[935],
-    /* return matcher indices */ &kMatcherIndices[4],
+    /* parameters */ &kParameters[960],
+    /* return matcher indices */ &kMatcherIndices[101],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Identity,
   },
   {
-    /* [284] */
+    /* [277] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[33],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[936],
-    /* return matcher indices */ &kMatcherIndices[4],
+    /* parameters */ &kParameters[961],
+    /* return matcher indices */ &kMatcherIndices[101],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
   },
   {
+    /* [278] */
+    /* num parameters */ 0,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[1016],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Zero,
+  },
+  {
+    /* [279] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[962],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Identity,
+  },
+  {
+    /* [280] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[34],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[963],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Conv,
+  },
+  {
+    /* [281] */
+    /* num parameters */ 0,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[1016],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Zero,
+  },
+  {
+    /* [282] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[964],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Identity,
+  },
+  {
+    /* [283] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[35],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[965],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Conv,
+  },
+  {
+    /* [284] */
+    /* num parameters */ 0,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[1016],
+    /* return matcher indices */ &kMatcherIndices[35],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Zero,
+  },
+  {
     /* [285] */
-    /* num parameters */ 3,
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[966],
+    /* return matcher indices */ &kMatcherIndices[35],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Identity,
+  },
+  {
+    /* [286] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[36],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[967],
+    /* return matcher indices */ &kMatcherIndices[35],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Conv,
+  },
+  {
+    /* [287] */
+    /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[477],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* parameters */ &kParameters[804],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [286] */
-    /* num parameters */ 3,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[480],
-    /* return matcher indices */ &kMatcherIndices[30],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [287] */
-    /* num parameters */ 3,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[483],
-    /* return matcher indices */ &kMatcherIndices[30],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::abs,
   },
   {
     /* [288] */
     /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[871],
-    /* return matcher indices */ &kMatcherIndices[41],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpNot,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[805],
+    /* return matcher indices */ &kMatcherIndices[30],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::abs,
   },
   {
     /* [289] */
     /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[872],
-    /* return matcher indices */ &kMatcherIndices[39],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpNot,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[806],
+    /* return matcher indices */ &kMatcherIndices[3],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::acos,
   },
   {
     /* [290] */
     /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[902],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
-    /* const eval */ nullptr,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[807],
+    /* return matcher indices */ &kMatcherIndices[30],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::acos,
   },
   {
     /* [291] */
     /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[901],
-    /* return matcher indices */ &kMatcherIndices[60],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
-    /* const eval */ nullptr,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[808],
+    /* return matcher indices */ &kMatcherIndices[3],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::acosh,
   },
   {
     /* [292] */
     /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[904],
-    /* return matcher indices */ &kMatcherIndices[172],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[809],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::acosh,
   },
   {
     /* [293] */
     /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[903],
-    /* return matcher indices */ &kMatcherIndices[78],
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[810],
+    /* return matcher indices */ &kMatcherIndices[35],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::all,
   },
   {
     /* [294] */
     /* num parameters */ 1,
     /* num template types */ 0,
-    /* num template numbers */ 0,
+    /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[898],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
-    /* const eval */ nullptr,
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[811],
+    /* return matcher indices */ &kMatcherIndices[35],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::all,
   },
   {
     /* [295] */
     /* num parameters */ 1,
     /* num template types */ 0,
-    /* num template numbers */ 1,
+    /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[897],
-    /* return matcher indices */ &kMatcherIndices[60],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
-    /* const eval */ nullptr,
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[812],
+    /* return matcher indices */ &kMatcherIndices[35],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::any,
   },
   {
     /* [296] */
-    /* num parameters */ 4,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[24],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[423],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[813],
+    /* return matcher indices */ &kMatcherIndices[35],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::insertBits,
+    /* const eval */ &ConstEval::any,
   },
   {
     /* [297] */
-    /* num parameters */ 4,
+    /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[24],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[427],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[815],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::insertBits,
+    /* const eval */ &ConstEval::asin,
   },
   {
     /* [298] */
     /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[896],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[816],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::asin,
   },
   {
     /* [299] */
     /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[895],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[817],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::asinh,
   },
   {
     /* [300] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[689],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[818],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::asinh,
   },
   {
     /* [301] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[687],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[819],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::atan,
   },
   {
     /* [302] */
     /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[894],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[820],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::atan,
   },
   {
     /* [303] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[893],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[594],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::atan2,
   },
   {
     /* [304] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[892],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[596],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::atan2,
   },
   {
     /* [305] */
     /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[891],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[821],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::atanh,
   },
   {
     /* [306] */
     /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[890],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[822],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::atanh,
   },
   {
     /* [307] */
     /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[889],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[823],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::ceil,
   },
   {
     /* [308] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[25],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[675],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[824],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::ceil,
   },
   {
     /* [309] */
-    /* num parameters */ 2,
+    /* num parameters */ 3,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[673],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[447],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::clamp,
   },
   {
     /* [310] */
-    /* num parameters */ 2,
+    /* num parameters */ 3,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[25],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[671],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[450],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::clamp,
   },
   {
     /* [311] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[669],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[825],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::cos,
   },
   {
     /* [312] */
     /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[906],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[826],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::cos,
   },
   {
     /* [313] */
     /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[905],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[827],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::cosh,
   },
   {
     /* [314] */
     /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[888],
-    /* return matcher indices */ &kMatcherIndices[134],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[828],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::cosh,
   },
   {
     /* [315] */
     /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[887],
-    /* return matcher indices */ &kMatcherIndices[96],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[25],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[829],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::countLeadingZeros,
   },
   {
     /* [316] */
-    /* num parameters */ 3,
+    /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[453],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[25],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[830],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::countLeadingZeros,
   },
   {
     /* [317] */
-    /* num parameters */ 3,
+    /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[450],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[25],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[831],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::countOneBits,
   },
   {
     /* [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],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[25],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[832],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::floor,
+    /* const eval */ &ConstEval::countOneBits,
   },
   {
     /* [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],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[25],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[833],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::floor,
+    /* const eval */ &ConstEval::countTrailingZeros,
   },
   {
     /* [320] */
     /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[900],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
-    /* const eval */ nullptr,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[25],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[834],
+    /* return matcher indices */ &kMatcherIndices[30],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::countTrailingZeros,
   },
   {
     /* [321] */
     /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[899],
-    /* return matcher indices */ &kMatcherIndices[60],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
-    /* const eval */ nullptr,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[835],
+    /* return matcher indices */ &kMatcherIndices[3],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::degrees,
   },
   {
     /* [322] */
     /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[24],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[912],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[836],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::firstLeadingBit,
+    /* const eval */ &ConstEval::degrees,
   },
   {
     /* [323] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[24],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[911],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[600],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::firstLeadingBit,
+    /* const eval */ nullptr,
   },
   {
     /* [324] */
-    /* num parameters */ 3,
+    /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[24],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[594],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[602],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::extractBits,
+    /* const eval */ nullptr,
   },
   {
     /* [325] */
-    /* num parameters */ 3,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[24],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[459],
-    /* return matcher indices */ &kMatcherIndices[30],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::extractBits,
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[838],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* const eval */ nullptr,
   },
   {
     /* [326] */
     /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[914],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[839],
+    /* return matcher indices */ &kMatcherIndices[36],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [327] */
     /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[913],
-    /* return matcher indices */ &kMatcherIndices[30],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[840],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [328] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[637],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[841],
+    /* return matcher indices */ &kMatcherIndices[36],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [329] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[633],
-    /* return matcher indices */ &kMatcherIndices[30],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* 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[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [330] */
     /* num parameters */ 1,
     /* num template types */ 0,
-    /* num template numbers */ 0,
+    /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[880],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::quantizeToF16,
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[843],
+    /* return matcher indices */ &kMatcherIndices[36],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* const eval */ nullptr,
   },
   {
     /* [331] */
     /* num parameters */ 1,
     /* num template types */ 0,
-    /* num template numbers */ 1,
+    /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[879],
-    /* return matcher indices */ &kMatcherIndices[60],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::quantizeToF16,
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[844],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* const eval */ nullptr,
   },
   {
     /* [332] */
     /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[878],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[845],
+    /* return matcher indices */ &kMatcherIndices[36],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [333] */
     /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[877],
-    /* return matcher indices */ &kMatcherIndices[30],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[846],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [334] */
     /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[916],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[847],
+    /* return matcher indices */ &kMatcherIndices[36],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [335] */
     /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[915],
-    /* return matcher indices */ &kMatcherIndices[30],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[848],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [336] */
     /* num parameters */ 1,
     /* num template types */ 0,
-    /* num template numbers */ 0,
+    /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[918],
-    /* return matcher indices */ &kMatcherIndices[62],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[849],
+    /* return matcher indices */ &kMatcherIndices[36],
     /* 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),
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[850],
+    /* return matcher indices */ &kMatcherIndices[3],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [338] */
     /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[24],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[869],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[851],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::reverseBits,
+    /* const eval */ nullptr,
   },
   {
     /* [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],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[852],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::reverseBits,
+    /* const eval */ nullptr,
   },
   {
     /* [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],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[853],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [341] */
-    /* num parameters */ 1,
+    /* num parameters */ 3,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[866],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[25],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[453],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::extractBits,
   },
   {
     /* [342] */
-    /* num parameters */ 1,
+    /* num parameters */ 3,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[865],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[25],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[456],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::saturate,
+    /* const eval */ &ConstEval::extractBits,
   },
   {
     /* [343] */
     /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[864],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[25],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[854],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::saturate,
+    /* const eval */ &ConstEval::firstLeadingBit,
   },
   {
     /* [344] */
     /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[921],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
-    /* const eval */ nullptr,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[25],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[855],
+    /* return matcher indices */ &kMatcherIndices[30],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::firstLeadingBit,
   },
   {
     /* [345] */
     /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[920],
-    /* return matcher indices */ &kMatcherIndices[60],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
-    /* const eval */ nullptr,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[25],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[856],
+    /* return matcher indices */ &kMatcherIndices[3],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::firstTrailingBit,
   },
   {
     /* [346] */
     /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[863],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[25],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[857],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::sign,
+    /* const eval */ &ConstEval::firstTrailingBit,
   },
   {
     /* [347] */
     /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 1,
+    /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[862],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[858],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::sign,
+    /* const eval */ &ConstEval::floor,
   },
   {
     /* [348] */
     /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[861],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[859],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::floor,
   },
   {
     /* [349] */
-    /* num parameters */ 1,
+    /* num parameters */ 3,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[858],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[462],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [350] */
-    /* num parameters */ 1,
+    /* num parameters */ 3,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[857],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[465],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -12548,35 +12533,35 @@
     /* [351] */
     /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[856],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[860],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [352] */
-    /* num parameters */ 3,
+    /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[579],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[861],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [353] */
-    /* num parameters */ 3,
+    /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[582],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[862],
+    /* return matcher indices */ &kMatcherIndices[104],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -12584,59 +12569,59 @@
     /* [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],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[863],
+    /* return matcher indices */ &kMatcherIndices[39],
     /* 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),
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[864],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [356] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[599],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::step,
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[865],
+    /* return matcher indices */ &kMatcherIndices[36],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* const eval */ nullptr,
   },
   {
     /* [357] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[597],
-    /* return matcher indices */ &kMatcherIndices[30],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::step,
-  },
-  {
-    /* [358] */
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[923],
-    /* return matcher indices */ &kMatcherIndices[62],
+    /* parameters */ &kParameters[866],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [358] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[867],
+    /* return matcher indices */ &kMatcherIndices[36],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
@@ -12644,95 +12629,95 @@
     /* [359] */
     /* num parameters */ 1,
     /* num template types */ 0,
-    /* num template numbers */ 1,
+    /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[1016],
-    /* return matcher indices */ &kMatcherIndices[60],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[868],
+    /* return matcher indices */ &kMatcherIndices[38],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [360] */
     /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[852],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[869],
+    /* return matcher indices */ &kMatcherIndices[36],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [361] */
-    /* num parameters */ 1,
+    /* num parameters */ 4,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[851],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[25],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[295],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::insertBits,
   },
   {
     /* [362] */
-    /* num parameters */ 1,
+    /* num parameters */ 4,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[850],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[25],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[299],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::insertBits,
   },
   {
     /* [363] */
     /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[849],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[870],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [364] */
     /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[925],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[871],
+    /* return matcher indices */ &kMatcherIndices[30],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [365] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[924],
-    /* return matcher indices */ &kMatcherIndices[60],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[610],
+    /* return matcher indices */ &kMatcherIndices[3],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [366] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[847],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[612],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -12740,817 +12725,817 @@
     /* [367] */
     /* num parameters */ 1,
     /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[872],
+    /* return matcher indices */ &kMatcherIndices[3],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::length,
+  },
+  {
+    /* [368] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[846],
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[873],
+    /* return matcher indices */ &kMatcherIndices[3],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::length,
+  },
+  {
+    /* [369] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[874],
+    /* return matcher indices */ &kMatcherIndices[3],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [370] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[875],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
-    /* [368] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[927],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [369] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[926],
-    /* return matcher indices */ &kMatcherIndices[60],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [370] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[24],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[775],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
     /* [371] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[24],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[779],
-    /* return matcher indices */ &kMatcherIndices[30],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[876],
+    /* return matcher indices */ &kMatcherIndices[3],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [372] */
     /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[929],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[877],
+    /* return matcher indices */ &kMatcherIndices[30],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [373] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[928],
-    /* return matcher indices */ &kMatcherIndices[60],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
-    /* const eval */ nullptr,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[614],
+    /* return matcher indices */ &kMatcherIndices[3],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::max,
   },
   {
     /* [374] */
     /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[28],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[753],
-    /* return matcher indices */ &kMatcherIndices[41],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpGreaterThanEqual,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[616],
+    /* return matcher indices */ &kMatcherIndices[30],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::max,
   },
   {
     /* [375] */
     /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[755],
-    /* return matcher indices */ &kMatcherIndices[39],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpGreaterThanEqual,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[618],
+    /* return matcher indices */ &kMatcherIndices[3],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::min,
   },
   {
     /* [376] */
     /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[28],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[749],
-    /* return matcher indices */ &kMatcherIndices[41],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpLessThanEqual,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[620],
+    /* return matcher indices */ &kMatcherIndices[30],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::min,
   },
   {
     /* [377] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[28],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[751],
-    /* return matcher indices */ &kMatcherIndices[39],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpLessThanEqual,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[878],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::modf,
   },
   {
     /* [378] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[28],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[745],
-    /* return matcher indices */ &kMatcherIndices[41],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpGreaterThan,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[879],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::modf,
   },
   {
     /* [379] */
     /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[28],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[747],
-    /* return matcher indices */ &kMatcherIndices[39],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpGreaterThan,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[622],
+    /* return matcher indices */ &kMatcherIndices[3],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
   },
   {
     /* [380] */
     /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[769],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[624],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [381] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[767],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[886],
+    /* return matcher indices */ &kMatcherIndices[38],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::quantizeToF16,
   },
   {
     /* [382] */
     /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[932],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[887],
+    /* return matcher indices */ &kMatcherIndices[36],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::quantizeToF16,
   },
   {
     /* [383] */
     /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[931],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[888],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::radians,
   },
   {
     /* [384] */
     /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[28],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1018],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[889],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::abs,
+    /* const eval */ &ConstEval::radians,
   },
   {
     /* [385] */
     /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[28],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[853],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[25],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[890],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::abs,
+    /* const eval */ &ConstEval::reverseBits,
   },
   {
     /* [386] */
     /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[24],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[952],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[25],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[891],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::countTrailingZeros,
+    /* const eval */ &ConstEval::reverseBits,
   },
   {
     /* [387] */
     /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[24],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[933],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[892],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::countTrailingZeros,
+    /* const eval */ &ConstEval::round,
   },
   {
     /* [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],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[893],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::countOneBits,
+    /* const eval */ &ConstEval::round,
   },
   {
     /* [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],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[894],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::countOneBits,
+    /* const eval */ &ConstEval::saturate,
   },
   {
     /* [390] */
     /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[0],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[808],
-    /* return matcher indices */ &kMatcherIndices[95],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[895],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::saturate,
   },
   {
     /* [391] */
     /* num parameters */ 1,
-    /* num template types */ 0,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[807],
-    /* return matcher indices */ &kMatcherIndices[95],
+    /* parameters */ &kParameters[896],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::sign,
   },
   {
     /* [392] */
     /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[24],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[972],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[897],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::countLeadingZeros,
+    /* const eval */ &ConstEval::sign,
   },
   {
     /* [393] */
     /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[24],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[963],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[898],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::countLeadingZeros,
+    /* const eval */ &ConstEval::sin,
   },
   {
     /* [394] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[28],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[741],
-    /* return matcher indices */ &kMatcherIndices[41],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpLessThan,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[899],
+    /* return matcher indices */ &kMatcherIndices[30],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::sin,
   },
   {
     /* [395] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[28],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[743],
-    /* return matcher indices */ &kMatcherIndices[39],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpLessThan,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[900],
+    /* return matcher indices */ &kMatcherIndices[3],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::sinh,
   },
   {
     /* [396] */
     /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[974],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[901],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::sinh,
   },
   {
     /* [397] */
-    /* num parameters */ 1,
+    /* num parameters */ 3,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[973],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[489],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::smoothstep,
   },
   {
     /* [398] */
-    /* num parameters */ 1,
+    /* num parameters */ 3,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[976],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[492],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::smoothstep,
   },
   {
     /* [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],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[902],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::sqrt,
   },
   {
     /* [400] */
-    /* num parameters */ 3,
+    /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[28],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[540],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[903],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::clamp,
+    /* const eval */ &ConstEval::sqrt,
   },
   {
     /* [401] */
-    /* num parameters */ 3,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[28],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[537],
-    /* return matcher indices */ &kMatcherIndices[30],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::clamp,
-  },
-  {
-    /* [402] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[978],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* parameters */ &kParameters[628],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::ceil,
+    /* const eval */ &ConstEval::step,
+  },
+  {
+    /* [402] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[630],
+    /* return matcher indices */ &kMatcherIndices[30],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::step,
   },
   {
     /* [403] */
     /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 1,
+    /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[977],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[904],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::ceil,
+    /* const eval */ &ConstEval::tan,
   },
   {
     /* [404] */
-    /* num parameters */ 3,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[510],
-    /* return matcher indices */ &kMatcherIndices[102],
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[905],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::tan,
   },
   {
     /* [405] */
-    /* num parameters */ 3,
-    /* num template types */ 0,
+    /* num parameters */ 1,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[513],
-    /* return matcher indices */ &kMatcherIndices[102],
+    /* parameters */ &kParameters[906],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::tanh,
   },
   {
     /* [406] */
     /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 0,
+    /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[987],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[907],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::atanh,
+    /* const eval */ &ConstEval::tanh,
   },
   {
     /* [407] */
     /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 1,
+    /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[986],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[909],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::atanh,
+    /* const eval */ &ConstEval::trunc,
   },
   {
     /* [408] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 0,
+    /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[797],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[910],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::atan2,
+    /* const eval */ &ConstEval::trunc,
   },
   {
     /* [409] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[793],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[0],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[948],
+    /* return matcher indices */ &kMatcherIndices[101],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::atan2,
+    /* const eval */ nullptr,
   },
   {
     /* [410] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
+    /* num parameters */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[38],
     /* 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,
+    /* parameters */ &kParameters[949],
+    /* return matcher indices */ &kMatcherIndices[101],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
   },
   {
     /* [411] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[26],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[805],
-    /* return matcher indices */ &kMatcherIndices[39],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpNotEqual,
+    /* num parameters */ 3,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[519],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
   },
   {
     /* [412] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
+    /* num parameters */ 3,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[38],
     /* 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,
+    /* parameters */ &kParameters[522],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
   },
   {
     /* [413] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[26],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[737],
-    /* return matcher indices */ &kMatcherIndices[39],
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[952],
+    /* return matcher indices */ &kMatcherIndices[35],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpEqual,
+    /* const eval */ &ConstEval::OpNot,
   },
   {
     /* [414] */
     /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[993],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::atan,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[953],
+    /* return matcher indices */ &kMatcherIndices[33],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpNot,
   },
   {
     /* [415] */
     /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[992],
-    /* return matcher indices */ &kMatcherIndices[30],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::atan,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[28],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[954],
+    /* return matcher indices */ &kMatcherIndices[3],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpComplement,
   },
   {
     /* [416] */
     /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[999],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::asinh,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[28],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[955],
+    /* return matcher indices */ &kMatcherIndices[30],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpComplement,
   },
   {
     /* [417] */
     /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[998],
-    /* return matcher indices */ &kMatcherIndices[30],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::asinh,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[29],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[956],
+    /* return matcher indices */ &kMatcherIndices[3],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpUnaryMinus,
   },
   {
     /* [418] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[36],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[709],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[29],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[957],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpXor,
+    /* const eval */ &ConstEval::OpUnaryMinus,
   },
   {
     /* [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],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[28],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[726],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpXor,
   },
   {
     /* [420] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* 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,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[28],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[728],
+    /* return matcher indices */ &kMatcherIndices[30],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpXor,
   },
   {
     /* [421] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* 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,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[27],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[750],
+    /* return matcher indices */ &kMatcherIndices[35],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpEqual,
   },
   {
     /* [422] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[922],
-    /* return matcher indices */ &kMatcherIndices[41],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::any,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[27],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[752],
+    /* return matcher indices */ &kMatcherIndices[33],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpEqual,
   },
   {
     /* [423] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[1011],
-    /* return matcher indices */ &kMatcherIndices[41],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::any,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[27],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[754],
+    /* return matcher indices */ &kMatcherIndices[35],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpNotEqual,
   },
   {
     /* [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[41],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::all,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[27],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[756],
+    /* return matcher indices */ &kMatcherIndices[33],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpNotEqual,
   },
   {
     /* [425] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[1017],
-    /* return matcher indices */ &kMatcherIndices[41],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::all,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[758],
+    /* return matcher indices */ &kMatcherIndices[35],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpLessThan,
   },
   {
     /* [426] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 0,
+    /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[949],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[760],
+    /* return matcher indices */ &kMatcherIndices[33],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpLessThan,
   },
   {
     /* [427] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 1,
+    /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[990],
-    /* return matcher indices */ &kMatcherIndices[30],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[762],
+    /* return matcher indices */ &kMatcherIndices[35],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpGreaterThan,
   },
   {
     /* [428] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[934],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::acos,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[764],
+    /* return matcher indices */ &kMatcherIndices[33],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpGreaterThan,
   },
   {
     /* [429] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[946],
-    /* return matcher indices */ &kMatcherIndices[30],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::acos,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[766],
+    /* return matcher indices */ &kMatcherIndices[35],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpLessThanEqual,
   },
   {
     /* [430] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[875],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[768],
+    /* return matcher indices */ &kMatcherIndices[33],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpUnaryMinus,
+    /* const eval */ &ConstEval::OpLessThanEqual,
   },
   {
     /* [431] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[37],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[876],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[770],
+    /* return matcher indices */ &kMatcherIndices[35],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpUnaryMinus,
+    /* const eval */ &ConstEval::OpGreaterThanEqual,
   },
   {
     /* [432] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[36],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[873],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[30],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[772],
+    /* return matcher indices */ &kMatcherIndices[33],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpComplement,
+    /* const eval */ &ConstEval::OpGreaterThanEqual,
   },
   {
     /* [433] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[36],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[874],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[25],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[782],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpComplement,
+    /* const eval */ nullptr,
   },
   {
     /* [434] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* 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,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[25],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[784],
+    /* return matcher indices */ &kMatcherIndices[30],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
   },
   {
     /* [435] */
@@ -13558,34 +13543,34 @@
     /* num template types */ 1,
     /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[24],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[909],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* template numbers */ &kTemplateNumbers[8],
+    /* parameters */ &kParameters[814],
+    /* return matcher indices */ &kMatcherIndices[101],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::firstTrailingBit,
+    /* const eval */ nullptr,
   },
   {
     /* [436] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[34],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[870],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* parameters */ &kParameters[598],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Identity,
+    /* const eval */ &ConstEval::cross,
   },
   {
     /* [437] */
-    /* num parameters */ 3,
+    /* num parameters */ 1,
     /* 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),
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[837],
+    /* return matcher indices */ &kMatcherIndices[3],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
@@ -13593,143 +13578,143 @@
     /* 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,
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[604],
+    /* return matcher indices */ &kMatcherIndices[3],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::dot,
   },
   {
     /* [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),
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[606],
+    /* return matcher indices */ &kMatcherIndices[9],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, 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),
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[608],
+    /* return matcher indices */ &kMatcherIndices[101],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [441] */
-    /* num parameters */ 2,
+    /* num parameters */ 3,
     /* 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),
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[459],
+    /* return matcher indices */ &kMatcherIndices[30],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [442] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* 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),
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[880],
+    /* return matcher indices */ &kMatcherIndices[30],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, 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,
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[881],
+    /* return matcher indices */ &kMatcherIndices[101],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::pack2x16float,
   },
   {
     /* [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,
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[882],
+    /* return matcher indices */ &kMatcherIndices[101],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::pack2x16snorm,
   },
   {
     /* [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,
+    /* 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[101],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::pack2x16unorm,
   },
   {
     /* [446] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* 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,
+    /* parameters */ &kParameters[884],
+    /* return matcher indices */ &kMatcherIndices[101],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::pack4x8snorm,
   },
   {
     /* [447] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* 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,
+    /* parameters */ &kParameters[885],
+    /* return matcher indices */ &kMatcherIndices[101],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::pack4x8unorm,
   },
   {
     /* [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),
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[626],
+    /* return matcher indices */ &kMatcherIndices[30],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [449] */
-    /* num parameters */ 1,
+    /* num parameters */ 3,
     /* 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),
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[4],
+    /* parameters */ &kParameters[477],
+    /* return matcher indices */ &kMatcherIndices[30],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
@@ -13739,21 +13724,21 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1019],
+    /* parameters */ &kParameters[1016],
     /* 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),
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 2,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[3],
+    /* parameters */ &kParameters[908],
+    /* return matcher indices */ &kMatcherIndices[18],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
@@ -13763,10 +13748,10 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[841],
-    /* return matcher indices */ &kMatcherIndices[102],
+    /* parameters */ &kParameters[911],
+    /* return matcher indices */ &kMatcherIndices[108],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::unpack4x8unorm,
+    /* const eval */ &ConstEval::unpack2x16float,
   },
   {
     /* [453] */
@@ -13775,10 +13760,10 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[842],
-    /* return matcher indices */ &kMatcherIndices[102],
+    /* parameters */ &kParameters[912],
+    /* return matcher indices */ &kMatcherIndices[108],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::unpack4x8snorm,
+    /* const eval */ &ConstEval::unpack2x16snorm,
   },
   {
     /* [454] */
@@ -13787,8 +13772,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[843],
-    /* return matcher indices */ &kMatcherIndices[132],
+    /* parameters */ &kParameters[913],
+    /* return matcher indices */ &kMatcherIndices[108],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::unpack2x16unorm,
   },
@@ -13799,10 +13784,10 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[844],
-    /* return matcher indices */ &kMatcherIndices[132],
+    /* parameters */ &kParameters[914],
+    /* return matcher indices */ &kMatcherIndices[110],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::unpack2x16snorm,
+    /* const eval */ &ConstEval::unpack4x8snorm,
   },
   {
     /* [456] */
@@ -13811,33 +13796,33 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[845],
-    /* return matcher indices */ &kMatcherIndices[132],
+    /* parameters */ &kParameters[915],
+    /* return matcher indices */ &kMatcherIndices[110],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::unpack4x8unorm,
   },
   {
     /* [457] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* 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),
+    /* num parameters */ 0,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[1016],
+    /* return matcher indices */ nullptr,
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [458] */
-    /* num parameters */ 3,
+    /* num parameters */ 1,
     /* 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),
+    /* template types */ &kTemplateTypes[25],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[950],
+    /* return matcher indices */ &kMatcherIndices[3],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
@@ -13845,168 +13830,156 @@
     /* 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),
+    /* template types */ &kTemplateTypes[25],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[654],
+    /* return matcher indices */ nullptr,
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [460] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[881],
-    /* return matcher indices */ &kMatcherIndices[95],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::pack4x8unorm,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[25],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[656],
+    /* return matcher indices */ &kMatcherIndices[3],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
   },
   {
     /* [461] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[882],
-    /* return matcher indices */ &kMatcherIndices[95],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::pack4x8snorm,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[25],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[658],
+    /* return matcher indices */ &kMatcherIndices[3],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
   },
   {
     /* [462] */
-    /* num parameters */ 1,
-    /* 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 */ &ConstEval::pack2x16snorm,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[25],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[660],
+    /* return matcher indices */ &kMatcherIndices[3],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
   },
   {
     /* [463] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[885],
-    /* return matcher indices */ &kMatcherIndices[95],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[25],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[662],
+    /* return matcher indices */ &kMatcherIndices[3],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [464] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[886],
-    /* return matcher indices */ &kMatcherIndices[30],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* template types */ &kTemplateTypes[25],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[664],
+    /* return matcher indices */ &kMatcherIndices[3],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [465] */
     /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[777],
-    /* return matcher indices */ &kMatcherIndices[104],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::cross,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[25],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[666],
+    /* return matcher indices */ &kMatcherIndices[3],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
   },
   {
     /* [466] */
-    /* num parameters */ 3,
+    /* num parameters */ 2,
     /* 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),
+    /* template types */ &kTemplateTypes[25],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[668],
+    /* return matcher indices */ &kMatcherIndices[3],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [467] */
     /* num parameters */ 2,
-    /* 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),
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[25],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[670],
+    /* return matcher indices */ &kMatcherIndices[3],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [468] */
-    /* 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),
+    /* num parameters */ 3,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[25],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[570],
+    /* return matcher indices */ &kMatcherIndices[160],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [469] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[765],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[951],
+    /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::Identity,
   },
   {
     /* [470] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* 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 */ 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 parameters */ 2,
     /* 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 */ &ConstEval::pack2x16unorm,
+    /* parameters */ &kParameters[746],
+    /* return matcher indices */ &kMatcherIndices[35],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [471] */
+    /* num parameters */ 2,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[748],
+    /* return matcher indices */ &kMatcherIndices[35],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
   },
 };
 
@@ -14016,357 +13989,357 @@
     /* 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[384],
+    /* overloads */ &kOverloads[287],
   },
   {
     /* [1] */
     /* fn acos<T : fa_f32_f16>(@test_value(0.96891242171) T) -> T */
     /* fn acos<N : num, T : fa_f32_f16>(@test_value(0.96891242171) vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[428],
+    /* overloads */ &kOverloads[289],
   },
   {
     /* [2] */
-    /* fn acosh<T : f32_f16>(T) -> T */
-    /* fn acosh<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
+    /* fn acosh<T : fa_f32_f16>(@test_value(2) T) -> T */
+    /* fn acosh<N : num, T : fa_f32_f16>(@test_value(2) vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[426],
+    /* overloads */ &kOverloads[291],
   },
   {
     /* [3] */
     /* fn all(bool) -> bool */
     /* fn all<N : num>(vec<N, bool>) -> bool */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[424],
+    /* overloads */ &kOverloads[293],
   },
   {
     /* [4] */
     /* fn any(bool) -> bool */
     /* fn any<N : num>(vec<N, bool>) -> bool */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[422],
+    /* overloads */ &kOverloads[295],
   },
   {
     /* [5] */
     /* fn arrayLength<T, A : access>(ptr<storage, array<T>, A>) -> u32 */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[471],
+    /* overloads */ &kOverloads[435],
   },
   {
     /* [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[420],
+    /* overloads */ &kOverloads[297],
   },
   {
     /* [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[416],
+    /* overloads */ &kOverloads[299],
   },
   {
     /* [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[414],
+    /* overloads */ &kOverloads[301],
   },
   {
     /* [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[408],
+    /* overloads */ &kOverloads[303],
   },
   {
     /* [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[406],
+    /* overloads */ &kOverloads[305],
   },
   {
     /* [11] */
     /* 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[402],
+    /* overloads */ &kOverloads[307],
   },
   {
     /* [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[400],
+    /* overloads */ &kOverloads[309],
   },
   {
     /* [13] */
-    /* fn cos<T : f32_f16>(T) -> T */
-    /* fn cos<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
+    /* fn cos<T : fa_f32_f16>(@test_value(0) T) -> T */
+    /* fn cos<N : num, T : fa_f32_f16>(@test_value(0) vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[398],
+    /* overloads */ &kOverloads[311],
   },
   {
     /* [14] */
-    /* fn cosh<T : f32_f16>(T) -> T */
-    /* fn cosh<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
+    /* fn cosh<T : fa_f32_f16>(@test_value(0) T) -> T */
+    /* fn cosh<N : num, T : fa_f32_f16>(@test_value(0) vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[396],
+    /* overloads */ &kOverloads[313],
   },
   {
     /* [15] */
     /* fn countLeadingZeros<T : iu32>(T) -> T */
     /* fn countLeadingZeros<N : num, T : iu32>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[392],
+    /* overloads */ &kOverloads[315],
   },
   {
     /* [16] */
     /* fn countOneBits<T : iu32>(T) -> T */
     /* fn countOneBits<N : num, T : iu32>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[388],
+    /* overloads */ &kOverloads[317],
   },
   {
     /* [17] */
     /* fn countTrailingZeros<T : iu32>(T) -> T */
     /* fn countTrailingZeros<N : num, T : iu32>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[386],
+    /* overloads */ &kOverloads[319],
   },
   {
     /* [18] */
     /* fn cross<T : fa_f32_f16>(vec3<T>, vec3<T>) -> vec3<T> */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[465],
+    /* overloads */ &kOverloads[436],
   },
   {
     /* [19] */
-    /* fn degrees<T : f32_f16>(T) -> T */
-    /* fn degrees<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
+    /* fn degrees<T : fa_f32_f16>(T) -> T */
+    /* fn degrees<N : num, T : fa_f32_f16>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[382],
+    /* overloads */ &kOverloads[321],
   },
   {
     /* [20] */
     /* fn determinant<N : num, T : f32_f16>(mat<N, N, T>) -> T */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[470],
+    /* overloads */ &kOverloads[437],
   },
   {
     /* [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[380],
+    /* overloads */ &kOverloads[323],
   },
   {
     /* [22] */
-    /* fn dot<N : num, T : fiu32_f16>(vec<N, T>, vec<N, T>) -> T */
+    /* fn dot<N : num, T : fia_fiu32_f16>(vec<N, T>, vec<N, T>) -> T */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[469],
+    /* overloads */ &kOverloads[438],
   },
   {
     /* [23] */
     /* fn dot4I8Packed(u32, u32) -> i32 */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[468],
+    /* overloads */ &kOverloads[439],
   },
   {
     /* [24] */
     /* fn dot4U8Packed(u32, u32) -> u32 */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[467],
+    /* overloads */ &kOverloads[440],
   },
   {
     /* [25] */
     /* fn dpdx(f32) -> f32 */
     /* fn dpdx<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[372],
+    /* overloads */ &kOverloads[325],
   },
   {
     /* [26] */
     /* fn dpdxCoarse(f32) -> f32 */
     /* fn dpdxCoarse<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[368],
+    /* overloads */ &kOverloads[327],
   },
   {
     /* [27] */
     /* fn dpdxFine(f32) -> f32 */
     /* fn dpdxFine<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[364],
+    /* overloads */ &kOverloads[329],
   },
   {
     /* [28] */
     /* fn dpdy(f32) -> f32 */
     /* fn dpdy<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[358],
+    /* overloads */ &kOverloads[331],
   },
   {
     /* [29] */
     /* fn dpdyCoarse(f32) -> f32 */
     /* fn dpdyCoarse<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[344],
+    /* overloads */ &kOverloads[333],
   },
   {
     /* [30] */
     /* fn dpdyFine(f32) -> f32 */
     /* fn dpdyFine<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[336],
+    /* overloads */ &kOverloads[335],
   },
   {
     /* [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[334],
+    /* overloads */ &kOverloads[337],
   },
   {
     /* [32] */
     /* fn exp2<T : f32_f16>(T) -> T */
     /* fn exp2<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[326],
+    /* overloads */ &kOverloads[339],
   },
   {
     /* [33] */
     /* fn extractBits<T : iu32>(T, u32, u32) -> T */
     /* fn extractBits<N : num, T : iu32>(vec<N, T>, u32, u32) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[324],
+    /* overloads */ &kOverloads[341],
   },
   {
     /* [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[466],
+    /* overloads */ &kOverloads[441],
   },
   {
     /* [35] */
     /* fn firstLeadingBit<T : iu32>(T) -> T */
     /* fn firstLeadingBit<N : num, T : iu32>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[322],
+    /* overloads */ &kOverloads[343],
   },
   {
     /* [36] */
     /* fn firstTrailingBit<T : iu32>(T) -> T */
     /* fn firstTrailingBit<N : num, T : iu32>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[434],
+    /* overloads */ &kOverloads[345],
   },
   {
     /* [37] */
     /* 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[318],
+    /* overloads */ &kOverloads[347],
   },
   {
     /* [38] */
     /* 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> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[316],
+    /* overloads */ &kOverloads[349],
   },
   {
     /* [39] */
     /* fn fract<T : f32_f16>(T) -> T */
     /* fn fract<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[312],
+    /* overloads */ &kOverloads[351],
   },
   {
     /* [40] */
     /* fn frexp<T : f32_f16>(T) -> __frexp_result<T> */
     /* fn frexp<N : num, T : f32_f16>(vec<N, T>) -> __frexp_result_vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[292],
+    /* overloads */ &kOverloads[353],
   },
   {
     /* [41] */
     /* fn fwidth(f32) -> f32 */
     /* fn fwidth<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[290],
+    /* overloads */ &kOverloads[355],
   },
   {
     /* [42] */
     /* fn fwidthCoarse(f32) -> f32 */
     /* fn fwidthCoarse<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[320],
+    /* overloads */ &kOverloads[357],
   },
   {
     /* [43] */
     /* fn fwidthFine(f32) -> f32 */
     /* fn fwidthFine<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[294],
+    /* overloads */ &kOverloads[359],
   },
   {
     /* [44] */
     /* 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> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[296],
+    /* overloads */ &kOverloads[361],
   },
   {
     /* [45] */
     /* fn inverseSqrt<T : f32_f16>(T) -> T */
     /* fn inverseSqrt<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[298],
+    /* overloads */ &kOverloads[363],
   },
   {
     /* [46] */
     /* fn ldexp<T : f32_f16>(T, i32) -> T */
     /* fn ldexp<N : num, T : f32_f16>(vec<N, T>, vec<N, i32>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[300],
+    /* overloads */ &kOverloads[365],
   },
   {
     /* [47] */
-    /* fn length<T : f32_f16>(T) -> T */
-    /* fn length<N : num, T : f32_f16>(vec<N, T>) -> T */
+    /* fn length<T : fa_f32_f16>(@test_value(0) T) -> T */
+    /* fn length<N : num, T : fa_f32_f16>(@test_value(0) vec<N, T>) -> T */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[302],
+    /* overloads */ &kOverloads[367],
   },
   {
     /* [48] */
     /* fn log<T : f32_f16>(T) -> T */
     /* fn log<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[304],
+    /* overloads */ &kOverloads[369],
   },
   {
     /* [49] */
     /* fn log2<T : f32_f16>(T) -> T */
     /* fn log2<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[306],
+    /* overloads */ &kOverloads[371],
   },
   {
     /* [50] */
-    /* fn max<T : fiu32_f16>(T, T) -> T */
-    /* fn max<N : num, T : fiu32_f16>(vec<N, T>, vec<N, T>) -> vec<N, T> */
+    /* fn max<T : fia_fiu32_f16>(T, T) -> T */
+    /* fn max<N : num, T : fia_fiu32_f16>(vec<N, T>, vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[308],
+    /* overloads */ &kOverloads[373],
   },
   {
     /* [51] */
-    /* fn min<T : fiu32_f16>(T, T) -> T */
-    /* fn min<N : num, T : fiu32_f16>(vec<N, T>, vec<N, T>) -> vec<N, T> */
+    /* fn min<T : fia_fiu32_f16>(T, T) -> T */
+    /* fn min<N : num, T : fia_fiu32_f16>(vec<N, T>, vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[310],
+    /* overloads */ &kOverloads[375],
   },
   {
     /* [52] */
@@ -14374,104 +14347,104 @@
     /* fn mix<N : num, T : f32_f16>(vec<N, T>, vec<N, T>, vec<N, T>) -> vec<N, T> */
     /* fn mix<N : num, T : f32_f16>(vec<N, T>, vec<N, T>, T) -> vec<N, T> */
     /* num overloads */ 3,
-    /* overloads */ &kOverloads[285],
+    /* overloads */ &kOverloads[266],
   },
   {
     /* [53] */
-    /* fn modf<T : f32_f16>(T) -> __modf_result<T> */
-    /* fn modf<N : num, T : f32_f16>(vec<N, T>) -> __modf_result_vec<N, T> */
+    /* fn modf<T : f32_f16>(@test_value(-1.5) T) -> __modf_result<T> */
+    /* fn modf<N : num, T : f32_f16>(@test_value(-1.5) vec<N, T>) -> __modf_result_vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[314],
+    /* overloads */ &kOverloads[377],
   },
   {
     /* [54] */
     /* fn normalize<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[464],
+    /* overloads */ &kOverloads[442],
   },
   {
     /* [55] */
     /* fn pack2x16float(vec2<f32>) -> u32 */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[463],
+    /* overloads */ &kOverloads[443],
   },
   {
     /* [56] */
     /* fn pack2x16snorm(vec2<f32>) -> u32 */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[462],
+    /* overloads */ &kOverloads[444],
   },
   {
     /* [57] */
     /* fn pack2x16unorm(vec2<f32>) -> u32 */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[472],
+    /* overloads */ &kOverloads[445],
   },
   {
     /* [58] */
     /* fn pack4x8snorm(vec4<f32>) -> u32 */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[461],
+    /* overloads */ &kOverloads[446],
   },
   {
     /* [59] */
     /* fn pack4x8unorm(vec4<f32>) -> u32 */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[460],
+    /* overloads */ &kOverloads[447],
   },
   {
     /* [60] */
     /* fn pow<T : f32_f16>(T, T) -> T */
     /* fn pow<N : num, T : f32_f16>(vec<N, T>, vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[328],
+    /* overloads */ &kOverloads[379],
   },
   {
     /* [61] */
     /* fn quantizeToF16(f32) -> f32 */
     /* fn quantizeToF16<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[330],
+    /* overloads */ &kOverloads[381],
   },
   {
     /* [62] */
-    /* fn radians<T : f32_f16>(T) -> T */
-    /* fn radians<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
+    /* fn radians<T : fa_f32_f16>(T) -> T */
+    /* fn radians<N : num, T : fa_f32_f16>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[332],
+    /* overloads */ &kOverloads[383],
   },
   {
     /* [63] */
     /* fn reflect<N : num, T : f32_f16>(vec<N, T>, vec<N, T>) -> vec<N, T> */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[459],
+    /* overloads */ &kOverloads[448],
   },
   {
     /* [64] */
     /* fn refract<N : num, T : f32_f16>(vec<N, T>, vec<N, T>, T) -> vec<N, T> */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[458],
+    /* overloads */ &kOverloads[449],
   },
   {
     /* [65] */
     /* fn reverseBits<T : iu32>(T) -> T */
     /* fn reverseBits<N : num, T : iu32>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[338],
+    /* overloads */ &kOverloads[385],
   },
   {
     /* [66] */
-    /* fn round<T : f32_f16>(T) -> T */
-    /* fn round<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
+    /* fn round<T : fa_f32_f16>(@test_value(3.4) T) -> T */
+    /* fn round<N : num, T : fa_f32_f16>(@test_value(3.4) vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[340],
+    /* overloads */ &kOverloads[387],
   },
   {
     /* [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],
+    /* overloads */ &kOverloads[389],
   },
   {
     /* [68] */
@@ -14479,49 +14452,49 @@
     /* 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> */
     /* num overloads */ 3,
-    /* overloads */ &kOverloads[276],
+    /* overloads */ &kOverloads[269],
   },
   {
     /* [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[346],
+    /* overloads */ &kOverloads[391],
   },
   {
     /* [70] */
-    /* fn sin<T : f32_f16>(T) -> T */
-    /* fn sin<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
+    /* fn sin<T : fa_f32_f16>(T) -> T */
+    /* fn sin<N : num, T : fa_f32_f16>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[348],
+    /* overloads */ &kOverloads[393],
   },
   {
     /* [71] */
-    /* fn sinh<T : f32_f16>(T) -> T */
-    /* fn sinh<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
+    /* fn sinh<T : fa_f32_f16>(T) -> T */
+    /* fn sinh<N : num, T : fa_f32_f16>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[350],
+    /* overloads */ &kOverloads[395],
   },
   {
     /* [72] */
-    /* 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 smoothstep<T : fa_f32_f16>(@test_value(2) T, @test_value(4) T, @test_value(3) T) -> T */
+    /* fn smoothstep<N : num, T : fa_f32_f16>(@test_value(2) vec<N, T>, @test_value(4) vec<N, T>, @test_value(3) vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[352],
+    /* overloads */ &kOverloads[397],
   },
   {
     /* [73] */
-    /* fn sqrt<T : f32_f16>(T) -> T */
-    /* fn sqrt<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
+    /* fn sqrt<T : fa_f32_f16>(T) -> T */
+    /* fn sqrt<N : num, T : fa_f32_f16>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[354],
+    /* overloads */ &kOverloads[399],
   },
   {
     /* [74] */
     /* 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],
+    /* overloads */ &kOverloads[401],
   },
   {
     /* [75] */
@@ -14531,42 +14504,42 @@
   },
   {
     /* [76] */
-    /* fn tan<T : f32_f16>(T) -> T */
-    /* fn tan<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
+    /* fn tan<T : fa_f32_f16>(T) -> T */
+    /* fn tan<N : num, T : fa_f32_f16>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[360],
+    /* overloads */ &kOverloads[403],
   },
   {
     /* [77] */
-    /* fn tanh<T : f32_f16>(T) -> T */
-    /* fn tanh<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
+    /* fn tanh<T : fa_f32_f16>(T) -> T */
+    /* fn tanh<N : num, T : fa_f32_f16>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[362],
+    /* overloads */ &kOverloads[405],
   },
   {
     /* [78] */
     /* fn transpose<M : num, N : num, T : f32_f16>(mat<M, N, T>) -> mat<N, M, T> */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[457],
+    /* overloads */ &kOverloads[451],
   },
   {
     /* [79] */
-    /* fn trunc<T : f32_f16>(T) -> T */
-    /* fn trunc<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
+    /* fn trunc<T : fa_f32_f16>(@test_value(1.5) T) -> T */
+    /* fn trunc<N : num, T : fa_f32_f16>(@test_value(1.5) vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[366],
+    /* overloads */ &kOverloads[407],
   },
   {
     /* [80] */
     /* fn unpack2x16float(u32) -> vec2<f32> */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[456],
+    /* overloads */ &kOverloads[452],
   },
   {
     /* [81] */
     /* fn unpack2x16snorm(u32) -> vec2<f32> */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[455],
+    /* overloads */ &kOverloads[453],
   },
   {
     /* [82] */
@@ -14578,19 +14551,19 @@
     /* [83] */
     /* fn unpack4x8snorm(u32) -> vec4<f32> */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[453],
+    /* overloads */ &kOverloads[455],
   },
   {
     /* [84] */
     /* fn unpack4x8unorm(u32) -> vec4<f32> */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[452],
+    /* overloads */ &kOverloads[456],
   },
   {
     /* [85] */
     /* fn workgroupBarrier() */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[451],
+    /* overloads */ &kOverloads[457],
   },
   {
     /* [86] */
@@ -14639,7 +14612,7 @@
     /* 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[84],
+    /* overloads */ &kOverloads[71],
   },
   {
     /* [88] */
@@ -14650,7 +14623,7 @@
     /* fn textureGatherCompare(texture: texture_depth_cube, sampler: sampler_comparison, coords: vec3<f32>, depth_ref: f32) -> vec4<f32> */
     /* fn textureGatherCompare<A : iu32>(texture: texture_depth_cube_array, sampler: sampler_comparison, coords: vec3<f32>, array_index: A, depth_ref: f32) -> vec4<f32> */
     /* num overloads */ 6,
-    /* overloads */ &kOverloads[190],
+    /* overloads */ &kOverloads[159],
   },
   {
     /* [89] */
@@ -14660,7 +14633,7 @@
     /* fn textureNumLayers(texture: texture_depth_cube_array) -> u32 */
     /* fn textureNumLayers<F : texel_format, A : write>(texture: texture_storage_2d_array<F, A>) -> u32 */
     /* num overloads */ 5,
-    /* overloads */ &kOverloads[242],
+    /* overloads */ &kOverloads[231],
   },
   {
     /* [90] */
@@ -14675,14 +14648,14 @@
     /* fn textureNumLevels(texture: texture_depth_cube) -> u32 */
     /* fn textureNumLevels(texture: texture_depth_cube_array) -> u32 */
     /* num overloads */ 10,
-    /* overloads */ &kOverloads[107],
+    /* overloads */ &kOverloads[106],
   },
   {
     /* [91] */
     /* fn textureNumSamples<T : fiu32>(texture: texture_multisampled_2d<T>) -> u32 */
     /* fn textureNumSamples(texture: texture_depth_multisampled_2d) -> u32 */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[390],
+    /* overloads */ &kOverloads[409],
   },
   {
     /* [92] */
@@ -14702,7 +14675,7 @@
     /* fn textureSample(texture: texture_depth_cube, sampler: sampler, coords: vec3<f32>) -> f32 */
     /* fn textureSample<A : iu32>(texture: texture_depth_cube_array, sampler: sampler, coords: vec3<f32>, array_index: A) -> f32 */
     /* num overloads */ 15,
-    /* overloads */ &kOverloads[57],
+    /* overloads */ &kOverloads[27],
   },
   {
     /* [93] */
@@ -14715,7 +14688,7 @@
     /* fn textureSampleBias(texture: texture_cube<f32>, sampler: sampler, coords: vec3<f32>, bias: f32) -> vec4<f32> */
     /* fn textureSampleBias<A : iu32>(texture: texture_cube_array<f32>, sampler: sampler, coords: vec3<f32>, array_index: A, bias: f32) -> vec4<f32> */
     /* num overloads */ 8,
-    /* overloads */ &kOverloads[144],
+    /* overloads */ &kOverloads[143],
   },
   {
     /* [94] */
@@ -14726,7 +14699,7 @@
     /* 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[166],
+    /* overloads */ &kOverloads[165],
   },
   {
     /* [95] */
@@ -14737,7 +14710,7 @@
     /* 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[172],
+    /* overloads */ &kOverloads[171],
   },
   {
     /* [96] */
@@ -14750,7 +14723,7 @@
     /* fn textureSampleGrad(texture: texture_cube<f32>, sampler: sampler, coords: vec3<f32>, ddx: vec3<f32>, ddy: vec3<f32>) -> vec4<f32> */
     /* fn textureSampleGrad<A : iu32>(texture: texture_cube_array<f32>, sampler: sampler, coords: vec3<f32>, array_index: A, ddx: vec3<f32>, ddy: vec3<f32>) -> vec4<f32> */
     /* num overloads */ 8,
-    /* overloads */ &kOverloads[152],
+    /* overloads */ &kOverloads[151],
   },
   {
     /* [97] */
@@ -14768,16 +14741,15 @@
     /* fn textureSampleLevel<A : iu32, L : iu32>(texture: texture_depth_2d_array, sampler: sampler, coords: vec2<f32>, array_index: A, level: L, @const offset: vec2<i32>) -> f32 */
     /* fn textureSampleLevel<L : iu32>(texture: texture_depth_cube, sampler: sampler, coords: vec3<f32>, level: L) -> f32 */
     /* fn textureSampleLevel<A : iu32, L : iu32>(texture: texture_depth_cube_array, sampler: sampler, coords: vec3<f32>, array_index: A, level: L) -> f32 */
-    /* fn textureSampleLevel(texture: texture_external, sampler: sampler, coords: vec2<f32>) -> vec4<f32> */
-    /* num overloads */ 15,
-    /* overloads */ &kOverloads[27],
+    /* num overloads */ 14,
+    /* overloads */ &kOverloads[57],
   },
   {
     /* [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[404],
+    /* overloads */ &kOverloads[411],
   },
   {
     /* [99] */
@@ -14794,7 +14766,7 @@
     /* 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[72],
+    /* overloads */ &kOverloads[83],
   },
   {
     /* [100] */
@@ -14808,79 +14780,79 @@
     /* fn textureLoad<C : iu32, S : iu32>(texture: texture_depth_multisampled_2d, coords: vec2<C>, sample_index: S) -> f32 */
     /* fn textureLoad<C : iu32>(texture: texture_external, coords: vec2<C>) -> vec4<f32> */
     /* num overloads */ 9,
-    /* overloads */ &kOverloads[135],
+    /* overloads */ &kOverloads[116],
   },
   {
     /* [101] */
     /* fn atomicLoad<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>) -> T */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[449],
+    /* overloads */ &kOverloads[458],
   },
   {
     /* [102] */
     /* fn atomicStore<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[448],
+    /* overloads */ &kOverloads[459],
   },
   {
     /* [103] */
     /* fn atomicAdd<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[445],
+    /* overloads */ &kOverloads[460],
   },
   {
     /* [104] */
     /* fn atomicSub<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[444],
+    /* overloads */ &kOverloads[461],
   },
   {
     /* [105] */
     /* fn atomicMax<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[443],
+    /* overloads */ &kOverloads[462],
   },
   {
     /* [106] */
     /* fn atomicMin<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[442],
+    /* overloads */ &kOverloads[463],
   },
   {
     /* [107] */
     /* fn atomicAnd<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[441],
+    /* overloads */ &kOverloads[464],
   },
   {
     /* [108] */
     /* fn atomicOr<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[440],
+    /* overloads */ &kOverloads[465],
   },
   {
     /* [109] */
     /* fn atomicXor<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[439],
+    /* overloads */ &kOverloads[466],
   },
   {
     /* [110] */
     /* fn atomicExchange<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[438],
+    /* overloads */ &kOverloads[467],
   },
   {
     /* [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],
+    /* overloads */ &kOverloads[468],
   },
   {
     /* [112] */
     /* fn _tint_materialize<T>(T) -> T */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[436],
+    /* overloads */ &kOverloads[469],
   },
 };
 
@@ -14890,21 +14862,21 @@
     /* op !(bool) -> bool */
     /* op !<N : num>(vec<N, bool>) -> vec<N, bool> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[288],
+    /* overloads */ &kOverloads[413],
   },
   {
     /* [1] */
     /* op ~<T : ia_iu32>(T) -> T */
     /* op ~<T : ia_iu32, N : num>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[432],
+    /* overloads */ &kOverloads[415],
   },
   {
     /* [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[430],
+    /* overloads */ &kOverloads[417],
   },
 };
 constexpr uint8_t kUnaryOperatorNot = 0;
@@ -14920,7 +14892,7 @@
     /* op +<T : fia_fiu32_f16, N : num>(T, vec<N, T>) -> vec<N, T> */
     /* op +<T : fa_f32_f16, N : num, M : num>(mat<N, M, T>, mat<N, M, T>) -> mat<N, M, T> */
     /* num overloads */ 5,
-    /* overloads */ &kOverloads[232],
+    /* overloads */ &kOverloads[236],
   },
   {
     /* [1] */
@@ -14930,7 +14902,7 @@
     /* op -<T : fia_fiu32_f16, N : num>(T, vec<N, T>) -> vec<N, T> */
     /* op -<T : fa_f32_f16, N : num, M : num>(mat<N, M, T>, mat<N, M, T>) -> mat<N, M, T> */
     /* num overloads */ 5,
-    /* overloads */ &kOverloads[237],
+    /* overloads */ &kOverloads[241],
   },
   {
     /* [2] */
@@ -14944,7 +14916,7 @@
     /* op *<T : fa_f32_f16, C : num, R : num>(vec<R, T>, mat<C, R, T>) -> vec<C, T> */
     /* op *<T : fa_f32_f16, K : num, C : num, R : num>(mat<K, R, T>, mat<C, K, T>) -> mat<C, R, T> */
     /* num overloads */ 9,
-    /* overloads */ &kOverloads[117],
+    /* overloads */ &kOverloads[125],
   },
   {
     /* [3] */
@@ -14953,7 +14925,7 @@
     /* op /<T : fia_fiu32_f16, N : num>(vec<N, T>, T) -> vec<N, T> */
     /* op /<T : fia_fiu32_f16, N : num>(T, vec<N, T>) -> vec<N, T> */
     /* num overloads */ 4,
-    /* overloads */ &kOverloads[255],
+    /* overloads */ &kOverloads[246],
   },
   {
     /* [4] */
@@ -14962,14 +14934,14 @@
     /* op %<T : fiu32_f16, N : num>(vec<N, T>, T) -> vec<N, T> */
     /* op %<T : fiu32_f16, N : num>(T, vec<N, T>) -> vec<N, T> */
     /* num overloads */ 4,
-    /* overloads */ &kOverloads[259],
+    /* overloads */ &kOverloads[250],
   },
   {
     /* [5] */
     /* 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[418],
+    /* overloads */ &kOverloads[419],
   },
   {
     /* [6] */
@@ -14978,7 +14950,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 */ 4,
-    /* overloads */ &kOverloads[263],
+    /* overloads */ &kOverloads[254],
   },
   {
     /* [7] */
@@ -14987,61 +14959,61 @@
     /* op |<T : ia_iu32>(T, T) -> T */
     /* op |<T : ia_iu32, N : num>(vec<N, T>, vec<N, T>) -> vec<N, T> */
     /* num overloads */ 4,
-    /* overloads */ &kOverloads[247],
+    /* overloads */ &kOverloads[258],
   },
   {
     /* [8] */
     /* op &&(bool, bool) -> bool */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[446],
+    /* overloads */ &kOverloads[470],
   },
   {
     /* [9] */
     /* op ||(bool, bool) -> bool */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[447],
+    /* overloads */ &kOverloads[471],
   },
   {
     /* [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[412],
+    /* overloads */ &kOverloads[421],
   },
   {
     /* [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[410],
+    /* overloads */ &kOverloads[423],
   },
   {
     /* [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[394],
+    /* overloads */ &kOverloads[425],
   },
   {
     /* [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[378],
+    /* overloads */ &kOverloads[427],
   },
   {
     /* [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[376],
+    /* overloads */ &kOverloads[429],
   },
   {
     /* [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[374],
+    /* overloads */ &kOverloads[431],
   },
   {
     /* [16] */
@@ -15050,14 +15022,14 @@
     /* op <<<T : ia>(T, u32) -> T */
     /* op <<<T : ia, N : num>(vec<N, T>, vec<N, u32>) -> vec<N, T> */
     /* num overloads */ 4,
-    /* overloads */ &kOverloads[251],
+    /* overloads */ &kOverloads[262],
   },
   {
     /* [17] */
     /* 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[370],
+    /* overloads */ &kOverloads[433],
   },
 };
 constexpr uint8_t kBinaryOperatorPlus = 0;
@@ -15086,7 +15058,7 @@
     /* init i32(i32) -> i32 */
     /* conv i32<T : scalar_no_i32>(T) -> i32 */
     /* num overloads */ 3,
-    /* overloads */ &kOverloads[282],
+    /* overloads */ &kOverloads[272],
   },
   {
     /* [1] */
@@ -15094,7 +15066,7 @@
     /* init u32(u32) -> u32 */
     /* conv u32<T : scalar_no_u32>(T) -> u32 */
     /* num overloads */ 3,
-    /* overloads */ &kOverloads[279],
+    /* overloads */ &kOverloads[275],
   },
   {
     /* [2] */
@@ -15102,7 +15074,7 @@
     /* init f32(f32) -> f32 */
     /* conv f32<T : scalar_no_f32>(T) -> f32 */
     /* num overloads */ 3,
-    /* overloads */ &kOverloads[273],
+    /* overloads */ &kOverloads[278],
   },
   {
     /* [3] */
@@ -15110,7 +15082,7 @@
     /* init f16(f16) -> f16 */
     /* conv f16<T : scalar_no_f16>(T) -> f16 */
     /* num overloads */ 3,
-    /* overloads */ &kOverloads[270],
+    /* overloads */ &kOverloads[281],
   },
   {
     /* [4] */
@@ -15118,7 +15090,7 @@
     /* init bool(bool) -> bool */
     /* conv bool<T : scalar_no_bool>(T) -> bool */
     /* num overloads */ 3,
-    /* overloads */ &kOverloads[267],
+    /* overloads */ &kOverloads[284],
   },
   {
     /* [5] */
@@ -15132,7 +15104,7 @@
     /* conv vec2<T : u32, U : scalar_no_u32>(vec2<U>) -> vec2<u32> */
     /* conv vec2<T : bool, U : scalar_no_bool>(vec2<U>) -> vec2<bool> */
     /* num overloads */ 9,
-    /* overloads */ &kOverloads[126],
+    /* overloads */ &kOverloads[134],
   },
   {
     /* [6] */
@@ -15148,7 +15120,7 @@
     /* conv vec3<T : u32, U : scalar_no_u32>(vec3<U>) -> vec3<u32> */
     /* conv vec3<T : bool, U : scalar_no_bool>(vec3<U>) -> vec3<bool> */
     /* num overloads */ 11,
-    /* overloads */ &kOverloads[96],
+    /* overloads */ &kOverloads[95],
   },
   {
     /* [7] */
@@ -15179,7 +15151,7 @@
     /* conv mat2x2<T : f16>(mat2x2<f32>) -> mat2x2<f16> */
     /* conv mat2x2<T : f32>(mat2x2<f16>) -> mat2x2<f32> */
     /* num overloads */ 6,
-    /* overloads */ &kOverloads[208],
+    /* overloads */ &kOverloads[177],
   },
   {
     /* [9] */
@@ -15190,7 +15162,7 @@
     /* conv mat2x3<T : f16>(mat2x3<f32>) -> mat2x3<f16> */
     /* conv mat2x3<T : f32>(mat2x3<f16>) -> mat2x3<f32> */
     /* num overloads */ 6,
-    /* overloads */ &kOverloads[202],
+    /* overloads */ &kOverloads[183],
   },
   {
     /* [10] */
@@ -15201,7 +15173,7 @@
     /* conv mat2x4<T : f16>(mat2x4<f32>) -> mat2x4<f16> */
     /* conv mat2x4<T : f32>(mat2x4<f16>) -> mat2x4<f32> */
     /* num overloads */ 6,
-    /* overloads */ &kOverloads[178],
+    /* overloads */ &kOverloads[189],
   },
   {
     /* [11] */
@@ -15212,7 +15184,7 @@
     /* conv mat3x2<T : f16>(mat3x2<f32>) -> mat3x2<f16> */
     /* conv mat3x2<T : f32>(mat3x2<f16>) -> mat3x2<f32> */
     /* num overloads */ 6,
-    /* overloads */ &kOverloads[160],
+    /* overloads */ &kOverloads[195],
   },
   {
     /* [12] */
@@ -15223,7 +15195,7 @@
     /* conv mat3x3<T : f16>(mat3x3<f32>) -> mat3x3<f16> */
     /* conv mat3x3<T : f32>(mat3x3<f16>) -> mat3x3<f32> */
     /* num overloads */ 6,
-    /* overloads */ &kOverloads[220],
+    /* overloads */ &kOverloads[201],
   },
   {
     /* [13] */
@@ -15234,7 +15206,7 @@
     /* conv mat3x4<T : f16>(mat3x4<f32>) -> mat3x4<f16> */
     /* conv mat3x4<T : f32>(mat3x4<f16>) -> mat3x4<f32> */
     /* num overloads */ 6,
-    /* overloads */ &kOverloads[214],
+    /* overloads */ &kOverloads[207],
   },
   {
     /* [14] */
@@ -15245,7 +15217,7 @@
     /* conv mat4x2<T : f16>(mat4x2<f32>) -> mat4x2<f16> */
     /* conv mat4x2<T : f32>(mat4x2<f16>) -> mat4x2<f32> */
     /* num overloads */ 6,
-    /* overloads */ &kOverloads[226],
+    /* overloads */ &kOverloads[213],
   },
   {
     /* [15] */
@@ -15256,7 +15228,7 @@
     /* conv mat4x3<T : f16>(mat4x3<f32>) -> mat4x3<f16> */
     /* conv mat4x3<T : f32>(mat4x3<f16>) -> mat4x3<f32> */
     /* num overloads */ 6,
-    /* overloads */ &kOverloads[196],
+    /* overloads */ &kOverloads[219],
   },
   {
     /* [16] */
@@ -15267,7 +15239,7 @@
     /* conv mat4x4<T : f16>(mat4x4<f32>) -> mat4x4<f16> */
     /* conv mat4x4<T : f32>(mat4x4<f16>) -> mat4x4<f32> */
     /* num overloads */ 6,
-    /* overloads */ &kOverloads[184],
+    /* overloads */ &kOverloads[225],
   },
 };
 
diff --git a/src/tint/resolver/materialize_test.cc b/src/tint/resolver/materialize_test.cc
index 3b24010..a2084a4 100644
--- a/src/tint/resolver/materialize_test.cc
+++ b/src/tint/resolver/materialize_test.cc
@@ -352,9 +352,11 @@
             Structure("S", utils::Vector{Member("v", target_ty())});
             WrapInFunction(Construct(ty.type_name("S"), abstract_expr));
             break;
-        case Method::kBinaryOp:
-            WrapInFunction(Add(target_expr(), abstract_expr));
-            break;
+        case Method::kBinaryOp: {
+            // Add 0 to ensure no overflow with max float values
+            auto binary_target_expr = data.target_expr(*this, 0);
+            WrapInFunction(Add(binary_target_expr, abstract_expr));
+        } break;
         case Method::kSwitchCond:
             WrapInFunction(
                 Switch(abstract_expr,                                                       //
@@ -472,8 +474,13 @@
 constexpr Method kNoMaterializeMethods[] = {
     Method::kPhonyAssign,  //
     Method::kBinaryOp,
-    // TODO(crbug.com/tint/1504): Enable once "min" supports const evaluation
-    // Method::kBuiltinArg,
+};
+
+/// Methods that do not materialize
+constexpr Method kNoMaterializeScalarVectorMethods[] = {
+    Method::kPhonyAssign,  //
+    Method::kBinaryOp,
+    Method::kBuiltinArg,
 };
 INSTANTIATE_TEST_SUITE_P(
     MaterializeScalar,
@@ -697,6 +704,17 @@
                                               Types<AFloatM, AFloatM>(1.0_a, 1.0_a),  //
                                           })));
 
+INSTANTIATE_TEST_SUITE_P(NoMaterializeScalarVector,
+                         MaterializeAbstractNumericToConcreteType,
+                         testing::Combine(testing::Values(Expectation::kNoMaterialize),
+                                          testing::ValuesIn(kNoMaterializeScalarVectorMethods),
+                                          testing::ValuesIn(std::vector<Data>{
+                                              Types<AInt, AInt>(1_a, 1_a),            //
+                                              Types<AIntV, AIntV>(1_a, 1_a),          //
+                                              Types<AFloat, AFloat>(1.0_a, 1.0_a),    //
+                                              Types<AFloatV, AFloatV>(1.0_a, 1.0_a),  //
+                                          })));
+
 INSTANTIATE_TEST_SUITE_P(InvalidConversion,
                          MaterializeAbstractNumericToConcreteType,
                          testing::Combine(testing::Values(Expectation::kInvalidConversion),
@@ -771,9 +789,6 @@
     // let a = abstract_expr;
     kLet,
 
-    // min(abstract_expr, abstract_expr)
-    kBuiltinArg,
-
     // bitcast<f32>(abstract_expr)
     kBitcastF32Arg,
 
@@ -810,8 +825,6 @@
             return o << "var";
         case Method::kLet:
             return o << "let";
-        case Method::kBuiltinArg:
-            return o << "builtin-arg";
         case Method::kBitcastF32Arg:
             return o << "bitcast-f32-arg";
         case Method::kBitcastVec3F32Arg:
@@ -890,10 +903,6 @@
             WrapInFunction(Decl(Let("a", abstract_expr())));
             break;
         }
-        case Method::kBuiltinArg: {
-            WrapInFunction(CallStmt(Call("min", abstract_expr(), abstract_expr())));
-            break;
-        }
         case Method::kBitcastF32Arg: {
             WrapInFunction(Bitcast<f32>(abstract_expr()));
             break;
@@ -949,17 +958,8 @@
         }
         case Expectation::kInvalidConversion: {
             ASSERT_FALSE(r()->Resolve());
-            std::string expect;
-            switch (method) {
-                case Method::kBuiltinArg:
-                    expect = "error: no matching call to min(" + data.abstract_type_name + ", " +
-                             data.abstract_type_name + ")";
-                    break;
-                default:
-                    expect = "error: cannot convert value of type '" + data.abstract_type_name +
-                             "' to type '" + data.expected_type_name + "'";
-                    break;
-            }
+            std::string expect = "error: cannot convert value of type '" + data.abstract_type_name +
+                                 "' to type '" + data.expected_type_name + "'";
             EXPECT_THAT(r()->error(), testing::StartsWith(expect));
             break;
         }
@@ -977,16 +977,17 @@
 constexpr Method kScalarMethods[] = {
     Method::kLet,
     Method::kVar,
-    Method::kBuiltinArg,
     Method::kBitcastF32Arg,
     Method::kTintMaterializeBuiltin,
 };
 
 /// Methods that support vector materialization
 constexpr Method kVectorMethods[] = {
-    Method::kLet,          Method::kVar,
-    Method::kBuiltinArg,   Method::kBitcastVec3F32Arg,
-    Method::kRuntimeIndex, Method::kTintMaterializeBuiltin,
+    Method::kLet,
+    Method::kVar,
+    Method::kBitcastVec3F32Arg,
+    Method::kRuntimeIndex,
+    Method::kTintMaterializeBuiltin,
 };
 
 /// Methods that support matrix materialization
diff --git a/src/tint/resolver/override_test.cc b/src/tint/resolver/override_test.cc
index a12d3c1..132bd55 100644
--- a/src/tint/resolver/override_test.cc
+++ b/src/tint/resolver/override_test.cc
@@ -66,7 +66,6 @@
 }
 
 TEST_F(ResolverOverrideTest, WithAndWithoutIds) {
-    std::vector<ast::Variable*> variables;
     auto* a = Override("a", ty.f32(), Expr(1_f));
     auto* b = Override("b", ty.f32(), Expr(1_f));
     auto* c = Override("c", ty.f32(), Expr(1_f), Id(2_u));
@@ -113,5 +112,217 @@
     EXPECT_EQ(r()->error(), "12:34 error: 'override' of type f16 is not implemented yet");
 }
 
+TEST_F(ResolverOverrideTest, TransitiveReferences_DirectUse) {
+    auto* a = Override("a", ty.f32());
+    auto* b = Override("b", ty.f32(), Expr(1_f));
+    Override("unused", ty.f32(), Expr(1_f));
+    auto* func = Func("foo", utils::Empty, ty.void_(),
+                      utils::Vector{
+                          Assign(Phony(), "a"),
+                          Assign(Phony(), "b"),
+                      });
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto& refs = Sem().Get(func)->TransitivelyReferencedGlobals();
+    ASSERT_EQ(refs.Length(), 2u);
+    EXPECT_EQ(refs[0], Sem().Get(a));
+    EXPECT_EQ(refs[1], Sem().Get(b));
+}
+
+TEST_F(ResolverOverrideTest, TransitiveReferences_ViaOverrideInit) {
+    auto* a = Override("a", ty.f32());
+    auto* b = Override("b", ty.f32(), Mul(2_a, "a"));
+    Override("unused", ty.f32(), Expr(1_f));
+    auto* func = Func("foo", utils::Empty, ty.void_(),
+                      utils::Vector{
+                          Assign(Phony(), "b"),
+                      });
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    {
+        auto& refs = Sem().Get(b)->TransitivelyReferencedOverrides();
+        ASSERT_EQ(refs.Length(), 1u);
+        EXPECT_EQ(refs[0], Sem().Get(a));
+    }
+
+    {
+        auto& refs = Sem().Get(func)->TransitivelyReferencedGlobals();
+        ASSERT_EQ(refs.Length(), 2u);
+        EXPECT_EQ(refs[0], Sem().Get(b));
+        EXPECT_EQ(refs[1], Sem().Get(a));
+    }
+}
+
+TEST_F(ResolverOverrideTest, TransitiveReferences_ViaPrivateInit) {
+    auto* a = Override("a", ty.f32());
+    auto* b = GlobalVar("b", ast::AddressSpace::kPrivate, ty.f32(), Mul(2_a, "a"));
+    Override("unused", ty.f32(), Expr(1_f));
+    auto* func = Func("foo", utils::Empty, ty.void_(),
+                      utils::Vector{
+                          Assign(Phony(), "b"),
+                      });
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    {
+        auto& refs = Sem().Get<sem::GlobalVariable>(b)->TransitivelyReferencedOverrides();
+        ASSERT_EQ(refs.Length(), 1u);
+        EXPECT_EQ(refs[0], Sem().Get(a));
+    }
+
+    {
+        auto& refs = Sem().Get(func)->TransitivelyReferencedGlobals();
+        ASSERT_EQ(refs.Length(), 2u);
+        EXPECT_EQ(refs[0], Sem().Get(b));
+        EXPECT_EQ(refs[1], Sem().Get(a));
+    }
+}
+
+TEST_F(ResolverOverrideTest, TransitiveReferences_ViaAttribute) {
+    auto* a = Override("a", ty.i32());
+    auto* b = Override("b", ty.i32(), Mul(2_a, "a"));
+    Override("unused", ty.i32(), Expr(1_a));
+    auto* func = Func("foo", utils::Empty, ty.void_(),
+                      utils::Vector{
+                          Assign(Phony(), "b"),
+                      },
+                      utils::Vector{
+                          Stage(ast::PipelineStage::kCompute),
+                          WorkgroupSize(Mul(2_a, "b")),
+                      });
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto& refs = Sem().Get(func)->TransitivelyReferencedGlobals();
+    ASSERT_EQ(refs.Length(), 2u);
+    EXPECT_EQ(refs[0], Sem().Get(b));
+    EXPECT_EQ(refs[1], Sem().Get(a));
+}
+
+TEST_F(ResolverOverrideTest, TransitiveReferences_ViaArraySize) {
+    auto* a = Override("a", ty.i32());
+    auto* b = Override("b", ty.i32(), Mul(2_a, "a"));
+    auto* arr_ty = ty.array(ty.i32(), Mul(2_a, "b"));
+    auto* arr = GlobalVar("arr", ast::AddressSpace::kWorkgroup, arr_ty);
+    Override("unused", ty.i32(), Expr(1_a));
+    auto* func = Func("foo", utils::Empty, ty.void_(),
+                      utils::Vector{
+                          Assign(IndexAccessor("arr", 0_a), 42_a),
+                      });
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    {
+        auto& refs = Sem().Get(arr_ty)->TransitivelyReferencedOverrides();
+        ASSERT_EQ(refs.Length(), 2u);
+        EXPECT_EQ(refs[0], Sem().Get(b));
+        EXPECT_EQ(refs[1], Sem().Get(a));
+    }
+
+    {
+        auto& refs = Sem().Get<sem::GlobalVariable>(arr)->TransitivelyReferencedOverrides();
+        ASSERT_EQ(refs.Length(), 2u);
+        EXPECT_EQ(refs[0], Sem().Get(b));
+        EXPECT_EQ(refs[1], Sem().Get(a));
+    }
+
+    {
+        auto& refs = Sem().Get(func)->TransitivelyReferencedGlobals();
+        ASSERT_EQ(refs.Length(), 3u);
+        EXPECT_EQ(refs[0], Sem().Get(arr));
+        EXPECT_EQ(refs[1], Sem().Get(b));
+        EXPECT_EQ(refs[2], Sem().Get(a));
+    }
+}
+
+TEST_F(ResolverOverrideTest, TransitiveReferences_ViaArraySize_Alias) {
+    auto* a = Override("a", ty.i32());
+    auto* b = Override("b", ty.i32(), Mul(2_a, "a"));
+    auto* arr_ty = Alias("arr_ty", ty.array(ty.i32(), Mul(2_a, "b")));
+    auto* arr = GlobalVar("arr", ast::AddressSpace::kWorkgroup, ty.type_name("arr_ty"));
+    Override("unused", ty.i32(), Expr(1_a));
+    auto* func = Func("foo", utils::Empty, ty.void_(),
+                      utils::Vector{
+                          Assign(IndexAccessor("arr", 0_a), 42_a),
+                      });
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    {
+        auto& refs = Sem().Get<sem::Array>(arr_ty->type)->TransitivelyReferencedOverrides();
+        ASSERT_EQ(refs.Length(), 2u);
+        EXPECT_EQ(refs[0], Sem().Get(b));
+        EXPECT_EQ(refs[1], Sem().Get(a));
+    }
+
+    {
+        auto& refs = Sem().Get<sem::GlobalVariable>(arr)->TransitivelyReferencedOverrides();
+        ASSERT_EQ(refs.Length(), 2u);
+        EXPECT_EQ(refs[0], Sem().Get(b));
+        EXPECT_EQ(refs[1], Sem().Get(a));
+    }
+
+    {
+        auto& refs = Sem().Get(func)->TransitivelyReferencedGlobals();
+        ASSERT_EQ(refs.Length(), 3u);
+        EXPECT_EQ(refs[0], Sem().Get(arr));
+        EXPECT_EQ(refs[1], Sem().Get(b));
+        EXPECT_EQ(refs[2], Sem().Get(a));
+    }
+}
+
+TEST_F(ResolverOverrideTest, TransitiveReferences_MultipleEntryPoints) {
+    auto* a = Override("a", ty.i32());
+    auto* b1 = Override("b1", ty.i32(), Mul(2_a, "a"));
+    auto* b2 = Override("b2", ty.i32(), Mul(2_a, "a"));
+    auto* c1 = Override("c1", ty.i32());
+    auto* c2 = Override("c2", ty.i32());
+    auto* d = Override("d", ty.i32());
+    Alias("arr_ty1", ty.array(ty.i32(), Mul("b1", "c1")));
+    Alias("arr_ty2", ty.array(ty.i32(), Mul("b2", "c2")));
+    auto* arr1 = GlobalVar("arr1", ast::AddressSpace::kWorkgroup, ty.type_name("arr_ty1"));
+    auto* arr2 = GlobalVar("arr2", ast::AddressSpace::kWorkgroup, ty.type_name("arr_ty2"));
+    Override("unused", ty.i32(), Expr(1_a));
+    auto* func1 = Func("foo1", utils::Empty, ty.void_(),
+                       utils::Vector{
+                           Assign(IndexAccessor("arr1", 0_a), 42_a),
+                       },
+                       utils::Vector{
+                           Stage(ast::PipelineStage::kCompute),
+                           WorkgroupSize(Mul(2_a, "d")),
+                       });
+    auto* func2 = Func("foo2", utils::Empty, ty.void_(),
+                       utils::Vector{
+                           Assign(IndexAccessor("arr2", 0_a), 42_a),
+                       },
+                       utils::Vector{
+                           Stage(ast::PipelineStage::kCompute),
+                           WorkgroupSize(64_a),
+                       });
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    {
+        auto& refs = Sem().Get(func1)->TransitivelyReferencedGlobals();
+        ASSERT_EQ(refs.Length(), 5u);
+        EXPECT_EQ(refs[0], Sem().Get(d));
+        EXPECT_EQ(refs[1], Sem().Get(arr1));
+        EXPECT_EQ(refs[2], Sem().Get(b1));
+        EXPECT_EQ(refs[3], Sem().Get(a));
+        EXPECT_EQ(refs[4], Sem().Get(c1));
+    }
+
+    {
+        auto& refs = Sem().Get(func2)->TransitivelyReferencedGlobals();
+        ASSERT_EQ(refs.Length(), 4u);
+        EXPECT_EQ(refs[0], Sem().Get(arr2));
+        EXPECT_EQ(refs[1], Sem().Get(b2));
+        EXPECT_EQ(refs[2], Sem().Get(a));
+        EXPECT_EQ(refs[3], Sem().Get(c2));
+    }
+}
+
 }  // namespace
 }  // namespace tint::resolver
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index cb24955..143334e 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -31,7 +31,6 @@
 #include "src/tint/ast/depth_texture.h"
 #include "src/tint/ast/disable_validation_attribute.h"
 #include "src/tint/ast/discard_statement.h"
-#include "src/tint/ast/fallthrough_statement.h"
 #include "src/tint/ast/for_loop_statement.h"
 #include "src/tint/ast/id_attribute.h"
 #include "src/tint/ast/if_statement.h"
@@ -86,6 +85,7 @@
 #include "src/tint/utils/math.h"
 #include "src/tint/utils/reverse.h"
 #include "src/tint/utils/scoped_assignment.h"
+#include "src/tint/utils/string.h"
 #include "src/tint/utils/transform.h"
 #include "src/tint/utils/vector.h"
 
@@ -372,6 +372,8 @@
         return nullptr;
     }
 
+    RegisterLoadIfNeeded(rhs);
+
     // If the variable has no declared type, infer it from the RHS
     if (!ty) {
         ty = rhs->Type()->UnwrapRef();  // Implicit load of RHS
@@ -482,7 +484,7 @@
         sem->SetOverrideId(o);
 
         // Track the constant IDs that are specified in the shader.
-        override_ids_.emplace(o, sem);
+        override_ids_.Add(o, sem);
     }
 
     builder_->Sem().Add(v, sem);
@@ -578,6 +580,8 @@
         if (!storage_ty) {
             storage_ty = rhs->Type()->UnwrapRef();  // Implicit load of RHS
         }
+
+        RegisterLoadIfNeeded(rhs);
     }
 
     if (!storage_ty) {
@@ -842,7 +846,7 @@
             id = builder_->Sem().Get<sem::GlobalVariable>(override)->OverrideId();
         } else {
             // No ID was specified, so allocate the next available ID.
-            while (!ids_exhausted && override_ids_.count(next_id)) {
+            while (!ids_exhausted && override_ids_.Contains(next_id)) {
                 increment_next_id();
             }
             if (ids_exhausted) {
@@ -864,13 +868,16 @@
 void Resolver::SetShadows() {
     for (auto it : dependencies_.shadows) {
         Switch(
-            sem_.Get(it.first),  //
-            [&](sem::LocalVariable* local) { local->SetShadows(sem_.Get(it.second)); },
-            [&](sem::Parameter* param) { param->SetShadows(sem_.Get(it.second)); });
+            sem_.Get(it.key),  //
+            [&](sem::LocalVariable* local) { local->SetShadows(sem_.Get(it.value)); },
+            [&](sem::Parameter* param) { param->SetShadows(sem_.Get(it.value)); });
     }
 }
 
 sem::GlobalVariable* Resolver::GlobalVariable(const ast::Variable* v) {
+    utils::UniqueVector<const sem::GlobalVariable*, 4> transitively_referenced_overrides;
+    TINT_SCOPED_ASSIGNMENT(resolved_overrides_, &transitively_referenced_overrides);
+
     auto* sem = As<sem::GlobalVariable>(Variable(v, /* is_global */ true));
     if (!sem) {
         return nullptr;
@@ -894,6 +901,16 @@
         return nullptr;
     }
 
+    // Track the pipeline-overridable constants that are transitively referenced by this variable.
+    for (auto* var : transitively_referenced_overrides) {
+        sem->AddTransitivelyReferencedOverride(var);
+    }
+    if (auto* arr = sem->Type()->UnwrapRef()->As<sem::Array>()) {
+        for (auto* var : arr->TransitivelyReferencedOverrides()) {
+            sem->AddTransitivelyReferencedOverride(var);
+        }
+    }
+
     return sem;
 }
 
@@ -923,7 +940,7 @@
 
 sem::Function* Resolver::Function(const ast::Function* decl) {
     uint32_t parameter_index = 0;
-    std::unordered_map<Symbol, Source> parameter_names;
+    utils::Hashmap<Symbol, Source, 8> parameter_names;
     utils::Vector<sem::Parameter*, 8> parameters;
 
     // Resolve all the parameters
@@ -931,11 +948,10 @@
         Mark(param);
 
         {  // Check the parameter name is unique for the function
-            auto emplaced = parameter_names.emplace(param->symbol, param->source);
-            if (!emplaced.second) {
+            if (auto added = parameter_names.Add(param->symbol, param->source); !added) {
                 auto name = builder_->Symbols().NameFor(param->symbol);
                 AddError("redefinition of parameter '" + name + "'", param->source);
-                AddNote("previous definition is here", emplaced.first->second);
+                AddNote("previous definition is here", *added.value);
                 return nullptr;
             }
         }
@@ -1031,7 +1047,7 @@
     }
 
     if (decl->IsEntryPoint()) {
-        entry_points_.emplace_back(func);
+        entry_points_.Push(func);
     }
 
     if (decl->body) {
@@ -1217,7 +1233,6 @@
         [&](const ast::CompoundAssignmentStatement* c) { return CompoundAssignmentStatement(c); },
         [&](const ast::ContinueStatement* c) { return ContinueStatement(c); },
         [&](const ast::DiscardStatement* d) { return DiscardStatement(d); },
-        [&](const ast::FallthroughStatement* f) { return FallthroughStatement(f); },
         [&](const ast::IncrementDecrementStatement* i) { return IncrementDecrementStatement(i); },
         [&](const ast::ReturnStatement* r) { return ReturnStatement(r); },
         [&](const ast::VariableDeclStatement* v) { return VariableDeclStatement(v); },
@@ -1290,6 +1305,8 @@
         sem->Behaviors() = cond->Behaviors();
         sem->Behaviors().Remove(sem::Behavior::kNext);
 
+        RegisterLoadIfNeeded(cond);
+
         Mark(stmt->body);
         auto* body = builder_->create<sem::BlockStatement>(stmt->body, current_compound_statement_,
                                                            current_function_);
@@ -1383,6 +1400,8 @@
             }
             sem->SetCondition(cond);
             behaviors.Add(cond->Behaviors());
+
+            RegisterLoadIfNeeded(cond);
         }
 
         if (auto* continuing = stmt->continuing) {
@@ -1427,6 +1446,8 @@
         sem->SetCondition(cond);
         behaviors.Add(cond->Behaviors());
 
+        RegisterLoadIfNeeded(cond);
+
         Mark(stmt->body);
 
         auto* body = builder_->create<sem::LoopBlockStatement>(
@@ -1528,6 +1549,145 @@
     return nullptr;
 }
 
+void Resolver::RegisterLoadIfNeeded(const sem::Expression* expr) {
+    if (!expr) {
+        return;
+    }
+    if (!expr->Type()->Is<sem::Reference>()) {
+        return;
+    }
+    if (!current_function_) {
+        // There is currently no situation where the Load Rule can be invoked outside of a function.
+        return;
+    }
+    auto& info = alias_analysis_infos_[current_function_];
+    Switch(
+        expr->RootIdentifier(),
+        [&](const sem::GlobalVariable* global) {
+            info.module_scope_reads.insert({global, expr});
+        },
+        [&](const sem::Parameter* param) { info.parameter_reads.insert(param); });
+}
+
+void Resolver::RegisterStore(const sem::Expression* expr) {
+    auto& info = alias_analysis_infos_[current_function_];
+    Switch(
+        expr->RootIdentifier(),
+        [&](const sem::GlobalVariable* global) {
+            info.module_scope_writes.insert({global, expr});
+        },
+        [&](const sem::Parameter* param) { info.parameter_writes.insert(param); });
+}
+
+bool Resolver::AliasAnalysis(const sem::Call* call) {
+    auto* target = call->Target()->As<sem::Function>();
+    if (!target) {
+        return true;
+    }
+    if (validator_.IsValidationDisabled(target->Declaration()->attributes,
+                                        ast::DisabledValidation::kIgnorePointerAliasing)) {
+        return true;
+    }
+
+    // Helper to generate an aliasing error diagnostic.
+    struct Alias {
+        const sem::Expression* expr;          // the "other expression"
+        enum { Argument, ModuleScope } type;  // the type of the "other" expression
+        std::string access;                   // the access performed for the "other" expression
+    };
+    auto make_error = [&](const sem::Expression* arg, Alias&& var) {
+        // TODO(crbug.com/tint/1675): Switch to error and return false after deprecation period.
+        AddWarning("invalid aliased pointer argument", arg->Declaration()->source);
+        switch (var.type) {
+            case Alias::Argument:
+                AddNote("aliases with another argument passed here",
+                        var.expr->Declaration()->source);
+                break;
+            case Alias::ModuleScope: {
+                auto* func = var.expr->Stmt()->Function();
+                auto func_name = builder_->Symbols().NameFor(func->Declaration()->symbol);
+                AddNote(
+                    "aliases with module-scope variable " + var.access + " in '" + func_name + "'",
+                    var.expr->Declaration()->source);
+                break;
+            }
+        }
+        return true;
+    };
+
+    auto& args = call->Arguments();
+    auto& target_info = alias_analysis_infos_[target];
+    auto& caller_info = alias_analysis_infos_[current_function_];
+
+    // Track the set of root identifiers that are read and written by arguments passed in this call.
+    std::unordered_map<const sem::Variable*, const sem::Expression*> arg_reads;
+    std::unordered_map<const sem::Variable*, const sem::Expression*> arg_writes;
+    for (size_t i = 0; i < args.Length(); i++) {
+        auto* arg = args[i];
+        if (!arg->Type()->Is<sem::Pointer>()) {
+            continue;
+        }
+
+        auto* root = arg->RootIdentifier();
+        if (target_info.parameter_writes.count(target->Parameters()[i])) {
+            // Arguments that are written to can alias with any other argument or module-scope
+            // variable access.
+            if (arg_writes.count(root)) {
+                return make_error(arg, {arg_writes.at(root), Alias::Argument, "write"});
+            }
+            if (arg_reads.count(root)) {
+                return make_error(arg, {arg_reads.at(root), Alias::Argument, "read"});
+            }
+            if (target_info.module_scope_reads.count(root)) {
+                return make_error(
+                    arg, {target_info.module_scope_reads.at(root), Alias::ModuleScope, "read"});
+            }
+            if (target_info.module_scope_writes.count(root)) {
+                return make_error(
+                    arg, {target_info.module_scope_writes.at(root), Alias::ModuleScope, "write"});
+            }
+            arg_writes.insert({root, arg});
+
+            // Propagate the write access to the caller.
+            Switch(
+                root,
+                [&](const sem::GlobalVariable* global) {
+                    caller_info.module_scope_writes.insert({global, arg});
+                },
+                [&](const sem::Parameter* param) { caller_info.parameter_writes.insert(param); });
+        } else if (target_info.parameter_reads.count(target->Parameters()[i])) {
+            // Arguments that are read from can alias with arguments or module-scope variables that
+            // are written to.
+            if (arg_writes.count(root)) {
+                return make_error(arg, {arg_writes.at(root), Alias::Argument, "write"});
+            }
+            if (target_info.module_scope_writes.count(root)) {
+                return make_error(
+                    arg, {target_info.module_scope_writes.at(root), Alias::ModuleScope, "write"});
+            }
+            arg_reads.insert({root, arg});
+
+            // Propagate the read access to the caller.
+            Switch(
+                root,
+                [&](const sem::GlobalVariable* global) {
+                    caller_info.module_scope_reads.insert({global, arg});
+                },
+                [&](const sem::Parameter* param) { caller_info.parameter_reads.insert(param); });
+        }
+    }
+
+    // Propagate module-scope variable uses to the caller.
+    for (auto read : target_info.module_scope_reads) {
+        caller_info.module_scope_reads.insert({read.first, read.second});
+    }
+    for (auto write : target_info.module_scope_writes) {
+        caller_info.module_scope_writes.insert({write.first, write.second});
+    }
+
+    return true;
+}
+
 const sem::Type* Resolver::ConcreteType(const sem::Type* ty,
                                         const sem::Type* target_ty,
                                         const Source& source) {
@@ -1670,6 +1830,7 @@
         //     vec2(1, 2)[runtime-index]
         obj = Materialize(obj);
     }
+    RegisterLoadIfNeeded(idx);
     if (!obj) {
         return nullptr;
     }
@@ -1712,7 +1873,7 @@
     bool has_side_effects = idx->HasSideEffects() || obj->HasSideEffects();
     auto* sem = builder_->create<sem::IndexAccessorExpression>(
         expr, ty, stage, obj, idx, current_statement_, std::move(val), has_side_effects,
-        obj->SourceVariable());
+        obj->RootIdentifier());
     sem->Behaviors() = idx->Behaviors() + obj->Behaviors();
     return sem;
 }
@@ -1727,6 +1888,8 @@
         return nullptr;
     }
 
+    RegisterLoadIfNeeded(inner);
+
     const sem::Constant* val = nullptr;
     if (auto r = const_eval_.Bitcast(ty, inner)) {
         val = r.Get();
@@ -1766,6 +1929,8 @@
         args.Push(arg);
         args_stage = sem::EarliestStage(args_stage, arg->Stage());
         arg_behaviors.Add(arg->Behaviors());
+
+        RegisterLoadIfNeeded(arg);
     }
     arg_behaviors.Remove(sem::Behavior::kNext);
 
@@ -1850,8 +2015,8 @@
             [&](const sem::F32*) { return ct_init_or_conv(InitConvIntrinsic::kF32, nullptr); },
             [&](const sem::Bool*) { return ct_init_or_conv(InitConvIntrinsic::kBool, nullptr); },
             [&](const sem::Array* arr) -> sem::Call* {
-                auto* call_target = utils::GetOrCreate(
-                    array_inits_, ArrayInitializerSig{{arr, args.Length(), args_stage}},
+                auto* call_target = array_inits_.GetOrCreate(
+                    ArrayInitializerSig{{arr, args.Length(), args_stage}},
                     [&]() -> sem::TypeInitializer* {
                         auto params = utils::Transform(args, [&](auto, size_t i) {
                             return builder_->create<sem::Parameter>(
@@ -1877,8 +2042,8 @@
                 return call;
             },
             [&](const sem::Struct* str) -> sem::Call* {
-                auto* call_target = utils::GetOrCreate(
-                    struct_inits_, StructInitializerSig{{str, args.Length(), args_stage}},
+                auto* call_target = struct_inits_.GetOrCreate(
+                    StructInitializerSig{{str, args.Length(), args_stage}},
                     [&]() -> sem::TypeInitializer* {
                         utils::Vector<const sem::Parameter*, 8> params;
                         params.Resize(std::min(args.Length(), str->Members().size()));
@@ -1981,9 +2146,9 @@
                         AddError(
                             "cannot infer common array element type from initializer arguments",
                             expr->source);
-                        std::unordered_set<const sem::Type*> types;
+                        utils::Hashset<const sem::Type*, 8> types;
                         for (size_t i = 0; i < args.Length(); i++) {
-                            if (types.emplace(args[i]->Type()).second) {
+                            if (types.Add(args[i]->Type())) {
                                 AddNote("argument " + std::to_string(i) + " is of type '" +
                                             sem_.TypeNameOf(args[i]->Type()) + "'",
                                         args[i]->Declaration()->source);
@@ -2207,6 +2372,10 @@
             current_function_->AddTransitivelyReferencedGlobal(var);
         }
 
+        if (!AliasAnalysis(call)) {
+            return nullptr;
+        }
+
         // Note: Validation *must* be performed before calling this method.
         CollectTextureSamplerPairs(target, call->Arguments());
     }
@@ -2321,9 +2490,22 @@
             }
         }
 
+        auto* global = variable->As<sem::GlobalVariable>();
         if (current_function_) {
-            if (auto* global = variable->As<sem::GlobalVariable>()) {
+            if (global) {
                 current_function_->AddDirectlyReferencedGlobal(global);
+                for (auto* var : global->TransitivelyReferencedOverrides()) {
+                    current_function_->AddTransitivelyReferencedGlobal(var);
+                }
+            }
+        } else if (variable->Declaration()->Is<ast::Override>()) {
+            if (resolved_overrides_) {
+                // Track the reference to this pipeline-overridable constant and any other
+                // pipeline-overridable constants that it references.
+                resolved_overrides_->Add(global);
+                for (auto* var : global->TransitivelyReferencedOverrides()) {
+                    resolved_overrides_->Add(var);
+                }
             }
         } else if (variable->Declaration()->Is<ast::Var>()) {
             // Use of a module-scope 'var' outside of a function.
@@ -2365,7 +2547,7 @@
     auto* structure = sem_.TypeOf(expr->structure);
     auto* storage_ty = structure->UnwrapRef();
     auto* object = sem_.Get(expr->structure);
-    auto* source_var = object->SourceVariable();
+    auto* root_ident = object->RootIdentifier();
 
     const sem::Type* ty = nullptr;
 
@@ -2381,18 +2563,29 @@
             const sem::StructMember* member = nullptr;
             for (auto* m : str->Members()) {
                 if (m->Name() == symbol) {
-                    ty = m->Type();
                     member = m;
                     break;
                 }
             }
 
-            if (ty == nullptr) {
+            // TODO(crbug.com/tint/1757): Remove
+            if (utils::HasPrefix(builder_->Symbols().NameFor(str->Name()), "__frexp_result")) {
+                if (builder_->Symbols().NameFor(symbol) == "sig") {
+                    AddWarning(
+                        "use of deprecated language feature: 'sig' has been renamed to 'fract'",
+                        expr->member->source);
+                    member = str->Members()[0];
+                }
+            }
+
+            if (member == nullptr) {
                 AddError("struct member " + builder_->Symbols().NameFor(symbol) + " not found",
                          expr->source);
                 return nullptr;
             }
 
+            ty = member->Type();
+
             // If we're extracting from a reference, we return a reference.
             if (auto* ref = structure->As<sem::Reference>()) {
                 ty = builder_->create<sem::Reference>(ty, ref->AddressSpace(), ref->Access());
@@ -2404,7 +2597,7 @@
             }
             return builder_->create<sem::StructMemberAccess>(expr, ty, current_statement_,
                                                              val.Get(), object, member,
-                                                             has_side_effects, source_var);
+                                                             has_side_effects, root_ident);
         },
 
         [&](const sem::Vector* vec) -> sem::Expression* {
@@ -2476,7 +2669,7 @@
                 return nullptr;
             }
             return builder_->create<sem::Swizzle>(expr, ty, current_statement_, val.Get(), object,
-                                                  std::move(swizzle), has_side_effects, source_var);
+                                                  std::move(swizzle), has_side_effects, root_ident);
         },
 
         [&](Default) {
@@ -2511,6 +2704,9 @@
         }
     }
 
+    RegisterLoadIfNeeded(lhs);
+    RegisterLoadIfNeeded(rhs);
+
     const sem::Constant* value = nullptr;
     if (stage == sem::EvaluationStage::kConstant) {
         if (op.const_eval_fn) {
@@ -2549,7 +2745,7 @@
     }
 
     const sem::Type* ty = nullptr;
-    const sem::Variable* source_var = nullptr;
+    const sem::Variable* root_ident = nullptr;
     const sem::Constant* value = nullptr;
     auto stage = sem::EvaluationStage::kRuntime;
 
@@ -2573,7 +2769,7 @@
                 ty = builder_->create<sem::Pointer>(ref->StoreType(), ref->AddressSpace(),
                                                     ref->Access());
 
-                source_var = expr->SourceVariable();
+                root_ident = expr->RootIdentifier();
             } else {
                 AddError("cannot take the address of expression", unary->expr->source);
                 return nullptr;
@@ -2584,7 +2780,7 @@
             if (auto* ptr = expr_ty->As<sem::Pointer>()) {
                 ty = builder_->create<sem::Reference>(ptr->StoreType(), ptr->AddressSpace(),
                                                       ptr->Access());
-                source_var = expr->SourceVariable();
+                root_ident = expr->RootIdentifier();
             } else {
                 AddError("cannot dereference expression of type '" + sem_.TypeNameOf(expr_ty) + "'",
                          unary->expr->source);
@@ -2618,12 +2814,13 @@
                     stage = sem::EvaluationStage::kRuntime;
                 }
             }
+            RegisterLoadIfNeeded(expr);
             break;
         }
     }
 
     auto* sem = builder_->create<sem::Expression>(unary, ty, stage, current_statement_, value,
-                                                  expr->HasSideEffects(), source_var);
+                                                  expr->HasSideEffects(), root_ident);
     sem->Behaviors() = expr->Behaviors();
     return sem;
 }
@@ -2657,6 +2854,9 @@
         return nullptr;
     }
 
+    utils::UniqueVector<const sem::GlobalVariable*, 4> transitively_referenced_overrides;
+    TINT_SCOPED_ASSIGNMENT(resolved_overrides_, &transitively_referenced_overrides);
+
     auto* el_ty = Type(arr->type);
     if (!el_ty) {
         return nullptr;
@@ -2687,14 +2887,18 @@
     }
 
     if (el_ty->Is<sem::Atomic>()) {
-        atomic_composite_info_.emplace(out, arr->type->source);
+        atomic_composite_info_.Add(out, &arr->type->source);
     } else {
-        auto found = atomic_composite_info_.find(el_ty);
-        if (found != atomic_composite_info_.end()) {
-            atomic_composite_info_.emplace(out, found->second);
+        if (auto found = atomic_composite_info_.Get(el_ty)) {
+            atomic_composite_info_.Add(out, *found);
         }
     }
 
+    // Track the pipeline-overridable constants that are transitively referenced by this array type.
+    for (auto* var : transitively_referenced_overrides) {
+        out->AddTransitivelyReferencedOverride(var);
+    }
+
     return out;
 }
 
@@ -2750,9 +2954,14 @@
     for (auto* attr : attributes) {
         Mark(attr);
         if (auto* sd = attr->As<ast::StrideAttribute>()) {
-            explicit_stride = sd->stride;
-            if (!validator_.ArrayStrideAttribute(sd, el_ty->Size(), el_ty->Align())) {
-                return false;
+            // If the element type is not plain, then el_ty->Align() may be 0, in which case we
+            // could get a DBZ in ArrayStrideAttribute(). In this case, validation will error about
+            // the invalid array element type (which is tested later), so this is just a seatbelt.
+            if (IsPlain(el_ty)) {
+                explicit_stride = sd->stride;
+                if (!validator_.ArrayStrideAttribute(sd, el_ty->Size(), el_ty->Align())) {
+                    return false;
+                }
             }
             continue;
         }
@@ -2832,15 +3041,14 @@
     // validation.
     uint64_t struct_size = 0;
     uint64_t struct_align = 1;
-    std::unordered_map<Symbol, const ast::StructMember*> member_map;
+    utils::Hashmap<Symbol, const ast::StructMember*, 8> member_map;
 
     for (auto* member : str->members) {
         Mark(member);
-        auto result = member_map.emplace(member->symbol, member);
-        if (!result.second) {
+        if (auto added = member_map.Add(member->symbol, member); !added) {
             AddError("redefinition of '" + builder_->Symbols().NameFor(member->symbol) + "'",
                      member->source);
-            AddNote("previous definition is here", result.first->second->source);
+            AddNote("previous definition is here", (*added.value)->source);
             return nullptr;
         }
 
@@ -3027,12 +3235,11 @@
     for (size_t i = 0; i < sem_members.size(); i++) {
         auto* mem_type = sem_members[i]->Type();
         if (mem_type->Is<sem::Atomic>()) {
-            atomic_composite_info_.emplace(out, sem_members[i]->Declaration()->source);
+            atomic_composite_info_.Add(out, &sem_members[i]->Declaration()->source);
             break;
         } else {
-            auto found = atomic_composite_info_.find(mem_type);
-            if (found != atomic_composite_info_.end()) {
-                atomic_composite_info_.emplace(out, found->second);
+            if (auto found = atomic_composite_info_.Get(mem_type)) {
+                atomic_composite_info_.Add(out, *found);
                 break;
             }
         }
@@ -3070,6 +3277,8 @@
             }
             behaviors.Add(expr->Behaviors() - sem::Behavior::kNext);
             value_ty = expr->Type()->UnwrapRef();
+
+            RegisterLoadIfNeeded(expr);
         } else {
             value_ty = builder_->create<sem::Void>();
         }
@@ -3093,6 +3302,8 @@
         }
         behaviors = cond->Behaviors() - sem::Behavior::kNext;
 
+        RegisterLoadIfNeeded(cond);
+
         auto* cond_ty = cond->Type()->UnwrapRef();
 
         // Determine the common type across all selectors and the switch expression
@@ -3138,7 +3349,7 @@
         if (behaviors.Contains(sem::Behavior::kBreak)) {
             behaviors.Add(sem::Behavior::kNext);
         }
-        behaviors.Remove(sem::Behavior::kBreak, sem::Behavior::kFallthrough);
+        behaviors.Remove(sem::Behavior::kBreak);
 
         return validator_.SwitchStatement(stmt);
     });
@@ -3196,12 +3407,18 @@
             }
         }
 
+        RegisterLoadIfNeeded(rhs);
+
         auto& behaviors = sem->Behaviors();
         behaviors = rhs->Behaviors();
         if (!is_phony_assignment) {
             behaviors.Add(lhs->Behaviors());
         }
 
+        if (!is_phony_assignment) {
+            RegisterStore(lhs);
+        }
+
         return validator_.Assignment(stmt, sem_.TypeOf(stmt->rhs));
     });
 }
@@ -3228,6 +3445,8 @@
         sem->Behaviors() = cond->Behaviors();
         sem->Behaviors().Add(sem::Behavior::kBreak);
 
+        RegisterLoadIfNeeded(cond);
+
         return validator_.BreakIfStatement(sem, current_statement_);
     });
 }
@@ -3259,6 +3478,9 @@
             return false;
         }
 
+        RegisterLoadIfNeeded(rhs);
+        RegisterStore(lhs);
+
         sem->Behaviors() = rhs->Behaviors() + lhs->Behaviors();
 
         auto* lhs_ty = lhs->Type()->UnwrapRef();
@@ -3295,20 +3517,8 @@
     auto* sem =
         builder_->create<sem::Statement>(stmt, current_compound_statement_, current_function_);
     return StatementScope(stmt, sem, [&] {
-        sem->Behaviors() = sem::Behavior::kDiscard;
         current_function_->SetDiscardStatement(sem);
-
-        return validator_.DiscardStatement(sem, current_statement_);
-    });
-}
-
-sem::Statement* Resolver::FallthroughStatement(const ast::FallthroughStatement* stmt) {
-    auto* sem =
-        builder_->create<sem::Statement>(stmt, current_compound_statement_, current_function_);
-    return StatementScope(stmt, sem, [&] {
-        sem->Behaviors() = sem::Behavior::kFallthrough;
-
-        return validator_.FallthroughStatement(sem);
+        return true;
     });
 }
 
@@ -3323,6 +3533,9 @@
         }
         sem->Behaviors() = lhs->Behaviors();
 
+        RegisterLoadIfNeeded(lhs);
+        RegisterStore(lhs);
+
         return validator_.IncrementDecrementStatement(stmt);
     });
 }
diff --git a/src/tint/resolver/resolver.h b/src/tint/resolver/resolver.h
index 89aab13..0b31fe1 100644
--- a/src/tint/resolver/resolver.h
+++ b/src/tint/resolver/resolver.h
@@ -155,6 +155,18 @@
     sem::Expression* MemberAccessor(const ast::MemberAccessorExpression*);
     sem::Expression* UnaryOp(const ast::UnaryOpExpression*);
 
+    /// Register a memory load from an expression, to track accesses to root identifiers in order to
+    /// perform alias analysis.
+    void RegisterLoadIfNeeded(const sem::Expression* expr);
+
+    /// Register a memory store to an expression, to track accesses to root identifiers in order to
+    /// perform alias analysis.
+    void RegisterStore(const sem::Expression* expr);
+
+    /// Perform pointer alias analysis for `call`.
+    /// @returns true is the call arguments are free from aliasing issues, false otherwise.
+    bool AliasAnalysis(const sem::Call* call);
+
     /// If `expr` is not of an abstract-numeric type, then Materialize() will just return `expr`.
     /// If `expr` is of an abstract-numeric type:
     /// * Materialize will create and return a sem::Materialize node wrapping `expr`.
@@ -214,7 +226,6 @@
     sem::Statement* CompoundAssignmentStatement(const ast::CompoundAssignmentStatement*);
     sem::Statement* ContinueStatement(const ast::ContinueStatement*);
     sem::Statement* DiscardStatement(const ast::DiscardStatement*);
-    sem::Statement* FallthroughStatement(const ast::FallthroughStatement*);
     sem::ForLoopStatement* ForLoopStatement(const ast::ForLoopStatement*);
     sem::WhileStatement* WhileStatement(const ast::WhileStatement*);
     sem::GlobalVariable* GlobalVariable(const ast::Variable*);
@@ -426,6 +437,19 @@
         const char* constraint = nullptr;
     };
 
+    /// AliasAnalysisInfo captures the memory accesses performed by a given function for the purpose
+    /// of determining if any two arguments alias at any callsite.
+    struct AliasAnalysisInfo {
+        /// The set of module-scope variables that are written to, and where that write occurs.
+        std::unordered_map<const sem::Variable*, const sem::Expression*> module_scope_writes;
+        /// The set of module-scope variables that are read from, and where that read occurs.
+        std::unordered_map<const sem::Variable*, const sem::Expression*> module_scope_reads;
+        /// The set of function parameters that are written to.
+        std::unordered_set<const sem::Variable*> parameter_writes;
+        /// The set of function parameters that are read from.
+        std::unordered_set<const sem::Variable*> parameter_reads;
+    };
+
     ProgramBuilder* const builder_;
     diag::List& diagnostics_;
     ConstEval const_eval_;
@@ -434,17 +458,19 @@
     SemHelper sem_;
     Validator validator_;
     ast::Extensions enabled_extensions_;
-    std::vector<sem::Function*> entry_points_;
-    std::unordered_map<const sem::Type*, const Source&> atomic_composite_info_;
+    utils::Vector<sem::Function*, 8> entry_points_;
+    utils::Hashmap<const sem::Type*, const Source*, 8> atomic_composite_info_;
     utils::Bitset<0> marked_;
     ExprEvalStageConstraint expr_eval_stage_constraint_;
-    std::unordered_map<OverrideId, const sem::Variable*> override_ids_;
-    std::unordered_map<ArrayInitializerSig, sem::CallTarget*> array_inits_;
-    std::unordered_map<StructInitializerSig, sem::CallTarget*> struct_inits_;
+    std::unordered_map<const sem::Function*, AliasAnalysisInfo> alias_analysis_infos_;
+    utils::Hashmap<OverrideId, const sem::Variable*, 8> override_ids_;
+    utils::Hashmap<ArrayInitializerSig, sem::CallTarget*, 8> array_inits_;
+    utils::Hashmap<StructInitializerSig, sem::CallTarget*, 8> struct_inits_;
     sem::Function* current_function_ = nullptr;
     sem::Statement* current_statement_ = nullptr;
     sem::CompoundStatement* current_compound_statement_ = nullptr;
     uint32_t current_scoping_depth_ = 0;
+    utils::UniqueVector<const sem::GlobalVariable*, 4>* resolved_overrides_ = nullptr;
 };
 
 }  // namespace tint::resolver
diff --git a/src/tint/resolver/resolver_behavior_test.cc b/src/tint/resolver/resolver_behavior_test.cc
index 150512a..6f405a3 100644
--- a/src/tint/resolver/resolver_behavior_test.cc
+++ b/src/tint/resolver/resolver_behavior_test.cc
@@ -30,10 +30,11 @@
 class ResolverBehaviorTest : public ResolverTest {
   protected:
     void SetUp() override {
-        // Create a function called 'DiscardOrNext' which returns an i32, and has
-        // the behavior of {Discard, Return}, which when called, will have the
-        // behavior {Discard, Next}.
-        Func("DiscardOrNext", utils::Empty, ty.i32(),
+        // Create a function called 'Next' which returns an i32, and has the behavior of {Return},
+        // which when called, will have the behavior {Next}.
+        // It contains a discard statement, which should not affect the behavior analysis or any
+        // related validation.
+        Func("Next", utils::Empty, ty.i32(),
              utils::Vector{
                  If(true, Block(Discard())),
                  Return(1_i),
@@ -42,7 +43,7 @@
 };
 
 TEST_F(ResolverBehaviorTest, ExprBinaryOp_LHS) {
-    auto* stmt = Decl(Var("lhs", ty.i32(), Add(Call("DiscardOrNext"), 1_i)));
+    auto* stmt = Decl(Var("lhs", ty.i32(), Add(Call("Next"), 1_i)));
 
     Func("F", utils::Empty, ty.void_(), utils::Vector{stmt},
          utils::Vector{Stage(ast::PipelineStage::kFragment)});
@@ -50,11 +51,11 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(stmt);
-    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kNext));
 }
 
 TEST_F(ResolverBehaviorTest, ExprBinaryOp_RHS) {
-    auto* stmt = Decl(Var("lhs", ty.i32(), Add(1_i, Call("DiscardOrNext"))));
+    auto* stmt = Decl(Var("lhs", ty.i32(), Add(1_i, Call("Next"))));
 
     Func("F", utils::Empty, ty.void_(), utils::Vector{stmt},
          utils::Vector{Stage(ast::PipelineStage::kFragment)});
@@ -62,11 +63,11 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(stmt);
-    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kNext));
 }
 
 TEST_F(ResolverBehaviorTest, ExprBitcastOp) {
-    auto* stmt = Decl(Var("lhs", ty.u32(), Bitcast<u32>(Call("DiscardOrNext"))));
+    auto* stmt = Decl(Var("lhs", ty.u32(), Bitcast<u32>(Call("Next"))));
 
     Func("F", utils::Empty, ty.void_(), utils::Vector{stmt},
          utils::Vector{Stage(ast::PipelineStage::kFragment)});
@@ -74,7 +75,7 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(stmt);
-    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kNext));
 }
 
 TEST_F(ResolverBehaviorTest, ExprIndex_Arr) {
@@ -92,11 +93,11 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(stmt);
-    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kNext));
 }
 
 TEST_F(ResolverBehaviorTest, ExprIndex_Idx) {
-    auto* stmt = Decl(Var("lhs", ty.i32(), IndexAccessor("arr", Call("DiscardOrNext"))));
+    auto* stmt = Decl(Var("lhs", ty.i32(), IndexAccessor("arr", Call("Next"))));
 
     Func("F", utils::Empty, ty.void_(),
          utils::Vector{
@@ -108,13 +109,12 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(stmt);
-    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kNext));
 }
 
 TEST_F(ResolverBehaviorTest, ExprUnaryOp) {
-    auto* stmt =
-        Decl(Var("lhs", ty.i32(),
-                 create<ast::UnaryOpExpression>(ast::UnaryOp::kComplement, Call("DiscardOrNext"))));
+    auto* stmt = Decl(Var("lhs", ty.i32(),
+                          create<ast::UnaryOpExpression>(ast::UnaryOp::kComplement, Call("Next"))));
 
     Func("F", utils::Empty, ty.void_(), utils::Vector{stmt},
          utils::Vector{Stage(ast::PipelineStage::kFragment)});
@@ -122,7 +122,7 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(stmt);
-    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kNext));
 }
 
 TEST_F(ResolverBehaviorTest, StmtAssign) {
@@ -138,7 +138,7 @@
 }
 
 TEST_F(ResolverBehaviorTest, StmtAssign_LHSDiscardOrNext) {
-    auto* stmt = Assign(IndexAccessor("lhs", Call("DiscardOrNext")), 1_i);
+    auto* stmt = Assign(IndexAccessor("lhs", Call("Next")), 1_i);
 
     Func("F", utils::Empty, ty.void_(),
          utils::Vector{
@@ -150,11 +150,11 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(stmt);
-    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kNext));
 }
 
 TEST_F(ResolverBehaviorTest, StmtAssign_RHSDiscardOrNext) {
-    auto* stmt = Assign("lhs", Call("DiscardOrNext"));
+    auto* stmt = Assign("lhs", Call("Next"));
 
     Func("F", utils::Empty, ty.void_(),
          utils::Vector{
@@ -166,7 +166,7 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(stmt);
-    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kNext));
 }
 
 TEST_F(ResolverBehaviorTest, StmtBlockEmpty) {
@@ -180,7 +180,7 @@
 }
 
 TEST_F(ResolverBehaviorTest, StmtBlockSingleStmt) {
-    auto* stmt = Block(Discard());
+    auto* stmt = Block(Return());
 
     Func("F", utils::Empty, ty.void_(), utils::Vector{stmt},
          utils::Vector{Stage(ast::PipelineStage::kFragment)});
@@ -188,7 +188,7 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(stmt);
-    EXPECT_EQ(sem->Behaviors(), sem::Behavior::kDiscard);
+    EXPECT_EQ(sem->Behaviors(), sem::Behavior::kReturn);
 }
 
 TEST_F(ResolverBehaviorTest, StmtCallReturn) {
@@ -212,12 +212,11 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(stmt);
-    EXPECT_EQ(sem->Behaviors(), sem::Behavior::kDiscard);
+    EXPECT_EQ(sem->Behaviors(), sem::Behavior::kNext);
 }
 
 TEST_F(ResolverBehaviorTest, StmtCallFuncMayDiscard) {
-    auto* stmt =
-        For(Decl(Var("v", ty.i32(), Call("DiscardOrNext"))), nullptr, nullptr, Block(Break()));
+    auto* stmt = For(Decl(Var("v", ty.i32(), Call("Next"))), nullptr, nullptr, Block(Break()));
 
     Func("F", utils::Empty, ty.void_(), utils::Vector{stmt},
          utils::Vector{Stage(ast::PipelineStage::kFragment)});
@@ -225,7 +224,7 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(stmt);
-    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kNext));
 }
 
 TEST_F(ResolverBehaviorTest, StmtBreak) {
@@ -258,7 +257,7 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(stmt);
-    EXPECT_EQ(sem->Behaviors(), sem::Behavior::kDiscard);
+    EXPECT_EQ(sem->Behaviors(), sem::Behavior::kNext);
 }
 
 TEST_F(ResolverBehaviorTest, StmtForLoopEmpty_NoExit) {
@@ -288,7 +287,7 @@
 }
 
 TEST_F(ResolverBehaviorTest, StmtForLoopDiscard) {
-    auto* stmt = For(nullptr, nullptr, nullptr, Block(Discard()));
+    auto* stmt = For(nullptr, nullptr, nullptr, Block(Discard(), Break()));
 
     Func("F", utils::Empty, ty.void_(), utils::Vector{stmt},
          utils::Vector{Stage(ast::PipelineStage::kFragment)});
@@ -296,7 +295,7 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(stmt);
-    EXPECT_EQ(sem->Behaviors(), sem::Behavior::kDiscard);
+    EXPECT_EQ(sem->Behaviors(), sem::Behavior::kNext);
 }
 
 TEST_F(ResolverBehaviorTest, StmtForLoopReturn) {
@@ -310,8 +309,7 @@
 }
 
 TEST_F(ResolverBehaviorTest, StmtForLoopBreak_InitCallFuncMayDiscard) {
-    auto* stmt =
-        For(Decl(Var("v", ty.i32(), Call("DiscardOrNext"))), nullptr, nullptr, Block(Break()));
+    auto* stmt = For(Decl(Var("v", ty.i32(), Call("Next"))), nullptr, nullptr, Block(Break()));
 
     Func("F", utils::Empty, ty.void_(), utils::Vector{stmt},
          utils::Vector{Stage(ast::PipelineStage::kFragment)});
@@ -319,11 +317,11 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(stmt);
-    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kNext));
 }
 
 TEST_F(ResolverBehaviorTest, StmtForLoopEmpty_InitCallFuncMayDiscard) {
-    auto* stmt = For(Decl(Var("v", ty.i32(), Call("DiscardOrNext"))), nullptr, nullptr, Block());
+    auto* stmt = For(Decl(Var("v", ty.i32(), Call("Next"))), nullptr, nullptr, Block(Break()));
 
     Func("F", utils::Empty, ty.void_(), utils::Vector{stmt},
          utils::Vector{Stage(ast::PipelineStage::kFragment)});
@@ -331,7 +329,7 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(stmt);
-    EXPECT_EQ(sem->Behaviors(), sem::Behavior::kDiscard);
+    EXPECT_EQ(sem->Behaviors(), sem::Behavior::kNext);
 }
 
 TEST_F(ResolverBehaviorTest, StmtForLoopEmpty_CondTrue) {
@@ -345,7 +343,7 @@
 }
 
 TEST_F(ResolverBehaviorTest, StmtForLoopEmpty_CondCallFuncMayDiscard) {
-    auto* stmt = For(nullptr, Equal(Call("DiscardOrNext"), 1_i), nullptr, Block());
+    auto* stmt = For(nullptr, Equal(Call("Next"), 1_i), nullptr, Block());
 
     Func("F", utils::Empty, ty.void_(), utils::Vector{stmt},
          utils::Vector{Stage(ast::PipelineStage::kFragment)});
@@ -353,7 +351,7 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(stmt);
-    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kNext));
 }
 
 TEST_F(ResolverBehaviorTest, StmtWhileBreak) {
@@ -375,7 +373,7 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(stmt);
-    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kNext));
 }
 
 TEST_F(ResolverBehaviorTest, StmtWhileReturn) {
@@ -399,7 +397,7 @@
 }
 
 TEST_F(ResolverBehaviorTest, StmtWhileEmpty_CondCallFuncMayDiscard) {
-    auto* stmt = While(Equal(Call("DiscardOrNext"), 1_i), Block());
+    auto* stmt = While(Equal(Call("Next"), 1_i), Block());
 
     Func("F", utils::Empty, ty.void_(), utils::Vector{stmt},
          utils::Vector{Stage(ast::PipelineStage::kFragment)});
@@ -407,7 +405,7 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(stmt);
-    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kNext));
 }
 
 TEST_F(ResolverBehaviorTest, StmtIfTrue_ThenEmptyBlock) {
@@ -429,7 +427,7 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(stmt);
-    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kNext));
 }
 
 TEST_F(ResolverBehaviorTest, StmtIfTrue_ThenEmptyBlock_ElseDiscard) {
@@ -441,7 +439,7 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(stmt);
-    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kNext));
 }
 
 TEST_F(ResolverBehaviorTest, StmtIfTrue_ThenDiscard_ElseDiscard) {
@@ -453,11 +451,11 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(stmt);
-    EXPECT_EQ(sem->Behaviors(), sem::Behavior::kDiscard);
+    EXPECT_EQ(sem->Behaviors(), sem::Behavior::kNext);
 }
 
 TEST_F(ResolverBehaviorTest, StmtIfCallFuncMayDiscard_ThenEmptyBlock) {
-    auto* stmt = If(Equal(Call("DiscardOrNext"), 1_i), Block());
+    auto* stmt = If(Equal(Call("Next"), 1_i), Block());
 
     Func("F", utils::Empty, ty.void_(), utils::Vector{stmt},
          utils::Vector{Stage(ast::PipelineStage::kFragment)});
@@ -465,12 +463,12 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(stmt);
-    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kNext));
 }
 
 TEST_F(ResolverBehaviorTest, StmtIfTrue_ThenEmptyBlock_ElseCallFuncMayDiscard) {
     auto* stmt = If(true, Block(),  //
-                    Else(If(Equal(Call("DiscardOrNext"), 1_i), Block())));
+                    Else(If(Equal(Call("Next"), 1_i), Block())));
 
     Func("F", utils::Empty, ty.void_(), utils::Vector{stmt},
          utils::Vector{Stage(ast::PipelineStage::kFragment)});
@@ -478,7 +476,7 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(stmt);
-    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kNext));
 }
 
 TEST_F(ResolverBehaviorTest, StmtLetDecl) {
@@ -492,7 +490,7 @@
 }
 
 TEST_F(ResolverBehaviorTest, StmtLetDecl_RHSDiscardOrNext) {
-    auto* stmt = Decl(Let("lhs", ty.i32(), Call("DiscardOrNext")));
+    auto* stmt = Decl(Let("lhs", ty.i32(), Call("Next")));
 
     Func("F", utils::Empty, ty.void_(), utils::Vector{stmt},
          utils::Vector{Stage(ast::PipelineStage::kFragment)});
@@ -500,7 +498,7 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(stmt);
-    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kNext));
 }
 
 TEST_F(ResolverBehaviorTest, StmtLoopEmpty_NoExit) {
@@ -530,7 +528,7 @@
 }
 
 TEST_F(ResolverBehaviorTest, StmtLoopDiscard) {
-    auto* stmt = Loop(Block(Discard()));
+    auto* stmt = Loop(Block(Discard(), Break()));
 
     Func("F", utils::Empty, ty.void_(), utils::Vector{stmt},
          utils::Vector{Stage(ast::PipelineStage::kFragment)});
@@ -538,7 +536,7 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(stmt);
-    EXPECT_EQ(sem->Behaviors(), sem::Behavior::kDiscard);
+    EXPECT_EQ(sem->Behaviors(), sem::Behavior::kNext);
 }
 
 TEST_F(ResolverBehaviorTest, StmtLoopReturn) {
@@ -559,16 +557,6 @@
     EXPECT_EQ(r()->error(), "12:34 error: loop does not exit");
 }
 
-TEST_F(ResolverBehaviorTest, StmtLoopEmpty_ContIfTrueBreak) {
-    auto* stmt = Loop(Block(), Block(If(true, Block(Break()))));
-    WrapInFunction(stmt);
-
-    ASSERT_TRUE(r()->Resolve()) << r()->error();
-
-    auto* sem = Sem().Get(stmt);
-    EXPECT_EQ(sem->Behaviors(), sem::Behavior::kNext);
-}
-
 TEST_F(ResolverBehaviorTest, StmtLoopEmpty_BreakIf) {
     auto* stmt = Loop(Block(), Block(BreakIf(true)));
     WrapInFunction(stmt);
@@ -590,14 +578,14 @@
 }
 
 TEST_F(ResolverBehaviorTest, StmtReturn_DiscardOrNext) {
-    auto* stmt = Return(Call("DiscardOrNext"));
+    auto* stmt = Return(Call("Next"));
 
     Func("F", utils::Empty, ty.i32(), utils::Vector{stmt});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(stmt);
-    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kReturn, sem::Behavior::kDiscard));
+    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kReturn));
 }
 
 TEST_F(ResolverBehaviorTest, StmtSwitch_CondTrue_DefaultEmpty) {
@@ -629,7 +617,7 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(stmt);
-    EXPECT_EQ(sem->Behaviors(), sem::Behavior::kDiscard);
+    EXPECT_EQ(sem->Behaviors(), sem::Behavior::kNext);
 }
 
 TEST_F(ResolverBehaviorTest, StmtSwitch_CondLiteral_DefaultReturn) {
@@ -661,7 +649,7 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(stmt);
-    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kNext, sem::Behavior::kDiscard));
+    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kNext));
 }
 
 TEST_F(ResolverBehaviorTest, StmtSwitch_CondLiteral_Case0Empty_DefaultReturn) {
@@ -683,7 +671,7 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(stmt);
-    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kNext));
 }
 
 TEST_F(ResolverBehaviorTest, StmtSwitch_CondLiteral_Case0Discard_DefaultDiscard) {
@@ -696,7 +684,7 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(stmt);
-    EXPECT_EQ(sem->Behaviors(), sem::Behavior::kDiscard);
+    EXPECT_EQ(sem->Behaviors(), sem::Behavior::kNext);
 }
 
 TEST_F(ResolverBehaviorTest, StmtSwitch_CondLiteral_Case0Discard_DefaultReturn) {
@@ -709,7 +697,7 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(stmt);
-    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kReturn));
+    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kReturn, sem::Behavior::kNext));
 }
 
 TEST_F(ResolverBehaviorTest, StmtSwitch_CondLiteral_Case0Discard_Case1Return_DefaultEmpty) {
@@ -724,12 +712,11 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(stmt);
-    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext,
-                                               sem::Behavior::kReturn));
+    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kNext, sem::Behavior::kReturn));
 }
 
 TEST_F(ResolverBehaviorTest, StmtSwitch_CondCallFuncMayDiscard_DefaultEmpty) {
-    auto* stmt = Switch(Call("DiscardOrNext"), DefaultCase(Block()));
+    auto* stmt = Switch(Call("Next"), DefaultCase(Block()));
 
     Func("F", utils::Empty, ty.void_(), utils::Vector{stmt},
          utils::Vector{Stage(ast::PipelineStage::kFragment)});
@@ -737,7 +724,7 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(stmt);
-    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kNext));
 }
 
 TEST_F(ResolverBehaviorTest, StmtVarDecl) {
@@ -751,7 +738,7 @@
 }
 
 TEST_F(ResolverBehaviorTest, StmtVarDecl_RHSDiscardOrNext) {
-    auto* stmt = Decl(Var("lhs", ty.i32(), Call("DiscardOrNext")));
+    auto* stmt = Decl(Var("lhs", ty.i32(), Call("Next")));
 
     Func("F", utils::Empty, ty.void_(), utils::Vector{stmt},
          utils::Vector{Stage(ast::PipelineStage::kFragment)});
@@ -759,7 +746,7 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(stmt);
-    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+    EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kNext));
 }
 
 }  // namespace
diff --git a/src/tint/resolver/source_variable_test.cc b/src/tint/resolver/root_identifier_test.cc
similarity index 69%
rename from src/tint/resolver/source_variable_test.cc
rename to src/tint/resolver/root_identifier_test.cc
index e662395..934feae 100644
--- a/src/tint/resolver/source_variable_test.cc
+++ b/src/tint/resolver/root_identifier_test.cc
@@ -23,9 +23,9 @@
 namespace tint::resolver {
 namespace {
 
-class ResolverSourceVariableTest : public ResolverTest {};
+class ResolverRootIdentifierTest : public ResolverTest {};
 
-TEST_F(ResolverSourceVariableTest, GlobalPrivateVar) {
+TEST_F(ResolverRootIdentifierTest, GlobalPrivateVar) {
     auto* a = GlobalVar("a", ty.f32(), ast::AddressSpace::kPrivate);
     auto* expr = Expr(a);
     WrapInFunction(expr);
@@ -33,10 +33,10 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem_a = Sem().Get(a);
-    EXPECT_EQ(Sem().Get(expr)->SourceVariable(), sem_a);
+    EXPECT_EQ(Sem().Get(expr)->RootIdentifier(), sem_a);
 }
 
-TEST_F(ResolverSourceVariableTest, GlobalWorkgroupVar) {
+TEST_F(ResolverRootIdentifierTest, GlobalWorkgroupVar) {
     auto* a = GlobalVar("a", ty.f32(), ast::AddressSpace::kWorkgroup);
     auto* expr = Expr(a);
     WrapInFunction(expr);
@@ -44,10 +44,10 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem_a = Sem().Get(a);
-    EXPECT_EQ(Sem().Get(expr)->SourceVariable(), sem_a);
+    EXPECT_EQ(Sem().Get(expr)->RootIdentifier(), sem_a);
 }
 
-TEST_F(ResolverSourceVariableTest, GlobalStorageVar) {
+TEST_F(ResolverRootIdentifierTest, GlobalStorageVar) {
     auto* a = GlobalVar("a", ty.f32(), ast::AddressSpace::kStorage, Group(0_a), Binding(0_a));
     auto* expr = Expr(a);
     WrapInFunction(expr);
@@ -55,10 +55,10 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem_a = Sem().Get(a);
-    EXPECT_EQ(Sem().Get(expr)->SourceVariable(), sem_a);
+    EXPECT_EQ(Sem().Get(expr)->RootIdentifier(), sem_a);
 }
 
-TEST_F(ResolverSourceVariableTest, GlobalUniformVar) {
+TEST_F(ResolverRootIdentifierTest, GlobalUniformVar) {
     auto* a = GlobalVar("a", ty.f32(), ast::AddressSpace::kUniform, Group(0_a), Binding(0_a));
     auto* expr = Expr(a);
     WrapInFunction(expr);
@@ -66,10 +66,10 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem_a = Sem().Get(a);
-    EXPECT_EQ(Sem().Get(expr)->SourceVariable(), sem_a);
+    EXPECT_EQ(Sem().Get(expr)->RootIdentifier(), sem_a);
 }
 
-TEST_F(ResolverSourceVariableTest, GlobalTextureVar) {
+TEST_F(ResolverRootIdentifierTest, GlobalTextureVar) {
     auto* a = GlobalVar("a", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
                         ast::AddressSpace::kNone, Group(0_a), Binding(0_a));
     auto* expr = Expr(a);
@@ -78,10 +78,10 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem_a = Sem().Get(a);
-    EXPECT_EQ(Sem().Get(expr)->SourceVariable(), sem_a);
+    EXPECT_EQ(Sem().Get(expr)->RootIdentifier(), sem_a);
 }
 
-TEST_F(ResolverSourceVariableTest, GlobalOverride) {
+TEST_F(ResolverRootIdentifierTest, GlobalOverride) {
     auto* a = Override("a", ty.f32(), Expr(1_f));
     auto* expr = Expr(a);
     WrapInFunction(expr);
@@ -89,10 +89,10 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem_a = Sem().Get(a);
-    EXPECT_EQ(Sem().Get(expr)->SourceVariable(), sem_a);
+    EXPECT_EQ(Sem().Get(expr)->RootIdentifier(), sem_a);
 }
 
-TEST_F(ResolverSourceVariableTest, GlobalConst) {
+TEST_F(ResolverRootIdentifierTest, GlobalConst) {
     auto* a = GlobalConst("a", ty.f32(), Expr(1_f));
     auto* expr = Expr(a);
     WrapInFunction(expr);
@@ -100,10 +100,10 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem_a = Sem().Get(a);
-    EXPECT_EQ(Sem().Get(expr)->SourceVariable(), sem_a);
+    EXPECT_EQ(Sem().Get(expr)->RootIdentifier(), sem_a);
 }
 
-TEST_F(ResolverSourceVariableTest, FunctionVar) {
+TEST_F(ResolverRootIdentifierTest, FunctionVar) {
     auto* a = Var("a", ty.f32());
     auto* expr = Expr(a);
     WrapInFunction(a, expr);
@@ -111,10 +111,10 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem_a = Sem().Get(a);
-    EXPECT_EQ(Sem().Get(expr)->SourceVariable(), sem_a);
+    EXPECT_EQ(Sem().Get(expr)->RootIdentifier(), sem_a);
 }
 
-TEST_F(ResolverSourceVariableTest, FunctionLet) {
+TEST_F(ResolverRootIdentifierTest, FunctionLet) {
     auto* a = Let("a", ty.f32(), Expr(1_f));
     auto* expr = Expr(a);
     WrapInFunction(a, expr);
@@ -122,10 +122,10 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem_a = Sem().Get(a);
-    EXPECT_EQ(Sem().Get(expr)->SourceVariable(), sem_a);
+    EXPECT_EQ(Sem().Get(expr)->RootIdentifier(), sem_a);
 }
 
-TEST_F(ResolverSourceVariableTest, Parameter) {
+TEST_F(ResolverRootIdentifierTest, Parameter) {
     auto* a = Param("a", ty.f32());
     auto* expr = Expr(a);
     Func("foo", utils::Vector{a}, ty.void_(), utils::Vector{WrapInStatement(expr)});
@@ -133,10 +133,10 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem_a = Sem().Get(a);
-    EXPECT_EQ(Sem().Get(expr)->SourceVariable(), sem_a);
+    EXPECT_EQ(Sem().Get(expr)->RootIdentifier(), sem_a);
 }
 
-TEST_F(ResolverSourceVariableTest, PointerParameter) {
+TEST_F(ResolverRootIdentifierTest, PointerParameter) {
     // fn foo(a : ptr<function, f32>)
     // {
     //   let b = a;
@@ -151,11 +151,11 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem_param = Sem().Get(param);
-    EXPECT_EQ(Sem().Get(expr_param)->SourceVariable(), sem_param);
-    EXPECT_EQ(Sem().Get(expr_let)->SourceVariable(), sem_param);
+    EXPECT_EQ(Sem().Get(expr_param)->RootIdentifier(), sem_param);
+    EXPECT_EQ(Sem().Get(expr_let)->RootIdentifier(), sem_param);
 }
 
-TEST_F(ResolverSourceVariableTest, VarCopyVar) {
+TEST_F(ResolverRootIdentifierTest, VarCopyVar) {
     // {
     //   var a : f32;
     //   var b = a;
@@ -170,11 +170,11 @@
 
     auto* sem_a = Sem().Get(a);
     auto* sem_b = Sem().Get(b);
-    EXPECT_EQ(Sem().Get(expr_a)->SourceVariable(), sem_a);
-    EXPECT_EQ(Sem().Get(expr_b)->SourceVariable(), sem_b);
+    EXPECT_EQ(Sem().Get(expr_a)->RootIdentifier(), sem_a);
+    EXPECT_EQ(Sem().Get(expr_b)->RootIdentifier(), sem_b);
 }
 
-TEST_F(ResolverSourceVariableTest, LetCopyVar) {
+TEST_F(ResolverRootIdentifierTest, LetCopyVar) {
     // {
     //   var a : f32;
     //   let b = a;
@@ -189,11 +189,11 @@
 
     auto* sem_a = Sem().Get(a);
     auto* sem_b = Sem().Get(b);
-    EXPECT_EQ(Sem().Get(expr_a)->SourceVariable(), sem_a);
-    EXPECT_EQ(Sem().Get(expr_b)->SourceVariable(), sem_b);
+    EXPECT_EQ(Sem().Get(expr_a)->RootIdentifier(), sem_a);
+    EXPECT_EQ(Sem().Get(expr_b)->RootIdentifier(), sem_b);
 }
 
-TEST_F(ResolverSourceVariableTest, ThroughIndexAccessor) {
+TEST_F(ResolverRootIdentifierTest, ThroughIndexAccessor) {
     // var<private> a : array<f32, 4u>;
     // {
     //   a[2i]
@@ -205,10 +205,10 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem_a = Sem().Get(a);
-    EXPECT_EQ(Sem().Get(expr)->SourceVariable(), sem_a);
+    EXPECT_EQ(Sem().Get(expr)->RootIdentifier(), sem_a);
 }
 
-TEST_F(ResolverSourceVariableTest, ThroughMemberAccessor) {
+TEST_F(ResolverRootIdentifierTest, ThroughMemberAccessor) {
     // struct S { f : f32 }
     // var<private> a : S;
     // {
@@ -222,10 +222,10 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem_a = Sem().Get(a);
-    EXPECT_EQ(Sem().Get(expr)->SourceVariable(), sem_a);
+    EXPECT_EQ(Sem().Get(expr)->RootIdentifier(), sem_a);
 }
 
-TEST_F(ResolverSourceVariableTest, ThroughPointers) {
+TEST_F(ResolverRootIdentifierTest, ThroughPointers) {
     // var<private> a : f32;
     // {
     //   let a_ptr1 = &*&a;
@@ -244,49 +244,49 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem_a = Sem().Get(a);
-    EXPECT_EQ(Sem().Get(address_of_1)->SourceVariable(), sem_a);
-    EXPECT_EQ(Sem().Get(address_of_2)->SourceVariable(), sem_a);
-    EXPECT_EQ(Sem().Get(address_of_3)->SourceVariable(), sem_a);
-    EXPECT_EQ(Sem().Get(deref_1)->SourceVariable(), sem_a);
-    EXPECT_EQ(Sem().Get(deref_2)->SourceVariable(), sem_a);
+    EXPECT_EQ(Sem().Get(address_of_1)->RootIdentifier(), sem_a);
+    EXPECT_EQ(Sem().Get(address_of_2)->RootIdentifier(), sem_a);
+    EXPECT_EQ(Sem().Get(address_of_3)->RootIdentifier(), sem_a);
+    EXPECT_EQ(Sem().Get(deref_1)->RootIdentifier(), sem_a);
+    EXPECT_EQ(Sem().Get(deref_2)->RootIdentifier(), sem_a);
 }
 
-TEST_F(ResolverSourceVariableTest, Literal) {
+TEST_F(ResolverRootIdentifierTest, Literal) {
     auto* expr = Expr(1_f);
     WrapInFunction(expr);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
-    EXPECT_EQ(Sem().Get(expr)->SourceVariable(), nullptr);
+    EXPECT_EQ(Sem().Get(expr)->RootIdentifier(), nullptr);
 }
 
-TEST_F(ResolverSourceVariableTest, FunctionReturnValue) {
+TEST_F(ResolverRootIdentifierTest, FunctionReturnValue) {
     auto* expr = Call("min", 1_f, 2_f);
     WrapInFunction(expr);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
-    EXPECT_EQ(Sem().Get(expr)->SourceVariable(), nullptr);
+    EXPECT_EQ(Sem().Get(expr)->RootIdentifier(), nullptr);
 }
 
-TEST_F(ResolverSourceVariableTest, BinaryExpression) {
+TEST_F(ResolverRootIdentifierTest, BinaryExpression) {
     auto* a = Var("a", ty.f32());
     auto* expr = Add(a, Expr(1_f));
     WrapInFunction(a, expr);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
-    EXPECT_EQ(Sem().Get(expr)->SourceVariable(), nullptr);
+    EXPECT_EQ(Sem().Get(expr)->RootIdentifier(), nullptr);
 }
 
-TEST_F(ResolverSourceVariableTest, UnaryExpression) {
+TEST_F(ResolverRootIdentifierTest, UnaryExpression) {
     auto* a = Var("a", ty.f32());
     auto* expr = create<ast::UnaryOpExpression>(ast::UnaryOp::kNegation, Expr(a));
     WrapInFunction(a, expr);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
-    EXPECT_EQ(Sem().Get(expr)->SourceVariable(), nullptr);
+    EXPECT_EQ(Sem().Get(expr)->RootIdentifier(), nullptr);
 }
 
 }  // namespace
diff --git a/src/tint/resolver/sem_helper.h b/src/tint/resolver/sem_helper.h
index 9b0967b..12ef4a2 100644
--- a/src/tint/resolver/sem_helper.h
+++ b/src/tint/resolver/sem_helper.h
@@ -54,8 +54,8 @@
     /// @param node the node to retrieve
     template <typename SEM = sem::Node>
     SEM* ResolvedSymbol(const ast::Node* node) const {
-        auto* resolved = utils::Lookup(dependencies_.resolved_symbols, node);
-        return resolved ? const_cast<SEM*>(builder_->Sem().Get<SEM>(resolved)) : nullptr;
+        auto* resolved = dependencies_.resolved_symbols.Find(node);
+        return resolved ? const_cast<SEM*>(builder_->Sem().Get<SEM>(*resolved)) : nullptr;
     }
 
     /// @returns the resolved type of the ast::Expression `expr`
diff --git a/src/tint/resolver/type_validation_test.cc b/src/tint/resolver/type_validation_test.cc
index b539e49..59367d0 100644
--- a/src/tint/resolver/type_validation_test.cc
+++ b/src/tint/resolver/type_validation_test.cc
@@ -836,6 +836,15 @@
               "12:34 error: texture_2d<f32> cannot be used as an element type of an array");
 }
 
+TEST_F(ResolverTypeValidationTest, ArrayOfNonStorableTypeWithStride) {
+    auto* ptr_ty = ty.pointer<u32>(Source{{12, 34}}, ast::AddressSpace::kUniform);
+    GlobalVar("arr", ty.array(ptr_ty, 4_i, 16), ast::AddressSpace::kPrivate);
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(),
+              "12:34 error: ptr<uniform, u32, read> cannot be used as an element type of an array");
+}
+
 TEST_F(ResolverTypeValidationTest, VariableAsType) {
     // var<private> a : i32;
     // var<private> b : a;
diff --git a/src/tint/resolver/uniformity.cc b/src/tint/resolver/uniformity.cc
index d5ff803..afdbdfb 100644
--- a/src/tint/resolver/uniformity.cc
+++ b/src/tint/resolver/uniformity.cc
@@ -16,8 +16,6 @@
 
 #include <limits>
 #include <string>
-#include <unordered_map>
-#include <unordered_set>
 #include <utility>
 #include <vector>
 
@@ -139,7 +137,7 @@
     bool pointer_may_become_non_uniform = false;
     /// The parameters that are required to be uniform for the contents of this pointer parameter to
     /// be uniform at function exit.
-    std::vector<const sem::Parameter*> pointer_param_output_sources;
+    utils::Vector<const sem::Parameter*, 8> pointer_param_output_sources;
     /// The node in the graph that corresponds to this parameter's initial value.
     Node* init_value;
     /// The node in the graph that corresponds to this parameter's output value (or nullptr).
@@ -166,7 +164,7 @@
         }
 
         // Create nodes for parameters.
-        parameters.resize(func->params.Length());
+        parameters.Resize(func->params.Length());
         for (size_t i = 0; i < func->params.Length(); i++) {
             auto* param = func->params[i];
             auto param_name = builder->Symbols().NameFor(param->symbol);
@@ -177,7 +175,7 @@
             if (sem->Type()->Is<sem::Pointer>()) {
                 node_init = CreateNode("ptrparam_" + name + "_init");
                 parameters[i].pointer_return_value = CreateNode("ptrparam_" + name + "_return");
-                local_var_decls.insert(sem);
+                local_var_decls.Add(sem);
             } else {
                 node_init = CreateNode("param_" + name);
             }
@@ -194,7 +192,7 @@
     /// The function's uniformity effects.
     FunctionTag function_tag;
     /// The uniformity requirements of the function's parameters.
-    std::vector<ParameterInfo> parameters;
+    utils::Vector<ParameterInfo, 8> parameters;
 
     /// The control flow graph.
     utils::BlockAllocator<Node> nodes;
@@ -213,24 +211,31 @@
 
     /// The set of a local read-write vars that are in scope at any given point in the process.
     /// Includes pointer parameters.
-    std::unordered_set<const sem::Variable*> local_var_decls;
+    utils::Hashset<const sem::Variable*, 8> local_var_decls;
 
     /// The set of partial pointer variables - pointers that point to a subobject (into an array or
     /// struct).
-    std::unordered_set<const sem::Variable*> partial_ptrs;
+    utils::Hashset<const sem::Variable*, 4> partial_ptrs;
 
     /// LoopSwitchInfo tracks information about the value of variables for a control flow construct.
     struct LoopSwitchInfo {
         /// The type of this control flow construct.
         std::string type;
         /// The input values for local variables at the start of this construct.
-        std::unordered_map<const sem::Variable*, Node*> var_in_nodes;
+        utils::Hashmap<const sem::Variable*, Node*, 8> var_in_nodes;
         /// The exit values for local variables at the end of this construct.
-        std::unordered_map<const sem::Variable*, Node*> var_exit_nodes;
+        utils::Hashmap<const sem::Variable*, Node*, 8> var_exit_nodes;
     };
 
-    /// Map from control flow statements to the corresponding LoopSwitchInfo structure.
-    std::unordered_map<const sem::Statement*, LoopSwitchInfo> loop_switch_infos;
+    /// @returns a LoopSwitchInfo for the given statement, allocating the LoopSwitchInfo if this is
+    /// the first call with the given statement.
+    LoopSwitchInfo& LoopSwitchInfoFor(const sem::Statement* stmt) {
+        return *loop_switch_infos.GetOrCreate(stmt,
+                                              [&] { return loop_switch_info_allocator.Create(); });
+    }
+
+    /// Disassociates the LoopSwitchInfo for the given statement.
+    void RemoveLoopSwitchInfoFor(const sem::Statement* stmt) { loop_switch_infos.Remove(stmt); }
 
     /// Create a new node.
     /// @param tag a tag used to identify the node for debugging purposes
@@ -263,7 +268,13 @@
 
   private:
     /// A list of tags that have already been used within the current function.
-    std::unordered_set<std::string> tags_;
+    utils::Hashset<std::string, 8> tags_;
+
+    /// Map from control flow statements to the corresponding LoopSwitchInfo structure.
+    utils::Hashmap<const sem::Statement*, LoopSwitchInfo*, 8> loop_switch_infos;
+
+    /// Allocator of LoopSwitchInfos
+    utils::BlockAllocator<LoopSwitchInfo> loop_switch_info_allocator;
 };
 
 /// UniformityGraph is used to analyze the uniformity requirements and effects of functions in a
@@ -312,7 +323,7 @@
     diag::List& diagnostics_;
 
     /// Map of analyzed function results.
-    std::unordered_map<const ast::Function*, FunctionInfo> functions_;
+    utils::Hashmap<const ast::Function*, FunctionInfo, 8> functions_;
 
     /// The function currently being analyzed.
     FunctionInfo* current_function_;
@@ -329,8 +340,7 @@
     /// @param func the function to process
     /// @returns true if there are no uniformity issues, false otherwise
     bool ProcessFunction(const ast::Function* func) {
-        functions_.emplace(func, FunctionInfo(func, builder_));
-        current_function_ = &functions_.at(func);
+        current_function_ = functions_.Add(func, FunctionInfo(func, builder_)).value;
 
         // Process function body.
         if (func->body) {
@@ -410,7 +420,7 @@
             for (size_t j = 0; j < func->params.Length(); j++) {
                 auto* param_source = sem_.Get<sem::Parameter>(func->params[j]);
                 if (reachable.Contains(current_function_->parameters[j].init_value)) {
-                    current_function_->parameters[i].pointer_param_output_sources.push_back(
+                    current_function_->parameters[i].pointer_param_output_sources.Push(
                         param_source);
                 }
             }
@@ -439,7 +449,7 @@
             },
 
             [&](const ast::BlockStatement* b) {
-                std::unordered_map<const sem::Variable*, Node*> scoped_assignments;
+                utils::Hashmap<const sem::Variable*, Node*, 8> scoped_assignments;
                 {
                     // Push a new scope for variable assignments in the block.
                     current_function_->variables.Push();
@@ -467,18 +477,17 @@
                 }
 
                 // Propagate all variables assignments to the containing scope if the behavior is
-                // either 'Next' or 'Fallthrough'.
+                // 'Next'.
                 auto& behaviors = sem_.Get(b)->Behaviors();
-                if (behaviors.Contains(sem::Behavior::kNext) ||
-                    behaviors.Contains(sem::Behavior::kFallthrough)) {
+                if (behaviors.Contains(sem::Behavior::kNext)) {
                     for (auto var : scoped_assignments) {
-                        current_function_->variables.Set(var.first, var.second);
+                        current_function_->variables.Set(var.key, var.value);
                     }
                 }
 
                 // Remove any variables declared in this scope from the set of in-scope variables.
                 for (auto decl : sem_.Get<sem::BlockStatement>(b)->Decls()) {
-                    current_function_->local_var_decls.erase(decl.value.variable);
+                    current_function_->local_var_decls.Remove(decl.value.variable);
                 }
 
                 return cf;
@@ -489,8 +498,8 @@
                 auto* parent = sem_.Get(b)
                                    ->FindFirstParent<sem::SwitchStatement, sem::LoopStatement,
                                                      sem::ForLoopStatement, sem::WhileStatement>();
-                TINT_ASSERT(Resolver, current_function_->loop_switch_infos.count(parent));
-                auto& info = current_function_->loop_switch_infos.at(parent);
+
+                auto& info = current_function_->LoopSwitchInfoFor(parent);
 
                 // Propagate variable values to the loop/switch exit nodes.
                 for (auto* var : current_function_->local_var_decls) {
@@ -502,7 +511,7 @@
                     }
 
                     // Add an edge from the variable exit node to its value at this point.
-                    auto* exit_node = utils::GetOrCreate(info.var_exit_nodes, var, [&]() {
+                    auto* exit_node = info.var_exit_nodes.GetOrCreate(var, [&]() {
                         auto name = builder_->Symbols().NameFor(var->Declaration()->symbol);
                         return CreateNode(name + "_value_" + info.type + "_exit");
                     });
@@ -526,8 +535,7 @@
 
                 {
                     auto* parent = sem_.Get(b)->FindFirstParent<sem::LoopStatement>();
-                    TINT_ASSERT(Resolver, current_function_->loop_switch_infos.count(parent));
-                    auto& info = current_function_->loop_switch_infos.at(parent);
+                    auto& info = current_function_->LoopSwitchInfoFor(parent);
 
                     // Propagate variable values to the loop exit nodes.
                     for (auto* var : current_function_->local_var_decls) {
@@ -539,7 +547,7 @@
                         }
 
                         // Add an edge from the variable exit node to its value at this point.
-                        auto* exit_node = utils::GetOrCreate(info.var_exit_nodes, var, [&]() {
+                        auto* exit_node = info.var_exit_nodes.GetOrCreate(var, [&]() {
                             auto name = builder_->Symbols().NameFor(var->Declaration()->symbol);
                             return CreateNode(name + "_value_" + info.type + "_exit");
                         });
@@ -580,8 +588,7 @@
                 auto* parent = sem_.Get(c)
                                    ->FindFirstParent<sem::LoopStatement, sem::ForLoopStatement,
                                                      sem::WhileStatement>();
-                TINT_ASSERT(Resolver, current_function_->loop_switch_infos.count(parent));
-                auto& info = current_function_->loop_switch_infos.at(parent);
+                auto& info = current_function_->LoopSwitchInfoFor(parent);
 
                 // Propagate assignments to the loop input nodes.
                 for (auto* var : current_function_->local_var_decls) {
@@ -593,11 +600,11 @@
                     }
 
                     // Add an edge from the variable's loop input node to its value at this point.
-                    TINT_ASSERT(Resolver, info.var_in_nodes.count(var));
-                    auto* in_node = info.var_in_nodes.at(var);
+                    auto** in_node = info.var_in_nodes.Find(var);
+                    TINT_ASSERT(Resolver, in_node != nullptr);
                     auto* out_node = current_function_->variables.Get(var);
-                    if (out_node != in_node) {
-                        in_node->AddEdge(out_node);
+                    if (out_node != *in_node) {
+                        (*in_node)->AddEdge(out_node);
                     }
                 }
                 return cf;
@@ -605,8 +612,6 @@
 
             [&](const ast::DiscardStatement*) { return cf; },
 
-            [&](const ast::FallthroughStatement*) { return cf; },
-
             [&](const ast::ForLoopStatement* f) {
                 auto* sem_loop = sem_.Get(f);
                 auto* cfx = CreateNode("loop_start");
@@ -618,7 +623,7 @@
                 }
                 auto* cf_start = cf_init;
 
-                auto& info = current_function_->loop_switch_infos[sem_loop];
+                auto& info = current_function_->LoopSwitchInfoFor(sem_loop);
                 info.type = "forloop";
 
                 // Create input nodes for any variables declared before this loop.
@@ -626,7 +631,7 @@
                     auto name = builder_->Symbols().NameFor(v->Declaration()->symbol);
                     auto* in_node = CreateNode(name + "_value_forloop_in");
                     in_node->AddEdge(current_function_->variables.Get(v));
-                    info.var_in_nodes[v] = in_node;
+                    info.var_in_nodes.Replace(v, in_node);
                     current_function_->variables.Set(v, in_node);
                 }
 
@@ -640,7 +645,7 @@
 
                     // Propagate assignments to the loop exit nodes.
                     for (auto* var : current_function_->local_var_decls) {
-                        auto* exit_node = utils::GetOrCreate(info.var_exit_nodes, var, [&]() {
+                        auto* exit_node = info.var_exit_nodes.GetOrCreate(var, [&]() {
                             auto name = builder_->Symbols().NameFor(var->Declaration()->symbol);
                             return CreateNode(name + "_value_" + info.type + "_exit");
                         });
@@ -660,19 +665,19 @@
 
                 // Add edges from variable loop input nodes to their values at the end of the loop.
                 for (auto v : info.var_in_nodes) {
-                    auto* in_node = v.second;
-                    auto* out_node = current_function_->variables.Get(v.first);
+                    auto* in_node = v.value;
+                    auto* out_node = current_function_->variables.Get(v.key);
                     if (out_node != in_node) {
                         in_node->AddEdge(out_node);
                     }
                 }
 
                 // Set each variable's exit node as its value in the outer scope.
-                for (auto v : info.var_exit_nodes) {
-                    current_function_->variables.Set(v.first, v.second);
+                for (auto& v : info.var_exit_nodes) {
+                    current_function_->variables.Set(v.key, v.value);
                 }
 
-                current_function_->loop_switch_infos.erase(sem_loop);
+                current_function_->RemoveLoopSwitchInfoFor(sem_loop);
 
                 if (sem_loop->Behaviors() == sem::Behaviors{sem::Behavior::kNext}) {
                     return cf;
@@ -687,7 +692,7 @@
 
                 auto* cf_start = cf;
 
-                auto& info = current_function_->loop_switch_infos[sem_loop];
+                auto& info = current_function_->LoopSwitchInfoFor(sem_loop);
                 info.type = "whileloop";
 
                 // Create input nodes for any variables declared before this loop.
@@ -695,7 +700,7 @@
                     auto name = builder_->Symbols().NameFor(v->Declaration()->symbol);
                     auto* in_node = CreateNode(name + "_value_forloop_in");
                     in_node->AddEdge(current_function_->variables.Get(v));
-                    info.var_in_nodes[v] = in_node;
+                    info.var_in_nodes.Replace(v, in_node);
                     current_function_->variables.Set(v, in_node);
                 }
 
@@ -710,7 +715,7 @@
 
                 // Propagate assignments to the loop exit nodes.
                 for (auto* var : current_function_->local_var_decls) {
-                    auto* exit_node = utils::GetOrCreate(info.var_exit_nodes, var, [&]() {
+                    auto* exit_node = info.var_exit_nodes.GetOrCreate(var, [&]() {
                         auto name = builder_->Symbols().NameFor(var->Declaration()->symbol);
                         return CreateNode(name + "_value_" + info.type + "_exit");
                     });
@@ -721,9 +726,9 @@
                 cfx->AddEdge(cf);
 
                 // Add edges from variable loop input nodes to their values at the end of the loop.
-                for (auto v : info.var_in_nodes) {
-                    auto* in_node = v.second;
-                    auto* out_node = current_function_->variables.Get(v.first);
+                for (auto& v : info.var_in_nodes) {
+                    auto* in_node = v.value;
+                    auto* out_node = current_function_->variables.Get(v.key);
                     if (out_node != in_node) {
                         in_node->AddEdge(out_node);
                     }
@@ -731,10 +736,10 @@
 
                 // Set each variable's exit node as its value in the outer scope.
                 for (auto v : info.var_exit_nodes) {
-                    current_function_->variables.Set(v.first, v.second);
+                    current_function_->variables.Set(v.key, v.value);
                 }
 
-                current_function_->loop_switch_infos.erase(sem_loop);
+                current_function_->RemoveLoopSwitchInfoFor(sem_loop);
 
                 if (sem_loop->Behaviors() == sem::Behaviors{sem::Behavior::kNext}) {
                     return cf;
@@ -752,15 +757,15 @@
                 v->affects_control_flow = true;
                 v->AddEdge(v_cond);
 
-                std::unordered_map<const sem::Variable*, Node*> true_vars;
-                std::unordered_map<const sem::Variable*, Node*> false_vars;
+                utils::Hashmap<const sem::Variable*, Node*, 8> true_vars;
+                utils::Hashmap<const sem::Variable*, Node*, 8> false_vars;
 
                 // Helper to process a statement with a new scope for variable assignments.
                 // Populates `assigned_vars` with new nodes for any variables that are assigned in
                 // this statement.
                 auto process_in_scope =
                     [&](Node* cf_in, const ast::Statement* s,
-                        std::unordered_map<const sem::Variable*, Node*>& assigned_vars) {
+                        utils::Hashmap<const sem::Variable*, Node*, 8>& assigned_vars) {
                         // Push a new scope for variable assignments.
                         current_function_->variables.Push();
 
@@ -790,7 +795,7 @@
                 // Update values for any variables assigned in the if or else blocks.
                 for (auto* var : current_function_->local_var_decls) {
                     // Skip variables not assigned in either block.
-                    if (true_vars.count(var) == 0 && false_vars.count(var) == 0) {
+                    if (!true_vars.Contains(var) && !false_vars.Contains(var)) {
                         continue;
                     }
 
@@ -801,15 +806,15 @@
                     // Add edges to the assigned value or the initial value.
                     // Only add edges if the behavior for that block contains 'Next'.
                     if (true_has_next) {
-                        if (true_vars.count(var)) {
-                            out_node->AddEdge(true_vars.at(var));
+                        if (true_vars.Contains(var)) {
+                            out_node->AddEdge(*true_vars.Find(var));
                         } else {
                             out_node->AddEdge(current_function_->variables.Get(var));
                         }
                     }
                     if (false_has_next) {
-                        if (false_vars.count(var)) {
-                            out_node->AddEdge(false_vars.at(var));
+                        if (false_vars.Contains(var)) {
+                            out_node->AddEdge(*false_vars.Find(var));
                         } else {
                             out_node->AddEdge(current_function_->variables.Get(var));
                         }
@@ -845,7 +850,7 @@
                 auto* sem_loop = sem_.Get(l);
                 auto* cfx = CreateNode("loop_start");
 
-                auto& info = current_function_->loop_switch_infos[sem_loop];
+                auto& info = current_function_->LoopSwitchInfoFor(sem_loop);
                 info.type = "loop";
 
                 // Create input nodes for any variables declared before this loop.
@@ -853,7 +858,7 @@
                     auto name = builder_->Symbols().NameFor(v->Declaration()->symbol);
                     auto* in_node = CreateNode(name + "_value_loop_in", v->Declaration());
                     in_node->AddEdge(current_function_->variables.Get(v));
-                    info.var_in_nodes[v] = in_node;
+                    info.var_in_nodes.Replace(v, in_node);
                     current_function_->variables.Set(v, in_node);
                 }
 
@@ -868,8 +873,8 @@
 
                 // Add edges from variable loop input nodes to their values at the end of the loop.
                 for (auto v : info.var_in_nodes) {
-                    auto* in_node = v.second;
-                    auto* out_node = current_function_->variables.Get(v.first);
+                    auto* in_node = v.value;
+                    auto* out_node = current_function_->variables.Get(v.key);
                     if (out_node != in_node) {
                         in_node->AddEdge(out_node);
                     }
@@ -877,10 +882,10 @@
 
                 // Set each variable's exit node as its value in the outer scope.
                 for (auto v : info.var_exit_nodes) {
-                    current_function_->variables.Set(v.first, v.second);
+                    current_function_->variables.Set(v.key, v.value);
                 }
 
-                current_function_->loop_switch_infos.erase(sem_loop);
+                current_function_->RemoveLoopSwitchInfoFor(sem_loop);
 
                 if (sem_loop->Behaviors() == sem::Behaviors{sem::Behavior::kNext}) {
                     return cf;
@@ -925,56 +930,44 @@
                     cf_end = CreateNode("switch_CFend");
                 }
 
-                auto& info = current_function_->loop_switch_infos[sem_switch];
+                auto& info = current_function_->LoopSwitchInfoFor(sem_switch);
                 info.type = "switch";
 
                 auto* cf_n = v;
-                bool previous_case_has_fallthrough = false;
                 for (auto* c : s->body) {
                     auto* sem_case = sem_.Get(c);
 
-                    if (previous_case_has_fallthrough) {
-                        cf_n = ProcessStatement(cf_n, c->body);
-                    } else {
-                        current_function_->variables.Push();
-                        cf_n = ProcessStatement(v, c->body);
-                    }
+                    current_function_->variables.Push();
+                    cf_n = ProcessStatement(v, c->body);
 
                     if (cf_end) {
                         cf_end->AddEdge(cf_n);
                     }
 
-                    bool has_fallthrough =
-                        sem_case->Behaviors().Contains(sem::Behavior::kFallthrough);
-                    if (!has_fallthrough) {
-                        if (sem_case->Behaviors().Contains(sem::Behavior::kNext)) {
-                            // Propagate variable values to the switch exit nodes.
-                            for (auto* var : current_function_->local_var_decls) {
-                                // Skip variables that were declared inside the switch.
-                                if (auto* lv = var->As<sem::LocalVariable>();
-                                    lv && lv->Statement()->FindFirstParent(
-                                              [&](auto* st) { return st == sem_switch; })) {
-                                    continue;
-                                }
-
-                                // Add an edge from the variable exit node to its new value.
-                                auto* exit_node =
-                                    utils::GetOrCreate(info.var_exit_nodes, var, [&]() {
-                                        auto name =
-                                            builder_->Symbols().NameFor(var->Declaration()->symbol);
-                                        return CreateNode(name + "_value_" + info.type + "_exit");
-                                    });
-                                exit_node->AddEdge(current_function_->variables.Get(var));
+                    if (sem_case->Behaviors().Contains(sem::Behavior::kNext)) {
+                        // Propagate variable values to the switch exit nodes.
+                        for (auto* var : current_function_->local_var_decls) {
+                            // Skip variables that were declared inside the switch.
+                            if (auto* lv = var->As<sem::LocalVariable>();
+                                lv && lv->Statement()->FindFirstParent(
+                                          [&](auto* st) { return st == sem_switch; })) {
+                                continue;
                             }
+
+                            // Add an edge from the variable exit node to its new value.
+                            auto* exit_node = info.var_exit_nodes.GetOrCreate(var, [&]() {
+                                auto name = builder_->Symbols().NameFor(var->Declaration()->symbol);
+                                return CreateNode(name + "_value_" + info.type + "_exit");
+                            });
+                            exit_node->AddEdge(current_function_->variables.Get(var));
                         }
-                        current_function_->variables.Pop();
                     }
-                    previous_case_has_fallthrough = has_fallthrough;
+                    current_function_->variables.Pop();
                 }
 
                 // Update nodes for any variables assigned in the switch statement.
                 for (auto var : info.var_exit_nodes) {
-                    current_function_->variables.Set(var.first, var.second);
+                    current_function_->variables.Set(var.key, var.value);
                 }
 
                 return cf_end ? cf_end : cf;
@@ -995,7 +988,7 @@
                             auto* e = UnwrapIndirectAndAddressOfChain(unary_init);
                             if (e->IsAnyOf<ast::IndexAccessorExpression,
                                            ast::MemberAccessorExpression>()) {
-                                current_function_->partial_ptrs.insert(sem_var);
+                                current_function_->partial_ptrs.Add(sem_var);
                             }
                         }
                     }
@@ -1005,7 +998,7 @@
                 current_function_->variables.Set(sem_var, node);
 
                 if (decl->variable->Is<ast::Var>()) {
-                    current_function_->local_var_decls.insert(
+                    current_function_->local_var_decls.Add(
                         sem_.Get<sem::LocalVariable>(decl->variable));
                 }
 
@@ -1157,8 +1150,8 @@
                 if (u->op == ast::UnaryOp::kIndirection) {
                     // Cut the analysis short, since we only need to know the originating variable
                     // which is being accessed.
-                    auto* source_var = sem_.Get(u)->SourceVariable();
-                    auto* value = current_function_->variables.Get(source_var);
+                    auto* root_ident = sem_.Get(u)->RootIdentifier();
+                    auto* value = current_function_->variables.Get(root_ident);
                     if (!value) {
                         value = cf;
                     }
@@ -1183,10 +1176,10 @@
         // To determine if we're dereferencing a partial pointer, unwrap *&
         // chains; if the final expression is an identifier, see if it's a
         // partial pointer. If it's not an identifier, then it must be an
-        // index/acessor expression, and thus a partial pointer.
+        // index/accessor expression, and thus a partial pointer.
         auto* e = UnwrapIndirectAndAddressOfChain(u);
         if (auto* var_user = sem_.Get<sem::VariableUser>(e)) {
-            if (current_function_->partial_ptrs.count(var_user->Variable())) {
+            if (current_function_->partial_ptrs.Contains(var_user->Variable())) {
                 return true;
             }
         } else {
@@ -1251,10 +1244,10 @@
                 if (u->op == ast::UnaryOp::kIndirection) {
                     // Cut the analysis short, since we only need to know the originating variable
                     // that is being written to.
-                    auto* source_var = sem_.Get(u)->SourceVariable();
-                    auto name = builder_->Symbols().NameFor(source_var->Declaration()->symbol);
+                    auto* root_ident = sem_.Get(u)->RootIdentifier();
+                    auto name = builder_->Symbols().NameFor(root_ident->Declaration()->symbol);
                     auto* deref = CreateNode(name + "_deref");
-                    auto* old_value = current_function_->variables.Set(source_var, deref);
+                    auto* old_value = current_function_->variables.Set(root_ident, deref);
 
                     if (old_value) {
                         // If derefercing a partial reference or partial pointer, we link back to
@@ -1290,7 +1283,7 @@
 
         // Process call arguments
         Node* cf_last_arg = cf;
-        std::vector<Node*> args;
+        utils::Vector<Node*, 8> args;
         for (size_t i = 0; i < call->args.Length(); i++) {
             auto [cf_i, arg_i] = ProcessExpression(cf_last_arg, call->args[i]);
 
@@ -1303,7 +1296,7 @@
             arg_node->AddEdge(arg_i);
 
             cf_last_arg = cf_i;
-            args.push_back(arg_node);
+            args.Push(arg_node);
         }
 
         // Note: This is an additional node that isn't described in the specification, for the
@@ -1341,11 +1334,11 @@
             [&](const sem::Function* func) {
                 // We must have already analyzed the user-defined function since we process
                 // functions in dependency order.
-                TINT_ASSERT(Resolver, functions_.count(func->Declaration()));
-                auto& info = functions_.at(func->Declaration());
-                callsite_tag = info.callsite_tag;
-                function_tag = info.function_tag;
-                func_info = &info;
+                auto* info = functions_.Find(func->Declaration());
+                TINT_ASSERT(Resolver, info != nullptr);
+                callsite_tag = info->callsite_tag;
+                function_tag = info->function_tag;
+                func_info = info;
             },
             [&](const sem::TypeInitializer*) {
                 callsite_tag = CallSiteNoRestriction;
@@ -1371,7 +1364,7 @@
         result->AddEdge(cf_after);
 
         // For each argument, add edges based on parameter tags.
-        for (size_t i = 0; i < args.size(); i++) {
+        for (size_t i = 0; i < args.Length(); i++) {
             if (func_info) {
                 switch (func_info->parameters[i].tag) {
                     case ParameterRequiredToBeUniform:
@@ -1405,9 +1398,9 @@
                     }
 
                     // Update the current stored value for this pointer argument.
-                    auto* source_var = sem_arg->SourceVariable();
-                    TINT_ASSERT(Resolver, source_var);
-                    current_function_->variables.Set(source_var, ptr_result);
+                    auto* root_ident = sem_arg->RootIdentifier();
+                    TINT_ASSERT(Resolver, root_ident);
+                    current_function_->variables.Set(root_ident, ptr_result);
                 }
             } else {
                 // All builtin function parameters are RequiredToBeUniformForReturnValue, as are
@@ -1429,11 +1422,11 @@
     /// @param source the starting node
     /// @param reachable the set of reachable nodes to populate, if required
     void Traverse(Node* source, utils::UniqueVector<Node*, 4>* reachable = nullptr) {
-        std::vector<Node*> to_visit{source};
+        utils::Vector<Node*, 8> to_visit{source};
 
-        while (!to_visit.empty()) {
-            auto* node = to_visit.back();
-            to_visit.pop_back();
+        while (!to_visit.IsEmpty()) {
+            auto* node = to_visit.Back();
+            to_visit.Pop();
 
             if (reachable) {
                 reachable->Add(node);
@@ -1441,7 +1434,7 @@
             for (auto* to : node->edges) {
                 if (to->visited_from == nullptr) {
                     to->visited_from = node;
-                    to_visit.push_back(to);
+                    to_visit.Push(to);
                 }
             }
         }
@@ -1473,8 +1466,8 @@
         } else if (auto* user = target->As<sem::Function>()) {
             // This is a call to a user-defined function, so inspect the functions called by that
             // function and look for one whose node has an edge from the RequiredToBeUniform node.
-            auto& target_info = functions_.at(user->Declaration());
-            for (auto* call_node : target_info.required_to_be_uniform->edges) {
+            auto* target_info = functions_.Find(user->Declaration());
+            for (auto* call_node : target_info->required_to_be_uniform->edges) {
                 if (call_node->type == Node::kRegular) {
                     auto* child_call = call_node->ast->As<ast::CallExpression>();
                     return FindBuiltinThatRequiresUniformity(child_call);
@@ -1643,9 +1636,9 @@
             // If this is a call to a user-defined function, add a note to show the reason that the
             // parameter is required to be uniform.
             if (auto* user = target->As<sem::Function>()) {
-                auto& next_function = functions_.at(user->Declaration());
-                Node* next_cause = next_function.parameters[cause->arg_index].init_value;
-                MakeError(next_function, next_cause, true);
+                auto* next_function = functions_.Find(user->Declaration());
+                Node* next_cause = next_function->parameters[cause->arg_index].init_value;
+                MakeError(*next_function, next_cause, true);
             }
         } else {
             // The requirement was on a function callsite.
diff --git a/src/tint/resolver/uniformity_test.cc b/src/tint/resolver/uniformity_test.cc
index fbaeff9..fd632df 100644
--- a/src/tint/resolver/uniformity_test.cc
+++ b/src/tint/resolver/uniformity_test.cc
@@ -736,7 +736,6 @@
     kBreak,
     kContinue,
     kReturn,
-    kDiscard,
 };
 enum Condition {
     kNone,
@@ -754,8 +753,6 @@
             return "continue";
         case kReturn:
             return "return";
-        case kDiscard:
-            return "discard";
     }
     return "";
 }
@@ -790,7 +787,7 @@
 
 INSTANTIATE_TEST_SUITE_P(UniformityAnalysisTest,
                          LoopTest,
-                         ::testing::Combine(::testing::Range<int>(0, kDiscard + 1),
+                         ::testing::Combine(::testing::Range<int>(0, kReturn + 1),
                                             ::testing::Range<int>(0, kNonUniform + 1)),
                          [](const ::testing::TestParamInfo<LoopTestParams>& p) {
                              ControlFlowInterrupt interrupt =
@@ -1025,7 +1022,7 @@
 
 INSTANTIATE_TEST_SUITE_P(UniformityAnalysisTest,
                          LoopDeadCodeTest,
-                         ::testing::Range<int>(0, kDiscard + 1),
+                         ::testing::Range<int>(0, kReturn + 1),
                          [](const ::testing::TestParamInfo<LoopDeadCodeTest::ParamType>& p) {
                              return ToStr(static_cast<ControlFlowInterrupt>(p.param));
                          });
@@ -2890,36 +2887,6 @@
 )");
 }
 
-TEST_F(UniformityAnalysisTest, IfElse_NonUniformDiscard_NoReconverge) {
-    // If statements should not reconverge after non-uniform discards.
-    std::string src = R"(
-@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
-
-fn foo() {
-  if (non_uniform == 42) {
-    discard;
-  } else {
-  }
-  workgroupBarrier();
-}
-)";
-
-    RunTest(src, false);
-    EXPECT_EQ(error_,
-              R"(test:9:3 warning: 'workgroupBarrier' must only be called from uniform control flow
-  workgroupBarrier();
-  ^^^^^^^^^^^^^^^^
-
-test:5:3 note: control flow depends on non-uniform value
-  if (non_uniform == 42) {
-  ^^
-
-test:5:7 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
-  if (non_uniform == 42) {
-      ^^^^^^^^^^^
-)");
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 /// Switch statement tests.
 ////////////////////////////////////////////////////////////////////////////////
@@ -3046,49 +3013,6 @@
     RunTest(src, true);
 }
 
-TEST_F(UniformityAnalysisTest, Switch_NonUniformBreakInDifferentCase_Fallthrough) {
-    std::string src = R"(
-@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
-@group(0) @binding(0) var<uniform> condition : i32;
-
-fn foo() {
-  switch (condition) {
-    case 0: {
-      if (non_uniform == 42) {
-        break;
-      }
-      fallthrough;
-    }
-    case 42: {
-      workgroupBarrier();
-    }
-    default: {
-    }
-  }
-}
-)";
-
-    RunTest(src, false);
-    EXPECT_EQ(
-        error_,
-        R"(test:11:7 warning: use of deprecated language feature: fallthrough is set to be removed from WGSL. Case can accept multiple selectors if the existing case bodies are empty. (e.g. `case 1, 2, 3:`) `default` is a valid case selector value. (e.g. `case 1, default:`)
-      fallthrough;
-      ^^^^^^^^^^^
-
-test:14:7 warning: 'workgroupBarrier' must only be called from uniform control flow
-      workgroupBarrier();
-      ^^^^^^^^^^^^^^^^
-
-test:8:7 note: control flow depends on non-uniform value
-      if (non_uniform == 42) {
-      ^^
-
-test:8:11 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
-      if (non_uniform == 42) {
-          ^^^^^^^^^^^
-)");
-}
-
 TEST_F(UniformityAnalysisTest, Switch_VarBecomesNonUniformInDifferentCase_WithBreak) {
     std::string src = R"(
 @group(0) @binding(0) var<storage, read_write> non_uniform : i32;
@@ -3115,50 +3039,6 @@
     RunTest(src, true);
 }
 
-TEST_F(UniformityAnalysisTest, Switch_VarBecomesNonUniformInDifferentCase_WithFallthrough) {
-    std::string src = R"(
-@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
-@group(0) @binding(0) var<uniform> condition : i32;
-
-fn foo() {
-  var x = 0;
-  switch (condition) {
-    case 0: {
-      x = non_uniform;
-      fallthrough;
-    }
-    case 42: {
-      if (x == 0) {
-        workgroupBarrier();
-      }
-    }
-    default: {
-    }
-  }
-}
-)";
-
-    RunTest(src, false);
-    EXPECT_EQ(
-        error_,
-        R"(test:10:7 warning: use of deprecated language feature: fallthrough is set to be removed from WGSL. Case can accept multiple selectors if the existing case bodies are empty. (e.g. `case 1, 2, 3:`) `default` is a valid case selector value. (e.g. `case 1, default:`)
-      fallthrough;
-      ^^^^^^^^^^^
-
-test:14:9 warning: 'workgroupBarrier' must only be called from uniform control flow
-        workgroupBarrier();
-        ^^^^^^^^^^^^^^^^
-
-test:13:7 note: control flow depends on non-uniform value
-      if (x == 0) {
-      ^^
-
-test:9:11 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
-      x = non_uniform;
-          ^^^^^^^^^^^
-)");
-}
-
 TEST_F(UniformityAnalysisTest, Switch_VarBecomesUniformInDifferentCase_WithBreak) {
     std::string src = R"(
 @group(0) @binding(0) var<storage, read_write> non_uniform : i32;
@@ -6749,6 +6629,22 @@
 /// Miscellaneous statement and expression tests.
 ////////////////////////////////////////////////////////////////////////////////
 
+TEST_F(UniformityAnalysisTest, NonUniformDiscard) {
+    // Non-uniform discard statements should not cause uniformity issues.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  if (non_uniform == 42) {
+    discard;
+  }
+  _ = dpdx(1.0);
+}
+)";
+
+    RunTest(src, true);
+}
+
 TEST_F(UniformityAnalysisTest, FunctionReconvergesOnExit) {
     // Call a function that has returns during non-uniform control flow, and test that the analysis
     // reconverges when returning to the caller.
@@ -6775,29 +6671,6 @@
     RunTest(src, true);
 }
 
-TEST_F(UniformityAnalysisTest, FunctionRequiresUniformFlowAndCausesNonUniformFlow) {
-    // Test that a function that requires uniform flow and then causes non-uniform flow can be
-    // called without error.
-    std::string src = R"(
-@group(0) @binding(0) var<storage, read_write> non_uniform_global : i32;
-
-fn foo() {
-  _ = dpdx(0.5);
-
-  if (non_uniform_global == 0) {
-    discard;
-  }
-}
-
-@fragment
-fn main() {
-  foo();
-}
-)";
-
-    RunTest(src, true);
-}
-
 TEST_F(UniformityAnalysisTest, TypeInitializer) {
     std::string src = R"(
 @group(0) @binding(0) var<storage, read_write> non_uniform_global : i32;
@@ -7031,22 +6904,6 @@
     RunTest(src, true);
 }
 
-TEST_F(UniformityAnalysisTest, DeadCode_AfterDiscard) {
-    // Dead code after a discard statement shouldn't cause uniformity errors.
-    std::string src = R"(
-@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
-
-fn foo() {
-  discard;
-  if (non_uniform == 42) {
-    workgroupBarrier();
-  }
-}
-)";
-
-    RunTest(src, true);
-}
-
 TEST_F(UniformityAnalysisTest, ArrayLength) {
     std::string src = R"(
 @group(0) @binding(0) var<storage, read_write> arr : array<f32>;
diff --git a/src/tint/resolver/validation_test.cc b/src/tint/resolver/validation_test.cc
index 34ddd90..2e41bf1 100644
--- a/src/tint/resolver/validation_test.cc
+++ b/src/tint/resolver/validation_test.cc
@@ -766,69 +766,17 @@
     // loop  {
     //   continuing {
     //     discard;
+    //     breakif true;
     //   }
     // }
 
-    WrapInFunction(Loop(  // loop
-        Block(),          //   loop block
-        Block(            //   loop continuing block
-            Discard(Source{{12, 34}}))));
+    Func("my_func", utils::Empty, ty.void_(),
+         utils::Vector{Loop(  // loop
+             Block(),         //   loop block
+             Block(           //   loop continuing block
+                 Discard(Source{{12, 34}}), BreakIf(true)))});
 
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              R"(12:34 error: continuing blocks must not contain a discard statement)");
-}
-
-TEST_F(ResolverTest, Stmt_Loop_DiscardInContinuing_Indirect) {
-    // loop {
-    //   if (false) { break; }
-    //   continuing {
-    //     loop { discard; }
-    //   }
-    // }
-
-    WrapInFunction(Loop(                   // outer loop
-        Block(If(false, Block(Break()))),  //   outer loop block
-        Block(Source{{56, 78}},            //   outer loop continuing block
-              Loop(                        //     inner loop
-                  Block(                   //       inner loop block
-                      Discard(Source{{12, 34}}))))));
-
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              R"(12:34 error: continuing blocks must not contain a discard statement
-56:78 note: see continuing block here)");
-}
-
-TEST_F(ResolverTest, Stmt_Loop_DiscardInContinuing_Indirect_ViaCall) {
-    // fn MayDiscard() { if (true) { discard; } }
-    // fn F() { MayDiscard(); }
-    // loop {
-    //   continuing {
-    //     loop { F(); }
-    //   }
-    // }
-
-    Func("MayDiscard", utils::Empty, ty.void_(),
-         utils::Vector{
-             If(true, Block(Discard())),
-         });
-    Func("SomeFunc", utils::Empty, ty.void_(),
-         utils::Vector{
-             CallStmt(Call("MayDiscard")),
-         });
-
-    WrapInFunction(Loop(         // outer loop
-        Block(),                 //   outer loop block
-        Block(Source{{56, 78}},  //   outer loop continuing block
-              Loop(              //     inner loop
-                  Block(         //       inner loop block
-                      CallStmt(Call(Source{{12, 34}}, "SomeFunc")))))));
-
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              R"(12:34 error: cannot call a function that may discard inside a continuing block
-56:78 note: see continuing block here)");
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
 TEST_F(ResolverTest, Stmt_Loop_ContinueInContinuing_Direct) {
@@ -901,7 +849,8 @@
     WrapInFunction(loop_stmt);
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), R"(12:23 error: break-if must be last statement in a continuing block
+    EXPECT_EQ(r()->error(),
+              R"(12:23 error: break-if must be the last statement in a continuing block
 10:9 note: see continuing block here)");
 }
 
@@ -919,7 +868,8 @@
     WrapInFunction(loop_stmt);
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), R"(12:23 error: break-if must be last statement in a continuing block
+    EXPECT_EQ(r()->error(),
+              R"(12:23 error: break-if must be the last statement in a continuing block
 10:9 note: see continuing block here)");
 }
 
@@ -973,55 +923,11 @@
     //   break;
     // }
 
-    WrapInFunction(For(nullptr, nullptr, Discard(Source{{12, 34}}),  //
-                       Block(Break())));
+    Func("my_func", utils::Empty, ty.void_(),
+         utils::Vector{For(nullptr, nullptr, Discard(Source{{12, 34}}),  //
+                           Block(Break()))});
 
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              R"(12:34 error: continuing blocks must not contain a discard statement)");
-}
-
-TEST_F(ResolverTest, Stmt_ForLoop_DiscardInContinuing_Indirect) {
-    // for(;; loop { discard }) {
-    //   break;
-    // }
-
-    WrapInFunction(For(nullptr, nullptr,
-                       Loop(Source{{56, 78}},                   //
-                            Block(Discard(Source{{12, 34}}))),  //
-                       Block(Break())));
-
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              R"(12:34 error: continuing blocks must not contain a discard statement
-56:78 note: see continuing block here)");
-}
-
-TEST_F(ResolverTest, Stmt_ForLoop_DiscardInContinuing_Indirect_ViaCall) {
-    // fn MayDiscard() { if (true) { discard; } }
-    // fn F() { MayDiscard(); }
-    // for(;; loop { F() }) {
-    //   break;
-    // }
-
-    Func("MayDiscard", utils::Empty, ty.void_(),
-         utils::Vector{
-             If(true, Block(Discard())),
-         });
-    Func("F", utils::Empty, ty.void_(),
-         utils::Vector{
-             CallStmt(Call("MayDiscard")),
-         });
-
-    WrapInFunction(For(nullptr, nullptr,
-                       Loop(Source{{56, 78}},                               //
-                            Block(CallStmt(Call(Source{{12, 34}}, "F")))),  //
-                       Block(Break())));
-
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              R"(12:34 error: cannot call a function that may discard inside a continuing block
-56:78 note: see continuing block here)");
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
 TEST_F(ResolverTest, Stmt_ForLoop_ContinueInContinuing_Direct) {
@@ -1123,10 +1029,10 @@
                                                   //   }
                                                   // }
     WrapInFunction(Loop(Block(), cont));
-    EXPECT_TRUE(r()->Resolve()) << r()->error();
+    EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 warning: use of deprecated language feature: `break` must not be used to exit "
-              "from a continuing block. Use `break-if` instead.");
+              "12:34 error: `break` must not be used to exit from a continuing block. "
+              "Use `break-if` instead.");
 }
 
 TEST_F(ResolverValidationTest, Stmt_BreakInIfElseInContinuing) {
@@ -1137,10 +1043,10 @@
                                              //   }
                                              // }
     WrapInFunction(Loop(Block(), cont));
-    EXPECT_TRUE(r()->Resolve()) << r()->error();
+    EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 warning: use of deprecated language feature: `break` must not be used to exit "
-              "from a continuing block. Use `break-if` instead.");
+              "12:34 error: `break` must not be used to exit from a continuing block. "
+              "Use `break-if` instead.");
 }
 
 TEST_F(ResolverValidationTest, Stmt_BreakInContinuing) {
@@ -1150,12 +1056,8 @@
     WrapInFunction(Loop(Block(), cont));
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 warning: use of deprecated language feature: `break` must not be used to exit "
-              "from a continuing block. Use `break-if` instead.\n"
-              "12:34 error: break statement in a continuing block must be the single "
-              "statement of an if statement's true or false block, and that if "
-              "statement must be the last statement of the continuing block\n"
-              "12:34 note: break statement is not directly in if statement block");
+              "12:34 error: `break` must not be used to exit from a continuing block. "
+              "Use `break-if` instead.");
 }
 
 TEST_F(ResolverValidationTest, Stmt_BreakInIfInIfInContinuing) {
@@ -1169,13 +1071,8 @@
     WrapInFunction(Loop(Block(), cont));
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 warning: use of deprecated language feature: `break` must not be used to exit "
-              "from a continuing block. Use `break-if` instead.\n"
-              "12:34 error: break statement in a continuing block must be the single "
-              "statement of an if statement's true or false block, and that if "
-              "statement must be the last statement of the continuing block\n"
-              "56:78 note: if statement containing break statement is not directly in "
-              "continuing block");
+              "12:34 error: `break` must not be used to exit from a continuing block. "
+              "Use `break-if` instead.");
 }
 
 TEST_F(ResolverValidationTest, Stmt_BreakInIfTrueMultipleStmtsInContinuing) {
@@ -1188,12 +1085,8 @@
     WrapInFunction(Loop(Block(), cont));
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 warning: use of deprecated language feature: `break` must not be used to exit "
-              "from a continuing block. Use `break-if` instead.\n"
-              "12:34 error: break statement in a continuing block must be the single "
-              "statement of an if statement's true or false block, and that if "
-              "statement must be the last statement of the continuing block\n"
-              "56:78 note: if statement block contains multiple statements");
+              "12:34 error: `break` must not be used to exit from a continuing block. "
+              "Use `break-if` instead.");
 }
 
 TEST_F(ResolverValidationTest, Stmt_BreakInIfElseMultipleStmtsInContinuing) {
@@ -1207,12 +1100,8 @@
     WrapInFunction(Loop(Block(), cont));
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 warning: use of deprecated language feature: `break` must not be used to exit "
-              "from a continuing block. Use `break-if` instead.\n"
-              "12:34 error: break statement in a continuing block must be the single "
-              "statement of an if statement's true or false block, and that if "
-              "statement must be the last statement of the continuing block\n"
-              "56:78 note: if statement block contains multiple statements");
+              "12:34 error: `break` must not be used to exit from a continuing block. "
+              "Use `break-if` instead.");
 }
 
 TEST_F(ResolverValidationTest, Stmt_BreakInIfElseIfInContinuing) {
@@ -1225,12 +1114,8 @@
     WrapInFunction(Loop(Block(), cont));
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 warning: use of deprecated language feature: `break` must not be used to exit "
-              "from a continuing block. Use `break-if` instead.\n"
-              "12:34 error: break statement in a continuing block must be the single "
-              "statement of an if statement's true or false block, and that if "
-              "statement must be the last statement of the continuing block\n"
-              "56:78 note: else has condition");
+              "12:34 error: `break` must not be used to exit from a continuing block. "
+              "Use `break-if` instead.");
 }
 
 TEST_F(ResolverValidationTest, Stmt_BreakInIfNonEmptyElseInContinuing) {
@@ -1244,12 +1129,8 @@
     WrapInFunction(Loop(Block(), cont));
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 warning: use of deprecated language feature: `break` must not be used to exit "
-              "from a continuing block. Use `break-if` instead.\n"
-              "12:34 error: break statement in a continuing block must be the single "
-              "statement of an if statement's true or false block, and that if "
-              "statement must be the last statement of the continuing block\n"
-              "56:78 note: non-empty false block");
+              "12:34 error: `break` must not be used to exit from a continuing block. "
+              "Use `break-if` instead.");
 }
 
 TEST_F(ResolverValidationTest, Stmt_BreakInIfElseNonEmptyTrueInContinuing) {
@@ -1263,12 +1144,8 @@
     WrapInFunction(Loop(Block(), cont));
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 warning: use of deprecated language feature: `break` must not be used to exit "
-              "from a continuing block. Use `break-if` instead.\n"
-              "12:34 error: break statement in a continuing block must be the single "
-              "statement of an if statement's true or false block, and that if "
-              "statement must be the last statement of the continuing block\n"
-              "56:78 note: non-empty true block");
+              "12:34 error: `break` must not be used to exit from a continuing block. "
+              "Use `break-if` instead.");
 }
 
 TEST_F(ResolverValidationTest, Stmt_BreakInIfInContinuingNotLast) {
@@ -1281,13 +1158,8 @@
     WrapInFunction(Loop(Block(), cont));
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 warning: use of deprecated language feature: `break` must not be used to exit "
-              "from a continuing block. Use `break-if` instead.\n"
-              "12:34 error: break statement in a continuing block must be the single "
-              "statement of an if statement's true or false block, and that if "
-              "statement must be the last statement of the continuing block\n"
-              "56:78 note: if statement containing break statement is not the last "
-              "statement of the continuing block");
+              "12:34 error: `break` must not be used to exit from a continuing block. "
+              "Use `break-if` instead.");
 }
 
 TEST_F(ResolverValidationTest, Stmt_BreakNotInLoopOrSwitch) {
diff --git a/src/tint/resolver/validator.cc b/src/tint/resolver/validator.cc
index f96af05..db93bbe 100644
--- a/src/tint/resolver/validator.cc
+++ b/src/tint/resolver/validator.cc
@@ -28,7 +28,6 @@
 #include "src/tint/ast/depth_texture.h"
 #include "src/tint/ast/disable_validation_attribute.h"
 #include "src/tint/ast/discard_statement.h"
-#include "src/tint/ast/fallthrough_statement.h"
 #include "src/tint/ast/for_loop_statement.h"
 #include "src/tint/ast/id_attribute.h"
 #include "src/tint/ast/if_statement.h"
@@ -531,9 +530,13 @@
     }
 
     if (auto* str = var->Type()->UnwrapRef()->As<sem::Struct>()) {
-        if (!AddressSpaceLayout(str, var->AddressSpace(), str->Declaration()->source, layouts)) {
-            AddNote("see declaration of variable", var->Declaration()->source);
-            return false;
+        // Check the structure has a declaration. Builtins like modf() and frexp() return untypeable
+        // structures, and so they have no declaration. Just skip validation for these.
+        if (auto* str_decl = str->Declaration()) {
+            if (!AddressSpaceLayout(str, var->AddressSpace(), str_decl->source, layouts)) {
+                AddNote("see declaration of variable", var->Declaration()->source);
+                return false;
+            }
         }
     } else {
         Source source = var->Declaration()->source;
@@ -580,8 +583,8 @@
 
 bool Validator::GlobalVariable(
     const sem::GlobalVariable* global,
-    const std::unordered_map<OverrideId, const sem::Variable*>& override_ids,
-    const std::unordered_map<const sem::Type*, const Source&>& atomic_composite_info) const {
+    const utils::Hashmap<OverrideId, const sem::Variable*, 8>& override_ids,
+    const utils::Hashmap<const sem::Type*, const Source*, 8>& atomic_composite_info) const {
     auto* decl = global->Declaration();
     if (global->AddressSpace() != ast::AddressSpace::kWorkgroup &&
         IsArrayWithOverrideCount(global->Type())) {
@@ -702,7 +705,7 @@
 // buffer variables with a read_write access mode.
 bool Validator::AtomicVariable(
     const sem::Variable* var,
-    std::unordered_map<const sem::Type*, const Source&> atomic_composite_info) const {
+    const utils::Hashmap<const sem::Type*, const Source*, 8>& atomic_composite_info) const {
     auto address_space = var->AddressSpace();
     auto* decl = var->Declaration();
     auto access = var->Access();
@@ -716,14 +719,13 @@
             return false;
         }
     } else if (type->IsAnyOf<sem::Struct, sem::Array>()) {
-        auto found = atomic_composite_info.find(type);
-        if (found != atomic_composite_info.end()) {
+        if (auto* found = atomic_composite_info.Find(type)) {
             if (address_space != ast::AddressSpace::kStorage &&
                 address_space != ast::AddressSpace::kWorkgroup) {
                 AddError("atomic variables must have <storage> or <workgroup> address space",
                          source);
                 AddNote("atomic sub-type of '" + sem_.TypeNameOf(type) + "' is declared here",
-                        found->second);
+                        **found);
                 return false;
             } else if (address_space == ast::AddressSpace::kStorage &&
                        access != ast::Access::kReadWrite) {
@@ -732,7 +734,7 @@
                     "access mode",
                     source);
                 AddNote("atomic sub-type of '" + sem_.TypeNameOf(type) + "' is declared here",
-                        found->second);
+                        **found);
                 return false;
             }
         }
@@ -783,7 +785,7 @@
 
 bool Validator::Override(
     const sem::GlobalVariable* v,
-    const std::unordered_map<OverrideId, const sem::Variable*>& override_ids) const {
+    const utils::Hashmap<OverrideId, const sem::Variable*, 8>& override_ids) const {
     auto* decl = v->Declaration();
     auto* storage_ty = v->Type()->UnwrapRef();
 
@@ -796,12 +798,12 @@
     for (auto* attr : decl->attributes) {
         if (attr->Is<ast::IdAttribute>()) {
             auto id = v->OverrideId();
-            if (auto it = override_ids.find(id); it != override_ids.end() && it->second != v) {
+            if (auto* var = override_ids.Find(id); var && *var != v) {
                 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)
-                            ->source);
+                AddNote(
+                    "a override with an ID of " + std::to_string(id.value) +
+                        " was previously declared here:",
+                    ast::GetAttribute<ast::IdAttribute>((*var)->Declaration()->attributes)->source);
                 return false;
             }
         } else {
@@ -853,8 +855,7 @@
     if (auto* ref = var->Type()->As<sem::Pointer>()) {
         auto address_space = ref->AddressSpace();
         if (!(address_space == ast::AddressSpace::kFunction ||
-              address_space == ast::AddressSpace::kPrivate ||
-              address_space == ast::AddressSpace::kWorkgroup) &&
+              address_space == ast::AddressSpace::kPrivate) &&
             IsValidationEnabled(decl->attributes, ast::DisabledValidation::kIgnoreAddressSpace)) {
             std::stringstream ss;
             ss << "function parameter of pointer type cannot be in '" << address_space
@@ -1076,12 +1077,8 @@
     }
 
     // https://www.w3.org/TR/WGSL/#behaviors-rules
-    // a function behavior is always one of {}, {Next}, {Discard}, or
-    // {Next, Discard}.
-    if (func->Behaviors() != sem::Behaviors{} &&  // NOLINT: bad warning
-        func->Behaviors() != sem::Behavior::kNext && func->Behaviors() != sem::Behavior::kDiscard &&
-        func->Behaviors() != sem::Behaviors{sem::Behavior::kNext,  //
-                                            sem::Behavior::kDiscard}) {
+    // a function behavior is always one of {}, or {Next}.
+    if (func->Behaviors() != sem::Behaviors{} && func->Behaviors() != sem::Behavior::kNext) {
         auto name = symbols_.NameFor(decl->symbol);
         TINT_ICE(Resolver, diagnostics_)
             << "function '" << name << "' behaviors are: " << func->Behaviors();
@@ -1098,8 +1095,8 @@
     // order to catch conflicts.
     // TODO(jrprice): This state could be stored in sem::Function instead, and then passed to
     // sem::Function since it would be useful there too.
-    std::unordered_set<ast::BuiltinValue> builtins;
-    std::unordered_set<uint32_t> locations;
+    utils::Hashset<ast::BuiltinValue, 4> builtins;
+    utils::Hashset<uint32_t, 8> locations;
     enum class ParamOrRetType {
         kParameter,
         kReturnType,
@@ -1135,7 +1132,7 @@
                 }
                 pipeline_io_attribute = attr;
 
-                if (builtins.count(builtin->builtin)) {
+                if (builtins.Contains(builtin->builtin)) {
                     AddError(attr_to_str(builtin) +
                                  " attribute appears multiple times as pipeline " +
                                  (param_or_ret == ParamOrRetType::kParameter ? "input" : "output"),
@@ -1147,7 +1144,7 @@
                                       /* is_input */ param_or_ret == ParamOrRetType::kParameter)) {
                     return false;
                 }
-                builtins.emplace(builtin->builtin);
+                builtins.Add(builtin->builtin);
             } else if (auto* loc_attr = attr->As<ast::LocationAttribute>()) {
                 if (pipeline_io_attribute) {
                     AddError("multiple entry point IO attributes", attr->source);
@@ -1292,8 +1289,8 @@
 
     // Clear IO sets after parameter validation. Builtin and location attributes in return types
     // should be validated independently from those used in parameters.
-    builtins.clear();
-    locations.clear();
+    builtins.Clear();
+    locations.Clear();
 
     if (!func->ReturnType()->Is<sem::Void>()) {
         if (!validate_entry_point_attributes(decl->return_type_attributes, func->ReturnType(),
@@ -1304,7 +1301,7 @@
     }
 
     if (decl->PipelineStage() == ast::PipelineStage::kVertex &&
-        builtins.count(ast::BuiltinValue::kPosition) == 0) {
+        !builtins.Contains(ast::BuiltinValue::kPosition)) {
         // Check module-scope variables, as the SPIR-V sanitizer generates these.
         bool found = false;
         for (auto* global : func->TransitivelyReferencedGlobals()) {
@@ -1332,18 +1329,18 @@
     }
 
     // Validate there are no resource variable binding collisions
-    std::unordered_map<sem::BindingPoint, const ast::Variable*> binding_points;
+    utils::Hashmap<sem::BindingPoint, const ast::Variable*, 8> binding_points;
     for (auto* global : func->TransitivelyReferencedGlobals()) {
         auto* var_decl = global->Declaration()->As<ast::Var>();
         if (!var_decl || !var_decl->HasBindingPoint()) {
             continue;
         }
         auto bp = global->BindingPoint();
-        auto res = binding_points.emplace(bp, var_decl);
-        if (!res.second &&
+        if (auto added = binding_points.Add(bp, var_decl);
+            !added &&
             IsValidationEnabled(decl->attributes,
                                 ast::DisabledValidation::kBindingPointCollision) &&
-            IsValidationEnabled(res.first->second->attributes,
+            IsValidationEnabled((*added.value)->attributes,
                                 ast::DisabledValidation::kBindingPointCollision)) {
             // https://gpuweb.github.io/gpuweb/wgsl/#resource-interface
             // Bindings must not alias within a shader stage: two different variables in the
@@ -1355,7 +1352,7 @@
                     "' references multiple variables that use the same resource binding @group(" +
                     std::to_string(bp.group) + "), @binding(" + std::to_string(bp.binding) + ")",
                 var_decl->source);
-            AddNote("first resource binding usage declared here", res.first->second->source);
+            AddNote("first resource binding usage declared here", (*added.value)->source);
             return false;
         }
     }
@@ -1441,64 +1438,11 @@
         AddError("break statement must be in a loop or switch case", stmt->Declaration()->source);
         return false;
     }
-    if (auto* continuing = ClosestContinuing(/*stop_at_loop*/ true, current_statement)) {
-        AddWarning(
-            "use of deprecated language feature: `break` must not be used to exit from "
-            "a continuing block. Use `break-if` instead.",
+    if (ClosestContinuing(/*stop_at_loop*/ true, current_statement) != nullptr) {
+        AddError(
+            "`break` must not be used to exit from a continuing block. Use `break-if` instead.",
             stmt->Declaration()->source);
-
-        auto fail = [&](const char* note_msg, const Source& note_src) {
-            constexpr const char* kErrorMsg =
-                "break statement in a continuing block must be the single statement of an if "
-                "statement's true or false block, and that if statement must be the last statement "
-                "of the continuing block";
-            AddError(kErrorMsg, stmt->Declaration()->source);
-            AddNote(note_msg, note_src);
-            return false;
-        };
-
-        if (auto* block = stmt->Parent()->As<sem::BlockStatement>()) {
-            auto* block_parent = block->Parent();
-            auto* if_stmt = block_parent->As<sem::IfStatement>();
-            if (!if_stmt) {
-                return fail("break statement is not directly in if statement block",
-                            stmt->Declaration()->source);
-            }
-            if (block->Declaration()->statements.Length() != 1) {
-                return fail("if statement block contains multiple statements",
-                            block->Declaration()->source);
-            }
-
-            if (if_stmt->Parent()->Is<sem::IfStatement>()) {
-                return fail("else has condition", if_stmt->Declaration()->source);
-            }
-
-            bool el_contains_break = block->Declaration() == if_stmt->Declaration()->else_statement;
-            if (el_contains_break) {
-                if (auto* true_block = if_stmt->Declaration()->body; !true_block->Empty()) {
-                    return fail("non-empty true block", true_block->source);
-                }
-            } else {
-                auto* else_stmt = if_stmt->Declaration()->else_statement;
-                if (else_stmt) {
-                    return fail("non-empty false block", else_stmt->source);
-                }
-            }
-
-            if (if_stmt->Parent()->Declaration() != continuing) {
-                return fail(
-                    "if statement containing break statement is not directly in continuing block",
-                    if_stmt->Declaration()->source);
-            }
-            if (auto* cont_block = continuing->As<ast::BlockStatement>()) {
-                if (if_stmt->Declaration() != cont_block->Last()) {
-                    return fail(
-                        "if statement containing break statement is not the last statement of the "
-                        "continuing block",
-                        if_stmt->Declaration()->source);
-                }
-            }
-        }
+        return false;
     }
     return true;
 }
@@ -1544,39 +1488,6 @@
     return true;
 }
 
-bool Validator::DiscardStatement(const sem::Statement* stmt,
-                                 sem::Statement* current_statement) const {
-    if (auto* continuing = ClosestContinuing(/*stop_at_loop*/ false, current_statement)) {
-        AddError("continuing blocks must not contain a discard statement",
-                 stmt->Declaration()->source);
-        if (continuing != stmt->Declaration() && continuing != stmt->Parent()->Declaration()) {
-            AddNote("see continuing block here", continuing->source);
-        }
-        return false;
-    }
-    return true;
-}
-
-bool Validator::FallthroughStatement(const sem::Statement* stmt) const {
-    if (auto* block = As<sem::BlockStatement>(stmt->Parent())) {
-        if (auto* c = As<sem::CaseStatement>(block->Parent())) {
-            if (block->Declaration()->Last() == stmt->Declaration()) {
-                if (auto* s = As<sem::SwitchStatement>(c->Parent())) {
-                    if (c->Declaration() != s->Declaration()->body.Back()) {
-                        return true;
-                    }
-                    AddError("a fallthrough statement must not be used in the last switch case",
-                             stmt->Declaration()->source);
-                    return false;
-                }
-            }
-        }
-    }
-    AddError("fallthrough must only be used as the last statement of a case block",
-             stmt->Declaration()->source);
-    return false;
-}
-
 bool Validator::LoopStatement(const sem::LoopStatement* stmt) const {
     if (stmt->Behaviors().Empty()) {
         AddError("loop does not exit", stmt->Declaration()->source.Begin());
@@ -1632,7 +1543,7 @@
         }
         if (auto* continuing = s->As<sem::LoopContinuingBlockStatement>()) {
             if (continuing->Declaration()->statements.Back() != stmt->Declaration()) {
-                AddError("break-if must be last statement in a continuing block",
+                AddError("break-if must be the last statement in a continuing block",
                          stmt->Declaration()->source);
                 AddNote("see continuing block here", s->Declaration()->source);
                 return false;
@@ -1641,7 +1552,7 @@
         }
     }
 
-    AddError("break-if must in a continuing block", stmt->Declaration()->source);
+    AddError("break-if must be in a continuing block", stmt->Declaration()->source);
     return false;
 }
 
@@ -1799,35 +1710,28 @@
         }
 
         if (param_type->Is<sem::Pointer>()) {
-            auto is_valid = false;
-            if (auto* ident_expr = arg_expr->As<ast::IdentifierExpression>()) {
-                auto* var = sem_.ResolvedSymbol<sem::Variable>(ident_expr);
-                if (!var) {
-                    TINT_ICE(Resolver, diagnostics_) << "failed to resolve identifier";
-                    return false;
-                }
-                if (var->Is<sem::Parameter>()) {
-                    is_valid = true;
-                }
-            } else if (auto* unary = arg_expr->As<ast::UnaryOpExpression>()) {
-                if (unary->op == ast::UnaryOp::kAddressOf) {
-                    if (auto* ident_unary = unary->expr->As<ast::IdentifierExpression>()) {
-                        auto* var = sem_.ResolvedSymbol<sem::Variable>(ident_unary);
-                        if (!var) {
-                            TINT_ICE(Resolver, diagnostics_) << "failed to resolve identifier";
-                            return false;
-                        }
-                        is_valid = true;
-                    }
-                }
+            // https://gpuweb.github.io/gpuweb/wgsl/#function-restriction
+            // Each argument of pointer type to a user-defined function must have the same memory
+            // view as its root identifier.
+            // We can validate this by just comparing the store type of the argument with that of
+            // its root identifier, as these will match iff the memory view is the same.
+            auto* arg_store_type = arg_type->As<sem::Pointer>()->StoreType();
+            auto* root = call->Arguments()[i]->RootIdentifier();
+            auto* root_ptr_ty = root->Type()->As<sem::Pointer>();
+            auto* root_ref_ty = root->Type()->As<sem::Reference>();
+            TINT_ASSERT(Resolver, root_ptr_ty || root_ref_ty);
+            const sem::Type* root_store_type;
+            if (root_ptr_ty) {
+                root_store_type = root_ptr_ty->StoreType();
+            } else {
+                root_store_type = root_ref_ty->StoreType();
             }
-
-            if (!is_valid &&
+            if (root_store_type != arg_store_type &&
                 IsValidationEnabled(param->Declaration()->attributes,
                                     ast::DisabledValidation::kIgnoreInvalidPointerArgument)) {
                 AddError(
-                    "expected an address-of expression of a variable identifier expression or a "
-                    "function parameter",
+                    "arguments of pointer type must not point to a subset of the originating "
+                    "variable",
                     arg_expr->source);
                 return false;
             }
@@ -1850,18 +1754,6 @@
         }
     }
 
-    if (call->Behaviors().Contains(sem::Behavior::kDiscard)) {
-        if (auto* continuing = ClosestContinuing(/*stop_at_loop*/ false, current_statement)) {
-            AddError("cannot call a function that may discard inside a continuing block",
-                     call->Declaration()->source);
-            if (continuing != call->Stmt()->Declaration() &&
-                continuing != call->Stmt()->Parent()->Declaration()) {
-                AddNote("see continuing block here", continuing->source);
-            }
-            return false;
-        }
-    }
-
     return true;
 }
 
@@ -1954,7 +1846,7 @@
     return true;
 }
 
-bool Validator::PipelineStages(const std::vector<sem::Function*>& entry_points) const {
+bool Validator::PipelineStages(const utils::VectorRef<sem::Function*> entry_points) const {
     auto backtrace = [&](const sem::Function* func, const sem::Function* entry_point) {
         if (func != entry_point) {
             TraverseCallChain(diagnostics_, entry_point, func, [&](const sem::Function* f) {
@@ -2049,7 +1941,7 @@
     return true;
 }
 
-bool Validator::PushConstants(const std::vector<sem::Function*>& entry_points) const {
+bool Validator::PushConstants(const utils::VectorRef<sem::Function*> entry_points) const {
     for (auto* entry_point : entry_points) {
         // State checked and modified by check_push_constant so that it remembers previously seen
         // push_constant variables for an entry-point.
@@ -2167,7 +2059,7 @@
         return false;
     }
 
-    std::unordered_set<uint32_t> locations;
+    utils::Hashset<uint32_t, 8> locations;
     for (auto* member : str->Members()) {
         if (auto* r = member->Type()->As<sem::Array>()) {
             if (r->IsRuntimeSized()) {
@@ -2285,7 +2177,7 @@
 bool Validator::LocationAttribute(const ast::LocationAttribute* loc_attr,
                                   uint32_t location,
                                   const sem::Type* type,
-                                  std::unordered_set<uint32_t>& locations,
+                                  utils::Hashset<uint32_t, 8>& locations,
                                   ast::PipelineStage stage,
                                   const Source& source,
                                   const bool is_input) const {
@@ -2306,12 +2198,11 @@
         return false;
     }
 
-    if (locations.count(location)) {
+    if (!locations.Add(location)) {
         AddError(attr_to_str(loc_attr, location) + " attribute appears multiple times",
                  loc_attr->source);
         return false;
     }
-    locations.emplace(location);
 
     return true;
 }
@@ -2348,7 +2239,7 @@
     }
 
     const sem::CaseSelector* default_selector = nullptr;
-    std::unordered_map<int64_t, Source> selectors;
+    utils::Hashmap<int64_t, Source, 4> selectors;
 
     for (auto* case_stmt : s->body) {
         auto* case_sem = sem_.Get<sem::CaseStatement>(case_stmt);
@@ -2375,18 +2266,16 @@
             }
 
             auto value = selector->Value()->As<uint32_t>();
-            auto it = selectors.find(value);
-            if (it != selectors.end()) {
+            if (auto added = selectors.Add(value, selector->Declaration()->source); !added) {
                 AddError("duplicate switch case '" +
                              (decl_ty->IsAnyOf<sem::I32, sem::AbstractNumeric>()
                                   ? std::to_string(i32(value))
                                   : std::to_string(value)) +
                              "'",
                          selector->Declaration()->source);
-                AddNote("previous case declared here", it->second);
+                AddNote("previous case declared here", *added.value);
                 return false;
             }
-            selectors.emplace(value, selector->Declaration()->source);
         }
     }
 
@@ -2514,12 +2403,12 @@
 }
 
 bool Validator::NoDuplicateAttributes(utils::VectorRef<const ast::Attribute*> attributes) const {
-    std::unordered_map<const TypeInfo*, Source> seen;
+    utils::Hashmap<const TypeInfo*, Source, 8> seen;
     for (auto* d : attributes) {
-        auto res = seen.emplace(&d->TypeInfo(), d->source);
-        if (!res.second && !d->Is<ast::InternalAttribute>()) {
+        auto added = seen.Add(&d->TypeInfo(), d->source);
+        if (!added && !d->Is<ast::InternalAttribute>()) {
             AddError("duplicate " + d->Name() + " attribute", d->source);
-            AddNote("first attribute declared here", res.first->second);
+            AddNote("first attribute declared here", *added.value);
             return false;
         }
     }
diff --git a/src/tint/resolver/validator.h b/src/tint/resolver/validator.h
index efc3842..500d056 100644
--- a/src/tint/resolver/validator.h
+++ b/src/tint/resolver/validator.h
@@ -17,16 +17,15 @@
 
 #include <set>
 #include <string>
-#include <unordered_map>
-#include <unordered_set>
 #include <utility>
-#include <vector>
 
 #include "src/tint/ast/pipeline_stage.h"
 #include "src/tint/program_builder.h"
 #include "src/tint/resolver/sem_helper.h"
 #include "src/tint/sem/evaluation_stage.h"
 #include "src/tint/source.h"
+#include "src/tint/utils/hashmap.h"
+#include "src/tint/utils/vector.h"
 
 // Forward declarations
 namespace tint::ast {
@@ -116,12 +115,12 @@
     /// Validates pipeline stages
     /// @param entry_points the entry points to the module
     /// @returns true on success, false otherwise.
-    bool PipelineStages(const std::vector<sem::Function*>& entry_points) const;
+    bool PipelineStages(const utils::VectorRef<sem::Function*> entry_points) const;
 
     /// Validates push_constant variables
     /// @param entry_points the entry points to the module
     /// @returns true on success, false otherwise.
-    bool PushConstants(const std::vector<sem::Function*>& entry_points) const;
+    bool PushConstants(const utils::VectorRef<sem::Function*> entry_points) const;
 
     /// Validates aliases
     /// @param alias the alias to validate
@@ -156,7 +155,7 @@
     /// @returns true on success, false otherwise.
     bool AtomicVariable(
         const sem::Variable* var,
-        std::unordered_map<const sem::Type*, const Source&> atomic_composite_info) const;
+        const utils::Hashmap<const sem::Type*, const Source*, 8>& atomic_composite_info) const;
 
     /// Validates an assignment
     /// @param a the assignment statement
@@ -199,12 +198,6 @@
     /// @returns true on success, false otherwise
     bool Call(const sem::Call* call, sem::Statement* current_statement) const;
 
-    /// Validates a discard statement
-    /// @param stmt the statement to validate
-    /// @param current_statement the current statement being resolved
-    /// @returns true on success, false otherwise
-    bool DiscardStatement(const sem::Statement* stmt, sem::Statement* current_statement) const;
-
     /// Validates an entry point
     /// @param func the entry point function to validate
     /// @param stage the pipeline stage for the entry point
@@ -230,11 +223,6 @@
     /// @returns true on success, false otherwise
     bool WhileStatement(const sem::WhileStatement* stmt) const;
 
-    /// Validates a fallthrough statement
-    /// @param stmt the fallthrough to validate
-    /// @returns true on success, false otherwise
-    bool FallthroughStatement(const sem::Statement* stmt) const;
-
     /// Validates a function
     /// @param func the function to validate
     /// @param stage the current pipeline stage
@@ -254,8 +242,8 @@
     /// @returns true on success, false otherwise
     bool GlobalVariable(
         const sem::GlobalVariable* var,
-        const std::unordered_map<OverrideId, const sem::Variable*>& override_id,
-        const std::unordered_map<const sem::Type*, const Source&>& atomic_composite_info) const;
+        const utils::Hashmap<OverrideId, const sem::Variable*, 8>& override_id,
+        const utils::Hashmap<const sem::Type*, const Source*, 8>& atomic_composite_info) const;
 
     /// Validates a break-if statement
     /// @param stmt the statement to validate
@@ -303,7 +291,7 @@
     bool LocationAttribute(const ast::LocationAttribute* loc_attr,
                            uint32_t location,
                            const sem::Type* type,
-                           std::unordered_set<uint32_t>& locations,
+                           utils::Hashset<uint32_t, 8>& locations,
                            ast::PipelineStage stage,
                            const Source& source,
                            const bool is_input = false) const;
@@ -398,7 +386,7 @@
     /// @param override_id the set of override ids in the module
     /// @returns true on success, false otherwise.
     bool Override(const sem::GlobalVariable* v,
-                  const std::unordered_map<OverrideId, const sem::Variable*>& override_id) const;
+                  const utils::Hashmap<OverrideId, const sem::Variable*, 8>& override_id) const;
 
     /// Validates a 'const' variable declaration
     /// @param v the variable to validate
diff --git a/src/tint/scope_stack.h b/src/tint/scope_stack.h
index 6838f5b..a2da4dd 100644
--- a/src/tint/scope_stack.h
+++ b/src/tint/scope_stack.h
@@ -14,11 +14,11 @@
 #ifndef SRC_TINT_SCOPE_STACK_H_
 #define SRC_TINT_SCOPE_STACK_H_
 
-#include <unordered_map>
 #include <utility>
-#include <vector>
 
 #include "src/tint/symbol.h"
+#include "src/tint/utils/hashmap.h"
+#include "src/tint/utils/vector.h"
 
 namespace tint {
 
@@ -27,22 +27,13 @@
 template <class K, class V>
 class ScopeStack {
   public:
-    /// Constructor
-    ScopeStack() {
-        // Push global bucket
-        stack_.push_back({});
-    }
-    /// Copy Constructor
-    ScopeStack(const ScopeStack&) = default;
-    ~ScopeStack() = default;
-
     /// Push a new scope on to the stack
-    void Push() { stack_.push_back({}); }
+    void Push() { stack_.Push({}); }
 
     /// Pop the scope off the top of the stack
     void Pop() {
-        if (stack_.size() > 1) {
-            stack_.pop_back();
+        if (stack_.Length() > 1) {
+            stack_.Pop();
         }
     }
 
@@ -52,8 +43,13 @@
     /// @returns the old value if there was an existing key at the top of the
     /// stack, otherwise the zero initializer for type T.
     V Set(const K& key, V val) {
-        std::swap(val, stack_.back()[key]);
-        return val;
+        auto& back = stack_.Back();
+        if (auto* el = back.Find(key)) {
+            std::swap(val, *el);
+            return val;
+        }
+        back.Add(key, val);
+        return {};
     }
 
     /// Retrieves a value from the stack
@@ -61,10 +57,8 @@
     /// @returns the value, or the zero initializer if the value was not found
     V Get(const K& key) const {
         for (auto iter = stack_.rbegin(); iter != stack_.rend(); ++iter) {
-            auto& map = *iter;
-            auto val = map.find(key);
-            if (val != map.end()) {
-                return val->second;
+            if (auto* val = iter->Find(key)) {
+                return *val;
             }
         }
 
@@ -73,16 +67,16 @@
 
     /// Return the top scope of the stack.
     /// @returns the top scope of the stack
-    const std::unordered_map<K, V>& Top() const { return stack_.back(); }
+    const utils::Hashmap<K, V, 8>& Top() const { return stack_.Back(); }
 
     /// Clear the scope stack.
     void Clear() {
-        stack_.clear();
-        stack_.push_back({});
+        stack_.Clear();
+        stack_.Push({});
     }
 
   private:
-    std::vector<std::unordered_map<K, V>> stack_;
+    utils::Vector<utils::Hashmap<K, V, 8>, 8> stack_ = {{}};
 };
 
 }  // namespace tint
diff --git a/src/tint/sem/array.h b/src/tint/sem/array.h
index 88e6373..4047ae4 100644
--- a/src/tint/sem/array.h
+++ b/src/tint/sem/array.h
@@ -23,6 +23,7 @@
 #include "src/tint/sem/node.h"
 #include "src/tint/sem/type.h"
 #include "src/tint/utils/compiler_macros.h"
+#include "src/tint/utils/unique_vector.h"
 
 // Forward declarations
 namespace tint::sem {
@@ -229,6 +230,17 @@
     /// @returns true if this array is runtime sized
     bool IsRuntimeSized() const { return std::holds_alternative<RuntimeArrayCount>(count_); }
 
+    /// Records that this array type (transitively) references the given override variable.
+    /// @param var the module-scope override variable
+    void AddTransitivelyReferencedOverride(const GlobalVariable* var) {
+        referenced_overrides_.Add(var);
+    }
+
+    /// @returns all transitively referenced override variables
+    const utils::UniqueVector<const GlobalVariable*, 4>& TransitivelyReferencedOverrides() const {
+        return referenced_overrides_;
+    }
+
     /// @param symbols the program's symbol table
     /// @returns the name for this type that closely resembles how it would be
     /// declared in WGSL.
@@ -241,6 +253,7 @@
     const uint32_t size_;
     const uint32_t stride_;
     const uint32_t implicit_stride_;
+    utils::UniqueVector<const GlobalVariable*, 4> referenced_overrides_;
 };
 
 }  // namespace tint::sem
diff --git a/src/tint/sem/behavior.cc b/src/tint/sem/behavior.cc
index 617794f..670cc1d 100644
--- a/src/tint/sem/behavior.cc
+++ b/src/tint/sem/behavior.cc
@@ -20,14 +20,10 @@
     switch (behavior) {
         case Behavior::kReturn:
             return out << "Return";
-        case Behavior::kDiscard:
-            return out << "Discard";
         case Behavior::kBreak:
             return out << "Break";
         case Behavior::kContinue:
             return out << "Continue";
-        case Behavior::kFallthrough:
-            return out << "Fallthrough";
         case Behavior::kNext:
             return out << "Next";
     }
diff --git a/src/tint/sem/behavior.h b/src/tint/sem/behavior.h
index 4acb8d5..011ca72 100644
--- a/src/tint/sem/behavior.h
+++ b/src/tint/sem/behavior.h
@@ -23,10 +23,8 @@
 /// @see https://www.w3.org/TR/WGSL/#behaviors
 enum class Behavior {
     kReturn,
-    kDiscard,
     kBreak,
     kContinue,
-    kFallthrough,
     kNext,
 };
 
diff --git a/src/tint/sem/expression.cc b/src/tint/sem/expression.cc
index cdaa2bc..d7fd255 100644
--- a/src/tint/sem/expression.cc
+++ b/src/tint/sem/expression.cc
@@ -28,9 +28,9 @@
                        const Statement* statement,
                        const Constant* constant,
                        bool has_side_effects,
-                       const Variable* source_var /* = nullptr */)
+                       const Variable* root_ident /* = nullptr */)
     : declaration_(declaration),
-      source_variable_(source_var),
+      root_identifier_(root_ident),
       type_(type),
       stage_(stage),
       statement_(statement),
diff --git a/src/tint/sem/expression.h b/src/tint/sem/expression.h
index 5942a71..cc87031 100644
--- a/src/tint/sem/expression.h
+++ b/src/tint/sem/expression.h
@@ -40,14 +40,14 @@
     /// @param statement the statement that owns this expression
     /// @param constant the constant value of the expression. May be null
     /// @param has_side_effects true if this expression may have side-effects
-    /// @param source_var the (optional) source variable for this expression
+    /// @param root_ident the (optional) root identifier for this expression
     Expression(const ast::Expression* declaration,
                const sem::Type* type,
                EvaluationStage stage,
                const Statement* statement,
                const Constant* constant,
                bool has_side_effects,
-               const Variable* source_var = nullptr);
+               const Variable* root_ident = nullptr);
 
     /// Destructor
     ~Expression() override;
@@ -71,8 +71,8 @@
     /// For reference and pointer expressions, this will either be the originating
     /// variable or a function parameter. For other types of expressions, it will
     /// either be the parameter or constant declaration, or nullptr.
-    /// @return the source variable of this expression, or nullptr
-    const Variable* SourceVariable() const { return source_variable_; }
+    /// @return the root identifier of this expression, or nullptr
+    const Variable* RootIdentifier() const { return root_identifier_; }
 
     /// @return the behaviors of this statement
     const sem::Behaviors& Behaviors() const { return behaviors_; }
@@ -89,8 +89,8 @@
   protected:
     /// The AST expression node for this semantic expression
     const ast::Expression* const declaration_;
-    /// The source variable for this semantic expression, or nullptr
-    const Variable* source_variable_;
+    /// The root identifier for this semantic expression, or nullptr
+    const Variable* root_identifier_;
 
   private:
     const sem::Type* const type_;
diff --git a/src/tint/sem/expression_test.cc b/src/tint/sem/expression_test.cc
index 63e01fb..845b279 100644
--- a/src/tint/sem/expression_test.cc
+++ b/src/tint/sem/expression_test.cc
@@ -46,7 +46,7 @@
     auto* a = create<Expression>(/* declaration */ nullptr, create<I32>(),
                                  sem::EvaluationStage::kRuntime, /* statement */ nullptr,
                                  /* constant_value */ nullptr,
-                                 /* has_side_effects */ false, /* source_var */ nullptr);
+                                 /* has_side_effects */ false, /* root_ident */ nullptr);
     auto* b = create<Materialize>(a, /* statement */ nullptr, &c);
 
     EXPECT_EQ(a, a->UnwrapMaterialize());
diff --git a/src/tint/sem/index_accessor_expression.cc b/src/tint/sem/index_accessor_expression.cc
index 70d1d5f..0565695 100644
--- a/src/tint/sem/index_accessor_expression.cc
+++ b/src/tint/sem/index_accessor_expression.cc
@@ -30,8 +30,8 @@
                                                  const Statement* statement,
                                                  const Constant* constant,
                                                  bool has_side_effects,
-                                                 const Variable* source_var /* = nullptr */)
-    : Base(declaration, type, stage, statement, constant, has_side_effects, source_var),
+                                                 const Variable* root_ident /* = nullptr */)
+    : Base(declaration, type, stage, statement, constant, has_side_effects, root_ident),
       object_(object),
       index_(index) {}
 
diff --git a/src/tint/sem/index_accessor_expression.h b/src/tint/sem/index_accessor_expression.h
index ea93df7..19ae82b 100644
--- a/src/tint/sem/index_accessor_expression.h
+++ b/src/tint/sem/index_accessor_expression.h
@@ -38,7 +38,7 @@
     /// @param statement the statement that owns this expression
     /// @param constant the constant value of the expression. May be null
     /// @param has_side_effects whether this expression may have side effects
-    /// @param source_var the (optional) source variable for this expression
+    /// @param root_ident the (optional) root identifier for this expression
     IndexAccessorExpression(const ast::IndexAccessorExpression* declaration,
                             const sem::Type* type,
                             EvaluationStage stage,
@@ -47,7 +47,7 @@
                             const Statement* statement,
                             const Constant* constant,
                             bool has_side_effects,
-                            const Variable* source_var = nullptr);
+                            const Variable* root_ident = nullptr);
 
     /// Destructor
     ~IndexAccessorExpression() override;
diff --git a/src/tint/sem/materialize.cc b/src/tint/sem/materialize.cc
index 15c31ae..c735f6d 100644
--- a/src/tint/sem/materialize.cc
+++ b/src/tint/sem/materialize.cc
@@ -26,7 +26,7 @@
            /* statement */ statement,
            /* constant */ constant,
            /* has_side_effects */ false,
-           /* source_var */ expr->SourceVariable()),
+           /* root_ident */ expr->RootIdentifier()),
       expr_(expr) {}
 
 Materialize::~Materialize() = default;
diff --git a/src/tint/sem/member_accessor_expression.cc b/src/tint/sem/member_accessor_expression.cc
index 4d194a9..8d08008 100644
--- a/src/tint/sem/member_accessor_expression.cc
+++ b/src/tint/sem/member_accessor_expression.cc
@@ -30,8 +30,8 @@
                                                    const Constant* constant,
                                                    const Expression* object,
                                                    bool has_side_effects,
-                                                   const Variable* source_var /* = nullptr */)
-    : Base(declaration, type, stage, statement, constant, has_side_effects, source_var),
+                                                   const Variable* root_ident /* = nullptr */)
+    : Base(declaration, type, stage, statement, constant, has_side_effects, root_ident),
       object_(object) {}
 
 MemberAccessorExpression::~MemberAccessorExpression() = default;
@@ -43,7 +43,7 @@
                                        const Expression* object,
                                        const StructMember* member,
                                        bool has_side_effects,
-                                       const Variable* source_var /* = nullptr */)
+                                       const Variable* root_ident /* = nullptr */)
     : Base(declaration,
            type,
            object->Stage(),
@@ -51,7 +51,7 @@
            constant,
            object,
            has_side_effects,
-           source_var),
+           root_ident),
       member_(member) {}
 
 StructMemberAccess::~StructMemberAccess() = default;
@@ -63,7 +63,7 @@
                  const Expression* object,
                  utils::VectorRef<uint32_t> indices,
                  bool has_side_effects,
-                 const Variable* source_var /* = nullptr */)
+                 const Variable* root_ident /* = nullptr */)
     : Base(declaration,
            type,
            object->Stage(),
@@ -71,7 +71,7 @@
            constant,
            object,
            has_side_effects,
-           source_var),
+           root_ident),
       indices_(std::move(indices)) {}
 
 Swizzle::~Swizzle() = default;
diff --git a/src/tint/sem/member_accessor_expression.h b/src/tint/sem/member_accessor_expression.h
index 2541f33..5541d1c 100644
--- a/src/tint/sem/member_accessor_expression.h
+++ b/src/tint/sem/member_accessor_expression.h
@@ -48,7 +48,7 @@
     /// @param constant the constant value of the expression. May be null.
     /// @param object the object that holds the member being accessed
     /// @param has_side_effects whether this expression may have side effects
-    /// @param source_var the (optional) source variable for this expression
+    /// @param root_ident the (optional) root identifier for this expression
     MemberAccessorExpression(const ast::MemberAccessorExpression* declaration,
                              const sem::Type* type,
                              EvaluationStage stage,
@@ -56,7 +56,7 @@
                              const Constant* constant,
                              const Expression* object,
                              bool has_side_effects,
-                             const Variable* source_var = nullptr);
+                             const Variable* root_ident = nullptr);
 
   private:
     Expression const* const object_;
@@ -75,7 +75,7 @@
     /// @param object the object that holds the member being accessed
     /// @param member the structure member
     /// @param has_side_effects whether this expression may have side effects
-    /// @param source_var the (optional) source variable for this expression
+    /// @param root_ident the (optional) root identifier for this expression
     StructMemberAccess(const ast::MemberAccessorExpression* declaration,
                        const sem::Type* type,
                        const Statement* statement,
@@ -83,7 +83,7 @@
                        const Expression* object,
                        const StructMember* member,
                        bool has_side_effects,
-                       const Variable* source_var = nullptr);
+                       const Variable* root_ident = nullptr);
 
     /// Destructor
     ~StructMemberAccess() override;
@@ -107,7 +107,7 @@
     /// @param object the object that holds the member being accessed
     /// @param indices the swizzle indices
     /// @param has_side_effects whether this expression may have side effects
-    /// @param source_var the (optional) source variable for this expression
+    /// @param root_ident the (optional) root identifier for this expression
     Swizzle(const ast::MemberAccessorExpression* declaration,
             const sem::Type* type,
             const Statement* statement,
@@ -115,7 +115,7 @@
             const Expression* object,
             utils::VectorRef<uint32_t> indices,
             bool has_side_effects,
-            const Variable* source_var = nullptr);
+            const Variable* root_ident = nullptr);
 
     /// Destructor
     ~Swizzle() override;
diff --git a/src/tint/sem/variable.cc b/src/tint/sem/variable.cc
index 225e308..f83745f 100644
--- a/src/tint/sem/variable.cc
+++ b/src/tint/sem/variable.cc
@@ -97,9 +97,9 @@
       variable_(variable) {
     auto* type = variable->Type();
     if (type->Is<sem::Pointer>() && variable->Initializer()) {
-        source_variable_ = variable->Initializer()->SourceVariable();
+        root_identifier_ = variable->Initializer()->RootIdentifier();
     } else {
-        source_variable_ = variable;
+        root_identifier_ = variable;
     }
 }
 
diff --git a/src/tint/sem/variable.h b/src/tint/sem/variable.h
index 634f5da..08ffe99 100644
--- a/src/tint/sem/variable.h
+++ b/src/tint/sem/variable.h
@@ -27,6 +27,7 @@
 #include "src/tint/sem/binding_point.h"
 #include "src/tint/sem/expression.h"
 #include "src/tint/sem/parameter_usage.h"
+#include "src/tint/utils/unique_vector.h"
 
 // Forward declarations
 namespace tint::ast {
@@ -182,11 +183,23 @@
     /// @returns the location value for the parameter, if set
     std::optional<uint32_t> Location() const { return location_; }
 
+    /// Records that this variable (transitively) references the given override variable.
+    /// @param var the module-scope override variable
+    void AddTransitivelyReferencedOverride(const GlobalVariable* var) {
+        referenced_overrides_.Add(var);
+    }
+
+    /// @returns all transitively referenced override variables
+    const utils::UniqueVector<const GlobalVariable*, 4>& TransitivelyReferencedOverrides() const {
+        return referenced_overrides_;
+    }
+
   private:
     const sem::BindingPoint binding_point_;
 
     tint::OverrideId override_id_;
     std::optional<uint32_t> location_;
+    utils::UniqueVector<const GlobalVariable*, 4> referenced_overrides_;
 };
 
 /// Parameter is a function parameter
diff --git a/src/tint/source.cc b/src/tint/source.cc
index 5dbed6c..6a10de5 100644
--- a/src/tint/source.cc
+++ b/src/tint/source.cc
@@ -113,13 +113,10 @@
 
 }  // namespace
 
-Source::FileContent::FileContent(const std::string& body)
-    : data(body), data_view(data), lines(SplitLines(data_view)) {}
+Source::FileContent::FileContent(const std::string& body) : data(body), lines(SplitLines(data)) {}
 
 Source::FileContent::FileContent(const FileContent& rhs)
-    : data(rhs.data),
-      data_view(data),
-      lines(CopyRelativeStringViews(rhs.lines, rhs.data_view, data_view)) {}
+    : data(rhs.data), lines(CopyRelativeStringViews(rhs.lines, rhs.data, data)) {}
 
 Source::FileContent::~FileContent() = default;
 
diff --git a/src/tint/source.h b/src/tint/source.h
index 734e693..d7ca7b9 100644
--- a/src/tint/source.h
+++ b/src/tint/source.h
@@ -43,8 +43,6 @@
 
         /// The original un-split file content
         const std::string data;
-        /// A string_view over #data
-        const std::string_view data_view;
         /// #data split by lines
         const std::vector<std::string_view> lines;
     };
diff --git a/src/tint/source_test.cc b/src/tint/source_test.cc
index b1241fb..df14a31 100644
--- a/src/tint/source_test.cc
+++ b/src/tint/source_test.cc
@@ -31,7 +31,6 @@
 TEST_F(SourceFileContentTest, Init) {
     Source::FileContent fc(kSource);
     EXPECT_EQ(fc.data, kSource);
-    EXPECT_EQ(fc.data_view, kSource);
     ASSERT_EQ(fc.lines.size(), 3u);
     EXPECT_EQ(fc.lines[0], "line one");
     EXPECT_EQ(fc.lines[1], "line two");
@@ -43,7 +42,6 @@
     Source::FileContent fc{*src};
     src.reset();
     EXPECT_EQ(fc.data, kSource);
-    EXPECT_EQ(fc.data_view, kSource);
     ASSERT_EQ(fc.lines.size(), 3u);
     EXPECT_EQ(fc.lines[0], "line one");
     EXPECT_EQ(fc.lines[1], "line two");
@@ -55,7 +53,6 @@
     Source::FileContent fc{std::move(*src)};
     src.reset();
     EXPECT_EQ(fc.data, kSource);
-    EXPECT_EQ(fc.data_view, kSource);
     ASSERT_EQ(fc.lines.size(), 3u);
     EXPECT_EQ(fc.lines[0], "line one");
     EXPECT_EQ(fc.lines[1], "line two");
diff --git a/src/tint/transform/builtin_polyfill.cc b/src/tint/transform/builtin_polyfill.cc
index 17fcc20..15aa233 100644
--- a/src/tint/transform/builtin_polyfill.cc
+++ b/src/tint/transform/builtin_polyfill.cc
@@ -14,6 +14,8 @@
 
 #include "src/tint/transform/builtin_polyfill.h"
 
+#include <algorithm>
+#include <tuple>
 #include <unordered_map>
 #include <utility>
 
@@ -29,6 +31,9 @@
 
 namespace tint::transform {
 
+/// BinaryOpSignature is tuple of a binary op, LHS type and RHS type
+using BinaryOpSignature = std::tuple<ast::BinaryOp, const sem::Type*, const sem::Type*>;
+
 /// PIMPL state for the transform
 struct BuiltinPolyfill::State {
     /// Constructor
@@ -36,14 +41,9 @@
     /// @param p the builtins to polyfill
     State(CloneContext& c, Builtins p) : ctx(c), polyfill(p) {}
 
-    /// The clone context
-    CloneContext& ctx;
-    /// The builtins to polyfill
-    Builtins polyfill;
-    /// The destination program builder
-    ProgramBuilder& b = *ctx.dst;
-    /// The source clone context
-    const sem::Info& sem = ctx.src->Sem();
+    ////////////////////////////////////////////////////////////////////////////
+    // Function polyfills
+    ////////////////////////////////////////////////////////////////////////////
 
     /// Builds the polyfill function for the `acosh` builtin
     /// @param ty the parameter and return type for the function
@@ -624,7 +624,102 @@
         return name;
     }
 
+    ////////////////////////////////////////////////////////////////////////////
+    // Inline polyfills
+    ////////////////////////////////////////////////////////////////////////////
+
+    /// Builds the polyfill inline expression for a bitshift left or bitshift right, ensuring that
+    /// the RHS is modulo the bit-width of the LHS.
+    /// @param bin_op the original BinaryExpression
+    /// @return the polyfill value for bitshift operation
+    const ast::Expression* BitshiftModulo(const ast::BinaryExpression* bin_op) {
+        auto* lhs_ty = ctx.src->TypeOf(bin_op->lhs)->UnwrapRef();
+        auto* rhs_ty = ctx.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);
+        }
+        auto* lhs = ctx.Clone(bin_op->lhs);
+        auto* rhs = b.And(ctx.Clone(bin_op->rhs), mask);
+        return b.create<ast::BinaryExpression>(ctx.Clone(bin_op->source), bin_op->op, lhs, rhs);
+    }
+
+    /// Builds the polyfill inline expression for a integer divide or modulo, preventing DBZs and
+    /// integer overflows.
+    /// @param bin_op the original BinaryExpression
+    /// @return the polyfill divide or modulo
+    const ast::Expression* IntDivMod(const ast::BinaryExpression* bin_op) {
+        auto* lhs_ty = ctx.src->TypeOf(bin_op->lhs)->UnwrapRef();
+        auto* rhs_ty = ctx.src->TypeOf(bin_op->rhs)->UnwrapRef();
+        BinaryOpSignature sig{bin_op->op, lhs_ty, rhs_ty};
+        auto fn = binary_op_polyfills.GetOrCreate(sig, [&] {
+            const bool is_div = bin_op->op == ast::BinaryOp::kDivide;
+
+            uint32_t lhs_width = 1;
+            uint32_t rhs_width = 1;
+            const auto* lhs_el_ty = sem::Type::ElementOf(lhs_ty, &lhs_width);
+            const auto* rhs_el_ty = sem::Type::ElementOf(rhs_ty, &rhs_width);
+
+            const uint32_t width = std::max(lhs_width, rhs_width);
+
+            const char* lhs = "lhs";
+            const char* rhs = "rhs";
+
+            utils::Vector<const ast::Statement*, 4> body;
+
+            if (lhs_width < width) {
+                // lhs is scalar, rhs is vector. Convert lhs to vector.
+                body.Push(b.Decl(b.Let("l", b.vec(T(lhs_el_ty), width, b.Expr(lhs)))));
+                lhs = "l";
+            }
+            if (rhs_width < width) {
+                // lhs is vector, rhs is scalar. Convert rhs to vector.
+                body.Push(b.Decl(b.Let("r", b.vec(T(rhs_el_ty), width, b.Expr(rhs)))));
+                rhs = "r";
+            }
+
+            auto name = b.Symbols().New(is_div ? "tint_div" : "tint_mod");
+            auto* use_one = b.Equal(rhs, ScalarOrVector(width, 0_a));
+            if (lhs_ty->is_signed_scalar_or_vector()) {
+                const auto bits = lhs_el_ty->Size() * 8;
+                auto min_int = AInt(AInt::kLowestValue >> (AInt::kNumBits - bits));
+                const ast::Expression* lhs_is_min = b.Equal(lhs, ScalarOrVector(width, min_int));
+                const ast::Expression* rhs_is_minus_one = b.Equal(rhs, ScalarOrVector(width, -1_a));
+                // use_one = use_one | ((lhs == MIN_INT) & (rhs == -1))
+                use_one = b.Or(use_one, b.And(lhs_is_min, rhs_is_minus_one));
+            }
+            auto* select = b.Call("select", rhs, ScalarOrVector(width, 1_a), use_one);
+
+            body.Push(b.Return(is_div ? b.Div(lhs, select) : b.Mod(lhs, select)));
+            b.Func(name,
+                   utils::Vector{
+                       b.Param("lhs", T(lhs_ty)),
+                       b.Param("rhs", T(rhs_ty)),
+                   },
+                   width == 1 ? T(lhs_ty) : b.ty.vec(T(lhs_el_ty), width),  // return type
+                   std::move(body));
+
+            return name;
+        });
+        auto* lhs = ctx.Clone(bin_op->lhs);
+        auto* rhs = ctx.Clone(bin_op->rhs);
+        return b.Call(fn, lhs, rhs);
+    }
+
   private:
+    /// The clone context
+    CloneContext& ctx;
+    /// The builtins to polyfill
+    Builtins polyfill;
+    /// The destination program builder
+    ProgramBuilder& b = *ctx.dst;
+    /// The source clone context
+    const sem::Info& sem = ctx.src->Sem();
+
+    // Polyfill functions for binary operators.
+    utils::Hashmap<BinaryOpSignature, Symbol, 8> binary_op_polyfills;
+
     /// @returns the AST type for the given sem type
     const ast::Type* T(const sem::Type* ty) const { return CreateASTTypeFor(ctx, ty); }
 
@@ -659,13 +754,13 @@
         return SkipTransform;
     }
 
-    auto& builtins = cfg->builtins;
+    auto& polyfill = cfg->builtins;
 
     utils::Hashmap<const sem::Builtin*, Symbol, 8> builtin_polyfills;
 
     ProgramBuilder b;
     CloneContext ctx{&b, src, /* auto_clone_symbols */ true};
-    State s{ctx, builtins};
+    State s{ctx, polyfill};
 
     bool made_changes = false;
     for (auto* node : src->ASTNodes().Objects()) {
@@ -679,84 +774,84 @@
             if (!builtin) {
                 continue;
             }
-            Symbol polyfill;
+            Symbol fn;
             switch (builtin->Type()) {
                 case sem::BuiltinType::kAcosh:
-                    if (builtins.acosh != Level::kNone) {
-                        polyfill = builtin_polyfills.GetOrCreate(
+                    if (polyfill.acosh != Level::kNone) {
+                        fn = builtin_polyfills.GetOrCreate(
                             builtin, [&] { return s.acosh(builtin->ReturnType()); });
                     }
                     break;
                 case sem::BuiltinType::kAsinh:
-                    if (builtins.asinh) {
-                        polyfill = builtin_polyfills.GetOrCreate(
+                    if (polyfill.asinh) {
+                        fn = builtin_polyfills.GetOrCreate(
                             builtin, [&] { return s.asinh(builtin->ReturnType()); });
                     }
                     break;
                 case sem::BuiltinType::kAtanh:
-                    if (builtins.atanh != Level::kNone) {
-                        polyfill = builtin_polyfills.GetOrCreate(
+                    if (polyfill.atanh != Level::kNone) {
+                        fn = builtin_polyfills.GetOrCreate(
                             builtin, [&] { return s.atanh(builtin->ReturnType()); });
                     }
                     break;
                 case sem::BuiltinType::kClamp:
-                    if (builtins.clamp_int) {
+                    if (polyfill.clamp_int) {
                         auto& sig = builtin->Signature();
                         if (sig.parameters[0]->Type()->is_integer_scalar_or_vector()) {
-                            polyfill = builtin_polyfills.GetOrCreate(
+                            fn = builtin_polyfills.GetOrCreate(
                                 builtin, [&] { return s.clampInteger(builtin->ReturnType()); });
                         }
                     }
                     break;
                 case sem::BuiltinType::kCountLeadingZeros:
-                    if (builtins.count_leading_zeros) {
-                        polyfill = builtin_polyfills.GetOrCreate(
+                    if (polyfill.count_leading_zeros) {
+                        fn = builtin_polyfills.GetOrCreate(
                             builtin, [&] { return s.countLeadingZeros(builtin->ReturnType()); });
                     }
                     break;
                 case sem::BuiltinType::kCountTrailingZeros:
-                    if (builtins.count_trailing_zeros) {
-                        polyfill = builtin_polyfills.GetOrCreate(
+                    if (polyfill.count_trailing_zeros) {
+                        fn = builtin_polyfills.GetOrCreate(
                             builtin, [&] { return s.countTrailingZeros(builtin->ReturnType()); });
                     }
                     break;
                 case sem::BuiltinType::kExtractBits:
-                    if (builtins.extract_bits != Level::kNone) {
-                        polyfill = builtin_polyfills.GetOrCreate(
+                    if (polyfill.extract_bits != Level::kNone) {
+                        fn = builtin_polyfills.GetOrCreate(
                             builtin, [&] { return s.extractBits(builtin->ReturnType()); });
                     }
                     break;
                 case sem::BuiltinType::kFirstLeadingBit:
-                    if (builtins.first_leading_bit) {
-                        polyfill = builtin_polyfills.GetOrCreate(
+                    if (polyfill.first_leading_bit) {
+                        fn = builtin_polyfills.GetOrCreate(
                             builtin, [&] { return s.firstLeadingBit(builtin->ReturnType()); });
                     }
                     break;
                 case sem::BuiltinType::kFirstTrailingBit:
-                    if (builtins.first_trailing_bit) {
-                        polyfill = builtin_polyfills.GetOrCreate(
+                    if (polyfill.first_trailing_bit) {
+                        fn = builtin_polyfills.GetOrCreate(
                             builtin, [&] { return s.firstTrailingBit(builtin->ReturnType()); });
                     }
                     break;
                 case sem::BuiltinType::kInsertBits:
-                    if (builtins.insert_bits != Level::kNone) {
-                        polyfill = builtin_polyfills.GetOrCreate(
+                    if (polyfill.insert_bits != Level::kNone) {
+                        fn = builtin_polyfills.GetOrCreate(
                             builtin, [&] { return s.insertBits(builtin->ReturnType()); });
                     }
                     break;
                 case sem::BuiltinType::kSaturate:
-                    if (builtins.saturate) {
-                        polyfill = builtin_polyfills.GetOrCreate(
+                    if (polyfill.saturate) {
+                        fn = builtin_polyfills.GetOrCreate(
                             builtin, [&] { return s.saturate(builtin->ReturnType()); });
                     }
                     break;
                 case sem::BuiltinType::kTextureSampleBaseClampToEdge:
-                    if (builtins.texture_sample_base_clamp_to_edge_2d_f32) {
+                    if (polyfill.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, [&] {
+                                fn = builtin_polyfills.GetOrCreate(builtin, [&] {
                                     return s.textureSampleBaseClampToEdge_2d_f32();
                                 });
                             }
@@ -764,9 +859,9 @@
                     }
                     break;
                 case sem::BuiltinType::kQuantizeToF16:
-                    if (builtins.quantize_to_vec_f16) {
+                    if (polyfill.quantize_to_vec_f16) {
                         if (auto* vec = builtin->ReturnType()->As<sem::Vector>()) {
-                            polyfill = builtin_polyfills.GetOrCreate(
+                            fn = builtin_polyfills.GetOrCreate(
                                 builtin, [&] { return s.quantizeToF16(vec); });
                         }
                     }
@@ -776,28 +871,32 @@
                     break;
             }
 
-            if (polyfill.IsValid()) {
-                auto* replacement = s.b.Call(polyfill, ctx.Clone(call->Declaration()->args));
+            if (fn.IsValid()) {
+                auto* replacement = b.Call(fn, 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);
-                        }
-                        auto* mod = b.And(ctx.Clone(bin_op->rhs), mask);
-                        ctx.Replace(bin_op->rhs, mod);
+                case ast::BinaryOp::kShiftRight: {
+                    if (polyfill.bitshift_modulo) {
+                        ctx.Replace(bin_op, [bin_op, &s] { return s.BitshiftModulo(bin_op); });
                         made_changes = true;
                     }
                     break;
+                }
+                case ast::BinaryOp::kDivide:
+                case ast::BinaryOp::kModulo: {
+                    if (polyfill.int_div_mod) {
+                        auto* lhs_ty = src->TypeOf(bin_op->lhs)->UnwrapRef();
+                        if (lhs_ty->is_integer_scalar_or_vector()) {
+                            ctx.Replace(bin_op, [bin_op, &s] { return s.IntDivMod(bin_op); });
+                            made_changes = true;
+                        }
+                    }
+                    break;
+                }
                 default:
                     break;
             }
diff --git a/src/tint/transform/builtin_polyfill.h b/src/tint/transform/builtin_polyfill.h
index 7083aa7..f9eb029 100644
--- a/src/tint/transform/builtin_polyfill.h
+++ b/src/tint/transform/builtin_polyfill.h
@@ -63,6 +63,9 @@
         bool first_trailing_bit = false;
         /// Should `insertBits()` be polyfilled?
         Level insert_bits = Level::kNone;
+        /// Should integer scalar / vector divides and modulos be polyfilled to avoid DBZ and
+        /// integer overflows?
+        bool int_div_mod = false;
         /// Should `saturate()` be polyfilled?
         bool saturate = false;
         /// Should `textureSampleBaseClampToEdge()` be polyfilled for texture_2d<f32> textures?
diff --git a/src/tint/transform/builtin_polyfill_test.cc b/src/tint/transform/builtin_polyfill_test.cc
index 1e380d4..87fd9d5 100644
--- a/src/tint/transform/builtin_polyfill_test.cc
+++ b/src/tint/transform/builtin_polyfill_test.cc
@@ -66,8 +66,7 @@
     EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillAcosh(Level::kFull)));
 }
 
-// TODO(crbug.com/tint/1581): Enable once `acosh` is implemented as @const
-TEST_F(BuiltinPolyfillTest, DISABLED_Acosh_ConstantExpression) {
+TEST_F(BuiltinPolyfillTest, Acosh_ConstantExpression) {
     auto* src = R"(
 fn f() {
   let r : f32 = acosh(1.0);
@@ -287,11 +286,10 @@
     EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillAtanh(Level::kFull)));
 }
 
-// TODO(crbug.com/tint/1581): Enable once `atanh` is implemented as @const
-TEST_F(BuiltinPolyfillTest, DISABLED_Atanh_ConstantExpression) {
+TEST_F(BuiltinPolyfillTest, Atanh_ConstantExpression) {
     auto* src = R"(
 fn f() {
-  let r : f32 = atanh(1.23);
+  let r : f32 = atanh(0.23);
 }
 )";
 
@@ -727,8 +725,7 @@
     EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillCountLeadingZeros()));
 }
 
-// TODO(crbug.com/tint/1581): Enable once `countLeadingZeros` is implemented as @const
-TEST_F(BuiltinPolyfillTest, DISABLED_CountLeadingZeros_ConstantExpression) {
+TEST_F(BuiltinPolyfillTest, CountLeadingZeros_ConstantExpression) {
     auto* src = R"(
 fn f() {
   let r : i32 = countLeadingZeros(15i);
@@ -905,8 +902,7 @@
     EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillCountTrailingZeros()));
 }
 
-// TODO(crbug.com/tint/1581): Enable once `countTrailingZeros` is implemented as @const
-TEST_F(BuiltinPolyfillTest, DISABLED_CountTrailingZeros_ConstantExpression) {
+TEST_F(BuiltinPolyfillTest, CountTrailingZeros_ConstantExpression) {
     auto* src = R"(
 fn f() {
   let r : i32 = countTrailingZeros(15i);
@@ -1338,8 +1334,7 @@
     EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillFirstLeadingBit()));
 }
 
-// TODO(crbug.com/tint/1581): Enable once `firstLeadingBit` is implemented as @const
-TEST_F(BuiltinPolyfillTest, DISABLED_FirstLeadingBit_ConstantExpression) {
+TEST_F(BuiltinPolyfillTest, FirstLeadingBit_ConstantExpression) {
     auto* src = R"(
 fn f() {
   let r : i32 = firstLeadingBit(15i);
@@ -1516,8 +1511,7 @@
     EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillFirstTrailingBit()));
 }
 
-// TODO(crbug.com/tint/1581): Enable once `firstTrailingBit` is implemented as @const
-TEST_F(BuiltinPolyfillTest, DISABLED_FirstTrailingBit_ConstantExpression) {
+TEST_F(BuiltinPolyfillTest, FirstTrailingBit_ConstantExpression) {
     auto* src = R"(
 fn f() {
   let r : i32 = firstTrailingBit(15i);
@@ -1696,12 +1690,10 @@
     EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillInsertBits(Level::kFull)));
 }
 
-// TODO(crbug.com/tint/1581): Enable once `insertBits` is implemented as @const
-TEST_F(BuiltinPolyfillTest, DISABLED_InsertBits_ConstantExpression) {
+TEST_F(BuiltinPolyfillTest, InsertBits_ConstantExpression) {
     auto* src = R"(
 fn f() {
-  let v = 1234i;
-  let r : i32 = insertBits(v, 5678, 5u, 6u);
+  let r : i32 = insertBits(1234i, 5678i, 5u, 6u);
 }
 )";
 
@@ -1921,6 +1913,775 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+// int_div_mod
+////////////////////////////////////////////////////////////////////////////////
+DataMap polyfillIntDivMod() {
+    BuiltinPolyfill::Builtins builtins;
+    builtins.int_div_mod = true;
+    DataMap data;
+    data.Add<BuiltinPolyfill::Config>(builtins);
+    return data;
+}
+
+TEST_F(BuiltinPolyfillTest, ShouldRunIntDiv) {
+    auto* src = R"(
+fn f() {
+  let v = 10i;
+  let x = 20i / v;
+}
+)";
+
+    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
+    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillIntDivMod()));
+}
+
+TEST_F(BuiltinPolyfillTest, ShouldRunIntMod) {
+    auto* src = R"(
+fn f() {
+  let v = 10i;
+  let x = 20i % v;
+}
+)";
+
+    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
+    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillIntDivMod()));
+}
+
+TEST_F(BuiltinPolyfillTest, IntDiv_ai_i32) {
+    auto* src = R"(
+fn f() {
+  let v = 10i;
+  let x = 20 / v;
+}
+)";
+
+    auto* expect = R"(
+fn tint_div(lhs : i32, rhs : i32) -> i32 {
+  return (lhs / select(rhs, 1, ((rhs == 0) | ((lhs == -2147483648) & (rhs == -1)))));
+}
+
+fn f() {
+  let v = 10i;
+  let x = tint_div(20, v);
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, IntMod_ai_i32) {
+    auto* src = R"(
+fn f() {
+  let v = 10i;
+  let x = 20 % v;
+}
+)";
+
+    auto* expect = R"(
+fn tint_mod(lhs : i32, rhs : i32) -> i32 {
+  return (lhs % select(rhs, 1, ((rhs == 0) | ((lhs == -2147483648) & (rhs == -1)))));
+}
+
+fn f() {
+  let v = 10i;
+  let x = tint_mod(20, v);
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, IntDiv_i32_ai) {
+    auto* src = R"(
+fn f() {
+  let v = 10i;
+  let x = v / 20;
+}
+)";
+
+    auto* expect = R"(
+fn tint_div(lhs : i32, rhs : i32) -> i32 {
+  return (lhs / select(rhs, 1, ((rhs == 0) | ((lhs == -2147483648) & (rhs == -1)))));
+}
+
+fn f() {
+  let v = 10i;
+  let x = tint_div(v, 20);
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, IntMod_i32_ai) {
+    auto* src = R"(
+fn f() {
+  let v = 10i;
+  let x = v % 20;
+}
+)";
+
+    auto* expect = R"(
+fn tint_mod(lhs : i32, rhs : i32) -> i32 {
+  return (lhs % select(rhs, 1, ((rhs == 0) | ((lhs == -2147483648) & (rhs == -1)))));
+}
+
+fn f() {
+  let v = 10i;
+  let x = tint_mod(v, 20);
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, IntDiv_i32_i32) {
+    auto* src = R"(
+fn f() {
+  let v = 10i;
+  let x = 20i / v;
+}
+)";
+
+    auto* expect = R"(
+fn tint_div(lhs : i32, rhs : i32) -> i32 {
+  return (lhs / select(rhs, 1, ((rhs == 0) | ((lhs == -2147483648) & (rhs == -1)))));
+}
+
+fn f() {
+  let v = 10i;
+  let x = tint_div(20i, v);
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, IntMod_i32_i32) {
+    auto* src = R"(
+fn f() {
+  let v = 10i;
+  let x = 20i % v;
+}
+)";
+
+    auto* expect = R"(
+fn tint_mod(lhs : i32, rhs : i32) -> i32 {
+  return (lhs % select(rhs, 1, ((rhs == 0) | ((lhs == -2147483648) & (rhs == -1)))));
+}
+
+fn f() {
+  let v = 10i;
+  let x = tint_mod(20i, v);
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, IntDiv_ai_u32) {
+    auto* src = R"(
+fn f() {
+  let v = 10u;
+  let x = 20 / v;
+}
+)";
+
+    auto* expect = R"(
+fn tint_div(lhs : u32, rhs : u32) -> u32 {
+  return (lhs / select(rhs, 1, (rhs == 0)));
+}
+
+fn f() {
+  let v = 10u;
+  let x = tint_div(20, v);
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, IntMod_ai_u32) {
+    auto* src = R"(
+fn f() {
+  let v = 10u;
+  let x = 20 % v;
+}
+)";
+
+    auto* expect = R"(
+fn tint_mod(lhs : u32, rhs : u32) -> u32 {
+  return (lhs % select(rhs, 1, (rhs == 0)));
+}
+
+fn f() {
+  let v = 10u;
+  let x = tint_mod(20, v);
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, IntDiv_u32_ai) {
+    auto* src = R"(
+fn f() {
+  let v = 10u;
+  let x = v / 20;
+}
+)";
+
+    auto* expect = R"(
+fn tint_div(lhs : u32, rhs : u32) -> u32 {
+  return (lhs / select(rhs, 1, (rhs == 0)));
+}
+
+fn f() {
+  let v = 10u;
+  let x = tint_div(v, 20);
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, IntMod_u32_ai) {
+    auto* src = R"(
+fn f() {
+  let v = 10u;
+  let x = v % 20;
+}
+)";
+
+    auto* expect = R"(
+fn tint_mod(lhs : u32, rhs : u32) -> u32 {
+  return (lhs % select(rhs, 1, (rhs == 0)));
+}
+
+fn f() {
+  let v = 10u;
+  let x = tint_mod(v, 20);
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, IntDiv_u32_u32) {
+    auto* src = R"(
+fn f() {
+  let v = 10u;
+  let x = 20u / v;
+}
+)";
+
+    auto* expect = R"(
+fn tint_div(lhs : u32, rhs : u32) -> u32 {
+  return (lhs / select(rhs, 1, (rhs == 0)));
+}
+
+fn f() {
+  let v = 10u;
+  let x = tint_div(20u, v);
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, IntMod_u32_u32) {
+    auto* src = R"(
+fn f() {
+  let v = 10u;
+  let x = 20u % v;
+}
+)";
+
+    auto* expect = R"(
+fn tint_mod(lhs : u32, rhs : u32) -> u32 {
+  return (lhs % select(rhs, 1, (rhs == 0)));
+}
+
+fn f() {
+  let v = 10u;
+  let x = tint_mod(20u, v);
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, IntDiv_vec3_ai_i32) {
+    auto* src = R"(
+fn f() {
+  let v = 10i;
+  let x = vec3(20) / v;
+}
+)";
+
+    auto* expect = R"(
+fn tint_div(lhs : vec3<i32>, rhs : i32) -> vec3<i32> {
+  let r = vec3<i32>(rhs);
+  return (lhs / select(r, vec3(1), ((r == vec3(0)) | ((lhs == vec3(-2147483648)) & (r == vec3(-1))))));
+}
+
+fn f() {
+  let v = 10i;
+  let x = tint_div(vec3(20), v);
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, IntMod_vec3_ai_i32) {
+    auto* src = R"(
+fn f() {
+  let v = 10i;
+  let x = vec3(20) % v;
+}
+)";
+
+    auto* expect = R"(
+fn tint_mod(lhs : vec3<i32>, rhs : i32) -> vec3<i32> {
+  let r = vec3<i32>(rhs);
+  return (lhs % select(r, vec3(1), ((r == vec3(0)) | ((lhs == vec3(-2147483648)) & (r == vec3(-1))))));
+}
+
+fn f() {
+  let v = 10i;
+  let x = tint_mod(vec3(20), v);
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, IntDiv_vec3_i32_ai) {
+    auto* src = R"(
+fn f() {
+  let v = 10i;
+  let x = vec3(v) / 20;
+}
+)";
+
+    auto* expect = R"(
+fn tint_div(lhs : vec3<i32>, rhs : i32) -> vec3<i32> {
+  let r = vec3<i32>(rhs);
+  return (lhs / select(r, vec3(1), ((r == vec3(0)) | ((lhs == vec3(-2147483648)) & (r == vec3(-1))))));
+}
+
+fn f() {
+  let v = 10i;
+  let x = tint_div(vec3(v), 20);
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, IntMod_vec3_i32_ai) {
+    auto* src = R"(
+fn f() {
+  let v = 10i;
+  let x = vec3(v) % 20;
+}
+)";
+
+    auto* expect = R"(
+fn tint_mod(lhs : vec3<i32>, rhs : i32) -> vec3<i32> {
+  let r = vec3<i32>(rhs);
+  return (lhs % select(r, vec3(1), ((r == vec3(0)) | ((lhs == vec3(-2147483648)) & (r == vec3(-1))))));
+}
+
+fn f() {
+  let v = 10i;
+  let x = tint_mod(vec3(v), 20);
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, IntDiv_vec3_i32_i32) {
+    auto* src = R"(
+fn f() {
+  let v = 10i;
+  let x = vec3<i32>(20i) / v;
+}
+)";
+
+    auto* expect = R"(
+fn tint_div(lhs : vec3<i32>, rhs : i32) -> vec3<i32> {
+  let r = vec3<i32>(rhs);
+  return (lhs / select(r, vec3(1), ((r == vec3(0)) | ((lhs == vec3(-2147483648)) & (r == vec3(-1))))));
+}
+
+fn f() {
+  let v = 10i;
+  let x = tint_div(vec3<i32>(20i), v);
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, IntMod_vec3_i32_i32) {
+    auto* src = R"(
+fn f() {
+  let v = 10i;
+  let x = vec3<i32>(20i) % v;
+}
+)";
+
+    auto* expect = R"(
+fn tint_mod(lhs : vec3<i32>, rhs : i32) -> vec3<i32> {
+  let r = vec3<i32>(rhs);
+  return (lhs % select(r, vec3(1), ((r == vec3(0)) | ((lhs == vec3(-2147483648)) & (r == vec3(-1))))));
+}
+
+fn f() {
+  let v = 10i;
+  let x = tint_mod(vec3<i32>(20i), v);
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, IntDiv_vec3_u32_u32) {
+    auto* src = R"(
+fn f() {
+  let v = 10u;
+  let x = vec3<u32>(20u) / v;
+}
+)";
+
+    auto* expect = R"(
+fn tint_div(lhs : vec3<u32>, rhs : u32) -> vec3<u32> {
+  let r = vec3<u32>(rhs);
+  return (lhs / select(r, vec3(1), (r == vec3(0))));
+}
+
+fn f() {
+  let v = 10u;
+  let x = tint_div(vec3<u32>(20u), v);
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, IntMod_vec3_u32_u32) {
+    auto* src = R"(
+fn f() {
+  let v = 10u;
+  let x = vec3<u32>(20u) % v;
+}
+)";
+
+    auto* expect = R"(
+fn tint_mod(lhs : vec3<u32>, rhs : u32) -> vec3<u32> {
+  let r = vec3<u32>(rhs);
+  return (lhs % select(r, vec3(1), (r == vec3(0))));
+}
+
+fn f() {
+  let v = 10u;
+  let x = tint_mod(vec3<u32>(20u), v);
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, IntDiv_ai_vec3_i32) {
+    auto* src = R"(
+fn f() {
+  let v = 10i;
+  let x = 20 / vec3(v);
+}
+)";
+
+    auto* expect = R"(
+fn tint_div(lhs : i32, rhs : vec3<i32>) -> vec3<i32> {
+  let l = vec3<i32>(lhs);
+  return (l / select(rhs, vec3(1), ((rhs == vec3(0)) | ((l == vec3(-2147483648)) & (rhs == vec3(-1))))));
+}
+
+fn f() {
+  let v = 10i;
+  let x = tint_div(20, vec3(v));
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, IntMod_ai_vec3_i32) {
+    auto* src = R"(
+fn f() {
+  let v = 10i;
+  let x = 20 % vec3(v);
+}
+)";
+
+    auto* expect = R"(
+fn tint_mod(lhs : i32, rhs : vec3<i32>) -> vec3<i32> {
+  let l = vec3<i32>(lhs);
+  return (l % select(rhs, vec3(1), ((rhs == vec3(0)) | ((l == vec3(-2147483648)) & (rhs == vec3(-1))))));
+}
+
+fn f() {
+  let v = 10i;
+  let x = tint_mod(20, vec3(v));
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, IntDiv_i32_vec3_i32) {
+    auto* src = R"(
+fn f() {
+  let v = 10i;
+  let x = 20i / vec3<i32>(v);
+}
+)";
+
+    auto* expect = R"(
+fn tint_div(lhs : i32, rhs : vec3<i32>) -> vec3<i32> {
+  let l = vec3<i32>(lhs);
+  return (l / select(rhs, vec3(1), ((rhs == vec3(0)) | ((l == vec3(-2147483648)) & (rhs == vec3(-1))))));
+}
+
+fn f() {
+  let v = 10i;
+  let x = tint_div(20i, vec3<i32>(v));
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, IntMod_i32_vec3_i32) {
+    auto* src = R"(
+fn f() {
+  let v = 10i;
+  let x = 20i % vec3<i32>(v);
+}
+)";
+
+    auto* expect = R"(
+fn tint_mod(lhs : i32, rhs : vec3<i32>) -> vec3<i32> {
+  let l = vec3<i32>(lhs);
+  return (l % select(rhs, vec3(1), ((rhs == vec3(0)) | ((l == vec3(-2147483648)) & (rhs == vec3(-1))))));
+}
+
+fn f() {
+  let v = 10i;
+  let x = tint_mod(20i, vec3<i32>(v));
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, IntDiv_u32_vec3_u32) {
+    auto* src = R"(
+fn f() {
+  let v = 10u;
+  let x = 20u / vec3<u32>(v);
+}
+)";
+
+    auto* expect = R"(
+fn tint_div(lhs : u32, rhs : vec3<u32>) -> vec3<u32> {
+  let l = vec3<u32>(lhs);
+  return (l / select(rhs, vec3(1), (rhs == vec3(0))));
+}
+
+fn f() {
+  let v = 10u;
+  let x = tint_div(20u, vec3<u32>(v));
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, IntMod_u32_vec3_u32) {
+    auto* src = R"(
+fn f() {
+  let v = 10u;
+  let x = 20u % vec3<u32>(v);
+}
+)";
+
+    auto* expect = R"(
+fn tint_mod(lhs : u32, rhs : vec3<u32>) -> vec3<u32> {
+  let l = vec3<u32>(lhs);
+  return (l % select(rhs, vec3(1), (rhs == vec3(0))));
+}
+
+fn f() {
+  let v = 10u;
+  let x = tint_mod(20u, vec3<u32>(v));
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, IntDiv_vec3_i32_vec3_i32) {
+    auto* src = R"(
+fn f() {
+  let v = 10i;
+  let x = vec3<i32>(20i) / vec3<i32>(v);
+}
+)";
+
+    auto* expect = R"(
+fn tint_div(lhs : vec3<i32>, rhs : vec3<i32>) -> vec3<i32> {
+  return (lhs / select(rhs, vec3(1), ((rhs == vec3(0)) | ((lhs == vec3(-2147483648)) & (rhs == vec3(-1))))));
+}
+
+fn f() {
+  let v = 10i;
+  let x = tint_div(vec3<i32>(20i), vec3<i32>(v));
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, IntMod_vec3_i32_vec3_i32) {
+    auto* src = R"(
+fn f() {
+  let v = 10i;
+  let x = vec3<i32>(20i) % vec3<i32>(v);
+}
+)";
+
+    auto* expect = R"(
+fn tint_mod(lhs : vec3<i32>, rhs : vec3<i32>) -> vec3<i32> {
+  return (lhs % select(rhs, vec3(1), ((rhs == vec3(0)) | ((lhs == vec3(-2147483648)) & (rhs == vec3(-1))))));
+}
+
+fn f() {
+  let v = 10i;
+  let x = tint_mod(vec3<i32>(20i), vec3<i32>(v));
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, IntDiv_vec3_u32_vec3_u32) {
+    auto* src = R"(
+fn f() {
+  let v = 10u;
+  let x = vec3<u32>(20u) / vec3<u32>(v);
+}
+)";
+
+    auto* expect = R"(
+fn tint_div(lhs : vec3<u32>, rhs : vec3<u32>) -> vec3<u32> {
+  return (lhs / select(rhs, vec3(1), (rhs == vec3(0))));
+}
+
+fn f() {
+  let v = 10u;
+  let x = tint_div(vec3<u32>(20u), vec3<u32>(v));
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, IntMod_vec3_u32_vec3_u32) {
+    auto* src = R"(
+fn f() {
+  let v = 10u;
+  let x = vec3<u32>(20u) % vec3<u32>(v);
+}
+)";
+
+    auto* expect = R"(
+fn tint_mod(lhs : vec3<u32>, rhs : vec3<u32>) -> vec3<u32> {
+  return (lhs % select(rhs, vec3(1), (rhs == vec3(0))));
+}
+
+fn f() {
+  let v = 10u;
+  let x = tint_mod(vec3<u32>(20u), vec3<u32>(v));
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+////////////////////////////////////////////////////////////////////////////////
 // saturate
 ////////////////////////////////////////////////////////////////////////////////
 DataMap polyfillSaturate() {
@@ -2239,5 +3000,37 @@
     EXPECT_EQ(expect, str(got));
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// Polyfill combinations
+////////////////////////////////////////////////////////////////////////////////
+
+TEST_F(BuiltinPolyfillTest, BitshiftAndModulo) {
+    auto* src = R"(
+fn f(x : i32, y : u32, z : u32) {
+    let l = x << (y % z);
+}
+)";
+
+    auto* expect = R"(
+fn tint_mod(lhs : u32, rhs : u32) -> u32 {
+  return (lhs % select(rhs, 1, (rhs == 0)));
+}
+
+fn f(x : i32, y : u32, z : u32) {
+  let l = (x << (tint_mod(y, z) & 31));
+}
+)";
+
+    BuiltinPolyfill::Builtins builtins;
+    builtins.bitshift_modulo = true;
+    builtins.int_div_mod = true;
+    DataMap data;
+    data.Add<BuiltinPolyfill::Config>(builtins);
+
+    auto got = Run<BuiltinPolyfill>(src, std::move(data));
+
+    EXPECT_EQ(expect, str(got));
+}
+
 }  // namespace
 }  // namespace tint::transform
diff --git a/src/tint/transform/demote_to_helper.cc b/src/tint/transform/demote_to_helper.cc
new file mode 100644
index 0000000..cb35c67
--- /dev/null
+++ b/src/tint/transform/demote_to_helper.cc
@@ -0,0 +1,248 @@
+// 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/transform/demote_to_helper.h"
+
+#include <unordered_map>
+#include <unordered_set>
+#include <utility>
+
+#include "src/tint/program_builder.h"
+#include "src/tint/sem/block_statement.h"
+#include "src/tint/sem/call.h"
+#include "src/tint/sem/function.h"
+#include "src/tint/sem/reference.h"
+#include "src/tint/sem/statement.h"
+#include "src/tint/transform/utils/hoist_to_decl_before.h"
+#include "src/tint/utils/map.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::transform::DemoteToHelper);
+
+using namespace tint::number_suffixes;  // NOLINT
+
+namespace tint::transform {
+
+DemoteToHelper::DemoteToHelper() = default;
+
+DemoteToHelper::~DemoteToHelper() = default;
+
+Transform::ApplyResult DemoteToHelper::Apply(const Program* src, const DataMap&, DataMap&) const {
+    auto& sem = src->Sem();
+
+    // Collect the set of functions that need to be processed.
+    // A function needs to be processed if it is reachable by a shader that contains a discard at
+    // any point in its call hierarchy.
+    std::unordered_set<const sem::Function*> functions_to_process;
+    for (auto* func : src->AST().Functions()) {
+        if (!func->IsEntryPoint()) {
+            continue;
+        }
+
+        // Determine whether this entry point and its callees need to be transformed.
+        bool needs_transform = false;
+        if (sem.Get(func)->DiscardStatement()) {
+            needs_transform = true;
+        } else {
+            for (auto* callee : sem.Get(func)->TransitivelyCalledFunctions()) {
+                if (callee->DiscardStatement()) {
+                    needs_transform = true;
+                    break;
+                }
+            }
+        }
+        if (!needs_transform) {
+            continue;
+        }
+
+        // Process the entry point and its callees.
+        functions_to_process.insert(sem.Get(func));
+        for (auto* callee : sem.Get(func)->TransitivelyCalledFunctions()) {
+            functions_to_process.insert(callee);
+        }
+    }
+
+    if (functions_to_process.empty()) {
+        return SkipTransform;
+    }
+
+    ProgramBuilder b;
+    CloneContext ctx{&b, src, /* auto_clone_symbols */ true};
+
+    // Create a module-scope flag that indicates whether the current invocation has been discarded.
+    auto flag = b.Symbols().New("tint_discarded");
+    b.GlobalVar(flag, ast::AddressSpace::kPrivate, b.Expr(false));
+
+    // Replace all discard statements with a statement that marks the invocation as discarded.
+    ctx.ReplaceAll([&](const ast::DiscardStatement*) -> const ast::Statement* {
+        return b.Assign(flag, b.Expr(true));
+    });
+
+    // Insert a conditional discard at the end of each entry point that does not end with a return.
+    for (auto* func : functions_to_process) {
+        if (func->Declaration()->IsEntryPoint()) {
+            auto* sem_body = sem.Get(func->Declaration()->body);
+            if (sem_body->Behaviors().Contains(sem::Behavior::kNext)) {
+                ctx.InsertBack(func->Declaration()->body->statements,
+                               b.If(flag, b.Block(b.Discard())));
+            }
+        }
+    }
+
+    HoistToDeclBefore hoist_to_decl_before(ctx);
+
+    // Mask all writes to host-visible memory using the discarded flag.
+    // We also insert a discard statement before all return statements in entry points for shaders
+    // that discard.
+    std::unordered_map<const sem::Type*, Symbol> atomic_cmpxchg_result_types;
+    for (auto* node : src->ASTNodes().Objects()) {
+        Switch(
+            node,
+
+            // Mask assignments to storage buffer variables.
+            [&](const ast::AssignmentStatement* assign) {
+                // Skip writes in functions that are not called from shaders that discard.
+                auto* func = sem.Get(assign)->Function();
+                if (functions_to_process.count(func) == 0) {
+                    return;
+                }
+
+                // Skip phony assignments.
+                if (assign->lhs->Is<ast::PhonyExpression>()) {
+                    return;
+                }
+
+                // Skip writes to invocation-private address spaces.
+                auto* ref = sem.Get(assign->lhs)->Type()->As<sem::Reference>();
+                switch (ref->AddressSpace()) {
+                    case ast::AddressSpace::kStorage:
+                        // Need to mask these.
+                        break;
+                    case ast::AddressSpace::kFunction:
+                    case ast::AddressSpace::kPrivate:
+                    case ast::AddressSpace::kOut:
+                        // Skip these.
+                        return;
+                    default:
+                        TINT_UNREACHABLE(Transform, b.Diagnostics())
+                            << "write to unhandled address space: " << ref->AddressSpace();
+                }
+
+                // Mask the assignment using the invocation-discarded flag.
+                ctx.Replace(assign, b.If(b.Not(flag), b.Block(ctx.Clone(assign))));
+            },
+
+            // Mask builtins that write to host-visible memory.
+            [&](const ast::CallExpression* call) {
+                auto* sem_call = sem.Get<sem::Call>(call);
+                auto* stmt = sem_call ? sem_call->Stmt() : nullptr;
+                auto* func = stmt ? stmt->Function() : nullptr;
+                auto* builtin = sem_call ? sem_call->Target()->As<sem::Builtin>() : nullptr;
+                if (functions_to_process.count(func) == 0 || !builtin) {
+                    return;
+                }
+
+                if (builtin->Type() == sem::BuiltinType::kTextureStore) {
+                    // A call to textureStore() will always be a statement.
+                    // Wrap it inside a conditional block.
+                    auto* masked_call = b.If(b.Not(flag), b.Block(ctx.Clone(stmt->Declaration())));
+                    ctx.Replace(stmt->Declaration(), masked_call);
+                } else if (builtin->IsAtomic() &&
+                           builtin->Type() != sem::BuiltinType::kAtomicLoad) {
+                    // A call to an atomic builtin can be a statement or an expression.
+                    if (auto* call_stmt = stmt->Declaration()->As<ast::CallStatement>();
+                        call_stmt && call_stmt->expr == call) {
+                        // This call is a statement.
+                        // Wrap it inside a conditional block.
+                        auto* masked_call = b.If(b.Not(flag), b.Block(ctx.Clone(call_stmt)));
+                        ctx.Replace(stmt->Declaration(), masked_call);
+                    } else {
+                        // This call is an expression.
+                        // We transform:
+                        //   let y = x + atomicAdd(&p, 1);
+                        // Into:
+                        //   var tmp : i32;
+                        //   if (!tint_discarded) {
+                        //     tmp = atomicAdd(&p, 1);
+                        //   }
+                        //   let y = x + tmp;
+                        auto result = b.Sym();
+                        const ast::Type* result_ty = nullptr;
+                        const ast::Statement* masked_call = nullptr;
+                        if (builtin->Type() == sem::BuiltinType::kAtomicCompareExchangeWeak) {
+                            // Special case for atomicCompareExchangeWeak as we cannot name its
+                            // result type. We have to declare an equivalent struct and copy the
+                            // original member values over to it.
+
+                            // Declare a struct to hold the result values.
+                            auto* result_struct = sem_call->Type()->As<sem::Struct>();
+                            auto* atomic_ty = result_struct->Members()[0]->Type();
+                            result_ty = b.ty.type_name(
+                                utils::GetOrCreate(atomic_cmpxchg_result_types, atomic_ty, [&]() {
+                                    auto name = b.Sym();
+                                    b.Structure(
+                                        name,
+                                        utils::Vector{
+                                            b.Member("old_value", CreateASTTypeFor(ctx, atomic_ty)),
+                                            b.Member("exchanged", b.ty.bool_()),
+                                        });
+                                    return name;
+                                }));
+
+                            // Generate the masked call and member-wise copy:
+                            //   if (!tint_discarded) {
+                            //     let tmp_result = atomicCompareExchangeWeak(&p, 1, 2);
+                            //     result.exchanged = tmp_result.exchanged;
+                            //     result.old_value = tmp_result.old_value;
+                            //   }
+                            auto tmp_result = b.Sym();
+                            masked_call =
+                                b.If(b.Not(flag),
+                                     b.Block(utils::Vector{
+                                         b.Decl(b.Let(tmp_result, ctx.CloneWithoutTransform(call))),
+                                         b.Assign(b.MemberAccessor(result, "old_value"),
+                                                  b.MemberAccessor(tmp_result, "old_value")),
+                                         b.Assign(b.MemberAccessor(result, "exchanged"),
+                                                  b.MemberAccessor(tmp_result, "exchanged")),
+                                     }));
+                        } else {
+                            result_ty = CreateASTTypeFor(ctx, sem_call->Type());
+                            masked_call =
+                                b.If(b.Not(flag),
+                                     b.Block(b.Assign(result, ctx.CloneWithoutTransform(call))));
+                        }
+                        auto* result_decl = b.Decl(b.Var(result, result_ty));
+                        hoist_to_decl_before.Prepare(sem_call);
+                        hoist_to_decl_before.InsertBefore(stmt, result_decl);
+                        hoist_to_decl_before.InsertBefore(stmt, masked_call);
+                        ctx.Replace(call, b.Expr(result));
+                    }
+                }
+            },
+
+            // Insert a conditional discard before all return statements in entry points.
+            [&](const ast::ReturnStatement* ret) {
+                auto* func = sem.Get(ret)->Function();
+                if (func->Declaration()->IsEntryPoint() && functions_to_process.count(func)) {
+                    auto* discard = b.If(flag, b.Block(b.Discard()));
+                    ctx.InsertBefore(sem.Get(ret)->Block()->Declaration()->statements, ret,
+                                     discard);
+                }
+            });
+    }
+
+    ctx.Clone();
+    return Program(std::move(b));
+}
+
+}  // namespace tint::transform
diff --git a/src/tint/transform/demote_to_helper.h b/src/tint/transform/demote_to_helper.h
new file mode 100644
index 0000000..ba87feb
--- /dev/null
+++ b/src/tint/transform/demote_to_helper.h
@@ -0,0 +1,47 @@
+// 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_TRANSFORM_DEMOTE_TO_HELPER_H_
+#define SRC_TINT_TRANSFORM_DEMOTE_TO_HELPER_H_
+
+#include "src/tint/transform/transform.h"
+
+namespace tint::transform {
+
+/// Implement demote-to-helper semantics for discard statements.
+///
+/// For backend targets that implement discard by terminating the invocation, we need to change the
+/// program to ensure that discarding the fragment does not affect uniformity with respect to
+/// derivative operations. We do this by setting a global flag and masking all writes to storage
+/// buffers and textures.
+///
+/// @note Depends on the following transforms to have been run first:
+/// * PromoteSideEffectsToDecl
+/// * ExpandCompoundAssignment
+class DemoteToHelper final : public Castable<DemoteToHelper, Transform> {
+  public:
+    /// Constructor
+    DemoteToHelper();
+    /// Destructor
+    ~DemoteToHelper() override;
+
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
+};
+
+}  // namespace tint::transform
+
+#endif  // SRC_TINT_TRANSFORM_DEMOTE_TO_HELPER_H_
diff --git a/src/tint/transform/demote_to_helper_test.cc b/src/tint/transform/demote_to_helper_test.cc
new file mode 100644
index 0000000..943da0d
--- /dev/null
+++ b/src/tint/transform/demote_to_helper_test.cc
@@ -0,0 +1,1201 @@
+// 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/transform/demote_to_helper.h"
+
+#include <utility>
+
+#include "src/tint/transform/test_helper.h"
+
+namespace tint::transform {
+namespace {
+
+using DemoteToHelperTest = TransformTest;
+
+TEST_F(DemoteToHelperTest, ShouldRunEmptyModule) {
+    auto* src = R"()";
+
+    EXPECT_FALSE(ShouldRun<DemoteToHelper>(src));
+}
+
+TEST_F(DemoteToHelperTest, ShouldRunNoDiscard) {
+    auto* src = R"(
+@group(0) @binding(0)
+var<storage, read_write> v : f32;
+
+@fragment
+fn foo() {
+  v = 42;
+}
+)";
+
+    EXPECT_FALSE(ShouldRun<DemoteToHelper>(src));
+}
+
+TEST_F(DemoteToHelperTest, ShouldRunDiscardInEntryPoint) {
+    auto* src = R"(
+@group(0) @binding(0)
+var<storage, read_write> v : f32;
+
+@fragment
+fn foo() {
+  discard;
+  v = 42;
+}
+)";
+
+    EXPECT_TRUE(ShouldRun<DemoteToHelper>(src));
+}
+
+TEST_F(DemoteToHelperTest, ShouldRunDiscardInHelper) {
+    auto* src = R"(
+@group(0) @binding(0)
+var<storage, read_write> v : f32;
+
+fn bar() {
+  discard;
+}
+
+@fragment
+fn foo() {
+  bar();
+  v = 42;
+}
+)";
+
+    EXPECT_TRUE(ShouldRun<DemoteToHelper>(src));
+}
+
+TEST_F(DemoteToHelperTest, EmptyModule) {
+    auto* src = R"()";
+
+    auto* expect = src;
+
+    auto got = Run<DemoteToHelper>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(DemoteToHelperTest, WriteInEntryPoint_DiscardInEntryPoint) {
+    auto* src = R"(
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+@group(0) @binding(2) var<storage, read_write> v : f32;
+
+@fragment
+fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
+  if (in == 0.0) {
+    discard;
+  }
+  let ret = textureSample(t, s, coord);
+  v = ret.x;
+}
+)";
+
+    auto* expect = R"(
+var<private> tint_discarded = false;
+
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+@group(0) @binding(2) var<storage, read_write> v : f32;
+
+@fragment
+fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
+  if ((in == 0.0)) {
+    tint_discarded = true;
+  }
+  let ret = textureSample(t, s, coord);
+  if (!(tint_discarded)) {
+    v = ret.x;
+  }
+  if (tint_discarded) {
+    discard;
+  }
+}
+)";
+
+    auto got = Run<DemoteToHelper>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(DemoteToHelperTest, WriteInEntryPoint_DiscardInHelper) {
+    auto* src = R"(
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+@group(0) @binding(2) var<storage, read_write> v : f32;
+
+fn bar() {
+  discard;
+}
+
+@fragment
+fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
+  if (in == 0.0) {
+    bar();
+  }
+  let ret = textureSample(t, s, coord);
+  v = ret.x;
+}
+)";
+
+    auto* expect = R"(
+var<private> tint_discarded = false;
+
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+@group(0) @binding(2) var<storage, read_write> v : f32;
+
+fn bar() {
+  tint_discarded = true;
+}
+
+@fragment
+fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
+  if ((in == 0.0)) {
+    bar();
+  }
+  let ret = textureSample(t, s, coord);
+  if (!(tint_discarded)) {
+    v = ret.x;
+  }
+  if (tint_discarded) {
+    discard;
+  }
+}
+)";
+
+    auto got = Run<DemoteToHelper>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(DemoteToHelperTest, WriteInHelper_DiscardInEntryPoint) {
+    auto* src = R"(
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+@group(0) @binding(2) var<storage, read_write> v : f32;
+
+fn bar(coord : vec2<f32>) {
+  let ret = textureSample(t, s, coord);
+  v = ret.x;
+}
+
+@fragment
+fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
+  if (in == 0.0) {
+    discard;
+  }
+  bar(coord);
+}
+)";
+
+    auto* expect = R"(
+var<private> tint_discarded = false;
+
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+@group(0) @binding(2) var<storage, read_write> v : f32;
+
+fn bar(coord : vec2<f32>) {
+  let ret = textureSample(t, s, coord);
+  if (!(tint_discarded)) {
+    v = ret.x;
+  }
+}
+
+@fragment
+fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
+  if ((in == 0.0)) {
+    tint_discarded = true;
+  }
+  bar(coord);
+  if (tint_discarded) {
+    discard;
+  }
+}
+)";
+
+    auto got = Run<DemoteToHelper>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(DemoteToHelperTest, WriteInHelper_DiscardInHelper) {
+    auto* src = R"(
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+@group(0) @binding(2) var<storage, read_write> v : f32;
+
+fn bar(in : f32, coord : vec2<f32>) {
+  if (in == 0.0) {
+    discard;
+  }
+  let ret = textureSample(t, s, coord);
+  v = ret.x;
+}
+
+@fragment
+fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
+  bar(in, coord);
+}
+)";
+
+    auto* expect = R"(
+var<private> tint_discarded = false;
+
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+@group(0) @binding(2) var<storage, read_write> v : f32;
+
+fn bar(in : f32, coord : vec2<f32>) {
+  if ((in == 0.0)) {
+    tint_discarded = true;
+  }
+  let ret = textureSample(t, s, coord);
+  if (!(tint_discarded)) {
+    v = ret.x;
+  }
+}
+
+@fragment
+fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
+  bar(in, coord);
+  if (tint_discarded) {
+    discard;
+  }
+}
+)";
+
+    auto got = Run<DemoteToHelper>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(DemoteToHelperTest, WriteInEntryPoint_NoDiscard) {
+    auto* src = R"(
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+@group(0) @binding(2) var<storage, read_write> v : f32;
+
+@fragment
+fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
+  let ret = textureSample(t, s, coord);
+  v = ret.x;
+}
+)";
+
+    auto* expect = src;
+
+    auto got = Run<DemoteToHelper>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+// Test that no additional discards are inserted when the function unconditionally returns in a
+// nested block.
+TEST_F(DemoteToHelperTest, EntryPointReturn_NestedInBlock) {
+    auto* src = R"(
+@fragment
+fn foo() {
+  {
+    discard;
+    return;
+  }
+}
+)";
+
+    auto* expect = R"(
+var<private> tint_discarded = false;
+
+@fragment
+fn foo() {
+  {
+    tint_discarded = true;
+    if (tint_discarded) {
+      discard;
+    }
+    return;
+  }
+}
+)";
+
+    auto got = Run<DemoteToHelper>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+// Test that a discard statement is inserted before every return statement in an entry point that
+// contains a discard.
+TEST_F(DemoteToHelperTest, EntryPointReturns_DiscardInEntryPoint) {
+    auto* src = R"(
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+@group(0) @binding(2) var<storage, read_write> v : f32;
+
+@fragment
+fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) f32 {
+  if (in == 0.0) {
+    discard;
+  }
+  let ret = textureSample(t, s, coord);
+  if (in < 1.0) {
+    return ret.x;
+  }
+  return 2.0;
+}
+)";
+
+    auto* expect = R"(
+var<private> tint_discarded = false;
+
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+@group(0) @binding(2) var<storage, read_write> v : f32;
+
+@fragment
+fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) f32 {
+  if ((in == 0.0)) {
+    tint_discarded = true;
+  }
+  let ret = textureSample(t, s, coord);
+  if ((in < 1.0)) {
+    if (tint_discarded) {
+      discard;
+    }
+    return ret.x;
+  }
+  if (tint_discarded) {
+    discard;
+  }
+  return 2.0;
+}
+)";
+
+    auto got = Run<DemoteToHelper>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+// Test that a discard statement is inserted before every return statement in an entry point that
+// calls a function that contains a discard.
+TEST_F(DemoteToHelperTest, EntryPointReturns_DiscardInHelper) {
+    auto* src = R"(
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+@group(0) @binding(2) var<storage, read_write> v : f32;
+
+fn bar() {
+  discard;
+}
+
+@fragment
+fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) f32 {
+  if (in == 0.0) {
+    bar();
+  }
+  let ret = textureSample(t, s, coord);
+  if (in < 1.0) {
+    return ret.x;
+  }
+  return 2.0;
+}
+)";
+
+    auto* expect = R"(
+var<private> tint_discarded = false;
+
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+@group(0) @binding(2) var<storage, read_write> v : f32;
+
+fn bar() {
+  tint_discarded = true;
+}
+
+@fragment
+fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) f32 {
+  if ((in == 0.0)) {
+    bar();
+  }
+  let ret = textureSample(t, s, coord);
+  if ((in < 1.0)) {
+    if (tint_discarded) {
+      discard;
+    }
+    return ret.x;
+  }
+  if (tint_discarded) {
+    discard;
+  }
+  return 2.0;
+}
+)";
+
+    auto got = Run<DemoteToHelper>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+// Test that no return statements are modified in an entry point that does not discard.
+TEST_F(DemoteToHelperTest, EntryPointReturns_NoDiscard) {
+    auto* src = R"(
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+@group(0) @binding(2) var<storage, read_write> v : f32;
+
+fn bar() {
+}
+
+@fragment
+fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) f32 {
+  if ((in == 0.0)) {
+    bar();
+  }
+  let ret = textureSample(t, s, coord);
+  if ((in < 1.0)) {
+    return ret.x;
+  }
+  return 2.0;
+}
+)";
+
+    auto* expect = src;
+
+    auto got = Run<DemoteToHelper>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+// Test that only functions that are part of a shader that discards are transformed.
+// Functions in non-discarding stages should not have their writes masked, and non-discarding entry
+// points should not have their return statements replaced.
+TEST_F(DemoteToHelperTest, MultipleShaders) {
+    auto* src = R"(
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+@group(0) @binding(2) var<storage, read_write> v1 : f32;
+
+@group(0) @binding(3) var<storage, read_write> v2 : f32;
+
+fn bar_discard(in : f32, coord : vec2<f32>) -> f32 {
+  let ret = textureSample(t, s, coord);
+  v1 = ret.x * 2.0;
+  return ret.y * 2.0;
+}
+
+fn bar_no_discard(in : f32, coord : vec2<f32>) -> f32 {
+  let ret = textureSample(t, s, coord);
+  v1 = ret.x * 2.0;
+  return ret.y * 2.0;
+}
+
+@fragment
+fn foo_discard(@location(0) in : f32, @location(1) coord : vec2<f32>) {
+  if (in == 0.0) {
+    discard;
+  }
+  let ret = bar_discard(in, coord);
+  v2 = ret;
+}
+
+@fragment
+fn foo_no_discard(@location(0) in : f32, @location(1) coord : vec2<f32>) {
+  if (in == 0.0) {
+    return;
+  }
+  let ret = bar_no_discard(in, coord);
+  v2 = ret;
+}
+)";
+
+    auto* expect = R"(
+var<private> tint_discarded = false;
+
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+@group(0) @binding(2) var<storage, read_write> v1 : f32;
+
+@group(0) @binding(3) var<storage, read_write> v2 : f32;
+
+fn bar_discard(in : f32, coord : vec2<f32>) -> f32 {
+  let ret = textureSample(t, s, coord);
+  if (!(tint_discarded)) {
+    v1 = (ret.x * 2.0);
+  }
+  return (ret.y * 2.0);
+}
+
+fn bar_no_discard(in : f32, coord : vec2<f32>) -> f32 {
+  let ret = textureSample(t, s, coord);
+  v1 = (ret.x * 2.0);
+  return (ret.y * 2.0);
+}
+
+@fragment
+fn foo_discard(@location(0) in : f32, @location(1) coord : vec2<f32>) {
+  if ((in == 0.0)) {
+    tint_discarded = true;
+  }
+  let ret = bar_discard(in, coord);
+  if (!(tint_discarded)) {
+    v2 = ret;
+  }
+  if (tint_discarded) {
+    discard;
+  }
+}
+
+@fragment
+fn foo_no_discard(@location(0) in : f32, @location(1) coord : vec2<f32>) {
+  if ((in == 0.0)) {
+    return;
+  }
+  let ret = bar_no_discard(in, coord);
+  v2 = ret;
+}
+)";
+
+    auto got = Run<DemoteToHelper>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+// Test that we do not mask writes to invocation-private address spaces.
+TEST_F(DemoteToHelperTest, InvocationPrivateWrites) {
+    auto* src = R"(
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+var<private> vp : f32;
+
+@fragment
+fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
+  if (in == 0.0) {
+    discard;
+  }
+  let ret = textureSample(t, s, coord);
+  var vf : f32;
+  vf = ret.x;
+  vp = ret.y;
+}
+)";
+
+    auto* expect = R"(
+var<private> tint_discarded = false;
+
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+var<private> vp : f32;
+
+@fragment
+fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
+  if ((in == 0.0)) {
+    tint_discarded = true;
+  }
+  let ret = textureSample(t, s, coord);
+  var vf : f32;
+  vf = ret.x;
+  vp = ret.y;
+  if (tint_discarded) {
+    discard;
+  }
+}
+)";
+
+    auto got = Run<DemoteToHelper>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(DemoteToHelperTest, TextureStoreInEntryPoint) {
+    auto* src = R"(
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+@group(0) @binding(2) var<storage, read_write> v : f32;
+
+@group(0) @binding(3) var t2 : texture_storage_2d<rgba8unorm, write>;
+
+@fragment
+fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
+  if (in == 0.0) {
+    discard;
+  }
+  let ret = textureSample(t, s, coord);
+  textureStore(t2, vec2<u32>(coord), ret);
+}
+)";
+
+    auto* expect = R"(
+var<private> tint_discarded = false;
+
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+@group(0) @binding(2) var<storage, read_write> v : f32;
+
+@group(0) @binding(3) var t2 : texture_storage_2d<rgba8unorm, write>;
+
+@fragment
+fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
+  if ((in == 0.0)) {
+    tint_discarded = true;
+  }
+  let ret = textureSample(t, s, coord);
+  if (!(tint_discarded)) {
+    textureStore(t2, vec2<u32>(coord), ret);
+  }
+  if (tint_discarded) {
+    discard;
+  }
+}
+)";
+
+    auto got = Run<DemoteToHelper>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(DemoteToHelperTest, TextureStoreInHelper) {
+    auto* src = R"(
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+@group(0) @binding(2) var<storage, read_write> v : f32;
+
+@group(0) @binding(3) var t2 : texture_storage_2d<rgba8unorm, write>;
+
+fn bar(coord : vec2<f32>, value : vec4<f32>) {
+  textureStore(t2, vec2<u32>(coord), value);
+}
+
+@fragment
+fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
+  if (in == 0.0) {
+    discard;
+  }
+  let ret = textureSample(t, s, coord);
+  bar(coord, ret);
+}
+)";
+
+    auto* expect = R"(
+var<private> tint_discarded = false;
+
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+@group(0) @binding(2) var<storage, read_write> v : f32;
+
+@group(0) @binding(3) var t2 : texture_storage_2d<rgba8unorm, write>;
+
+fn bar(coord : vec2<f32>, value : vec4<f32>) {
+  if (!(tint_discarded)) {
+    textureStore(t2, vec2<u32>(coord), value);
+  }
+}
+
+@fragment
+fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
+  if ((in == 0.0)) {
+    tint_discarded = true;
+  }
+  let ret = textureSample(t, s, coord);
+  bar(coord, ret);
+  if (tint_discarded) {
+    discard;
+  }
+}
+)";
+
+    auto got = Run<DemoteToHelper>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(DemoteToHelperTest, TextureStore_NoDiscard) {
+    auto* src = R"(
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+@group(0) @binding(2) var<storage, read_write> v : f32;
+
+@group(0) @binding(3) var t2 : texture_storage_2d<rgba8unorm, write>;
+
+@fragment
+fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
+  let ret = textureSample(t, s, coord);
+  textureStore(t2, vec2<u32>(coord), ret);
+}
+)";
+
+    auto* expect = src;
+
+    auto got = Run<DemoteToHelper>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(DemoteToHelperTest, AtomicStoreInEntryPoint) {
+    auto* src = R"(
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+@group(0) @binding(2) var<storage, read_write> v : f32;
+
+@group(0) @binding(3) var<storage, read_write> a : atomic<i32>;
+
+@fragment
+fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
+  if (in == 0.0) {
+    discard;
+  }
+  let ret = textureSample(t, s, coord);
+  atomicStore(&a, i32(ret.x));
+}
+)";
+
+    auto* expect = R"(
+var<private> tint_discarded = false;
+
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+@group(0) @binding(2) var<storage, read_write> v : f32;
+
+@group(0) @binding(3) var<storage, read_write> a : atomic<i32>;
+
+@fragment
+fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
+  if ((in == 0.0)) {
+    tint_discarded = true;
+  }
+  let ret = textureSample(t, s, coord);
+  if (!(tint_discarded)) {
+    atomicStore(&(a), i32(ret.x));
+  }
+  if (tint_discarded) {
+    discard;
+  }
+}
+)";
+
+    auto got = Run<DemoteToHelper>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(DemoteToHelperTest, AtomicStoreInHelper) {
+    auto* src = R"(
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+@group(0) @binding(2) var<storage, read_write> v : f32;
+
+@group(0) @binding(3) var<storage, read_write> a : atomic<i32>;
+
+fn bar(value : vec4<f32>) {
+  atomicStore(&a, i32(value.x));
+}
+
+@fragment
+fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
+  if (in == 0.0) {
+    discard;
+  }
+  let ret = textureSample(t, s, coord);
+  bar(ret);
+}
+)";
+
+    auto* expect = R"(
+var<private> tint_discarded = false;
+
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+@group(0) @binding(2) var<storage, read_write> v : f32;
+
+@group(0) @binding(3) var<storage, read_write> a : atomic<i32>;
+
+fn bar(value : vec4<f32>) {
+  if (!(tint_discarded)) {
+    atomicStore(&(a), i32(value.x));
+  }
+}
+
+@fragment
+fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
+  if ((in == 0.0)) {
+    tint_discarded = true;
+  }
+  let ret = textureSample(t, s, coord);
+  bar(ret);
+  if (tint_discarded) {
+    discard;
+  }
+}
+)";
+
+    auto got = Run<DemoteToHelper>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(DemoteToHelperTest, AtomicStore_NoDiscard) {
+    auto* src = R"(
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+@group(0) @binding(2) var<storage, read_write> v : f32;
+
+@group(0) @binding(3) var<storage, read_write> a : atomic<i32>;
+
+@fragment
+fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
+  let ret = textureSample(t, s, coord);
+  atomicStore(&(a), i32(ret.x));
+}
+)";
+
+    auto* expect = src;
+
+    auto got = Run<DemoteToHelper>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(DemoteToHelperTest, AtomicBuiltinExpression) {
+    auto* src = R"(
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+@group(0) @binding(2) var<storage, read_write> v : f32;
+
+@group(0) @binding(3) var<storage, read_write> a : atomic<i32>;
+
+@fragment
+fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) i32 {
+  if (in == 0.0) {
+    discard;
+  }
+  let v = i32(textureSample(t, s, coord).x);
+  let result = v + atomicAdd(&a, v);
+  return result;
+}
+)";
+
+    auto* expect = R"(
+var<private> tint_discarded = false;
+
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+@group(0) @binding(2) var<storage, read_write> v : f32;
+
+@group(0) @binding(3) var<storage, read_write> a : atomic<i32>;
+
+@fragment
+fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) i32 {
+  if ((in == 0.0)) {
+    tint_discarded = true;
+  }
+  let v = i32(textureSample(t, s, coord).x);
+  var tint_symbol : i32;
+  if (!(tint_discarded)) {
+    tint_symbol = atomicAdd(&(a), v);
+  }
+  let result = (v + tint_symbol);
+  if (tint_discarded) {
+    discard;
+  }
+  return result;
+}
+)";
+
+    auto got = Run<DemoteToHelper>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(DemoteToHelperTest, AtomicBuiltinExpression_InForLoopContinuing) {
+    auto* src = R"(
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+@group(0) @binding(2) var<storage, read_write> a : atomic<i32>;
+
+@fragment
+fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) i32 {
+  if (in == 0.0) {
+    discard;
+  }
+  var result = 0;
+  for (var i = 0; i < 10; i = atomicAdd(&a, 1)) {
+    result += i32(textureSample(t, s, coord).x);
+  }
+  return result;
+}
+)";
+
+    auto* expect = R"(
+var<private> tint_discarded = false;
+
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+@group(0) @binding(2) var<storage, read_write> a : atomic<i32>;
+
+@fragment
+fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) i32 {
+  if ((in == 0.0)) {
+    tint_discarded = true;
+  }
+  var result = 0;
+  {
+    var i = 0;
+    loop {
+      if (!((i < 10))) {
+        break;
+      }
+      {
+        result += i32(textureSample(t, s, coord).x);
+      }
+
+      continuing {
+        var tint_symbol : i32;
+        if (!(tint_discarded)) {
+          tint_symbol = atomicAdd(&(a), 1);
+        }
+        i = tint_symbol;
+      }
+    }
+  }
+  if (tint_discarded) {
+    discard;
+  }
+  return result;
+}
+)";
+
+    auto got = Run<DemoteToHelper>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(DemoteToHelperTest, AtomicCompareExchangeWeak) {
+    auto* src = R"(
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+@group(0) @binding(2) var<storage, read_write> a : atomic<i32>;
+
+@fragment
+fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) i32 {
+  if (in == 0.0) {
+    discard;
+  }
+  var result = 0;
+  if (!atomicCompareExchangeWeak(&a, i32(in), 42).exchanged) {
+    let xchg = atomicCompareExchangeWeak(&a, i32(in), 42);
+    result = i32(textureSample(t, s, coord).x) * xchg.old_value;
+  }
+  return result;
+}
+)";
+
+    auto* expect = R"(
+var<private> tint_discarded = false;
+
+struct tint_symbol_1 {
+  old_value : i32,
+  exchanged : bool,
+}
+
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+@group(0) @binding(2) var<storage, read_write> a : atomic<i32>;
+
+@fragment
+fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) i32 {
+  if ((in == 0.0)) {
+    tint_discarded = true;
+  }
+  var result = 0;
+  var tint_symbol : tint_symbol_1;
+  if (!(tint_discarded)) {
+    let tint_symbol_2 = atomicCompareExchangeWeak(&(a), i32(in), 42);
+    tint_symbol.old_value = tint_symbol_2.old_value;
+    tint_symbol.exchanged = tint_symbol_2.exchanged;
+  }
+  if (!(tint_symbol.exchanged)) {
+    var tint_symbol_3 : tint_symbol_1;
+    if (!(tint_discarded)) {
+      let tint_symbol_4 = atomicCompareExchangeWeak(&(a), i32(in), 42);
+      tint_symbol_3.old_value = tint_symbol_4.old_value;
+      tint_symbol_3.exchanged = tint_symbol_4.exchanged;
+    }
+    let xchg = tint_symbol_3;
+    result = (i32(textureSample(t, s, coord).x) * xchg.old_value);
+  }
+  if (tint_discarded) {
+    discard;
+  }
+  return result;
+}
+)";
+
+    auto got = Run<DemoteToHelper>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+// Test that no masking is generated for calls to `atomicLoad()`.
+TEST_F(DemoteToHelperTest, AtomicLoad) {
+    auto* src = R"(
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+@group(0) @binding(2) var<storage, read_write> v : f32;
+
+@group(0) @binding(3) var<storage, read_write> a : atomic<i32>;
+
+@fragment
+fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) i32 {
+  if (in == 0.0) {
+    discard;
+  }
+  let v = i32(textureSample(t, s, coord).x);
+  let result = v + atomicLoad(&a);
+  return result;
+}
+)";
+
+    auto* expect = R"(
+var<private> tint_discarded = false;
+
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+@group(0) @binding(2) var<storage, read_write> v : f32;
+
+@group(0) @binding(3) var<storage, read_write> a : atomic<i32>;
+
+@fragment
+fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) i32 {
+  if ((in == 0.0)) {
+    tint_discarded = true;
+  }
+  let v = i32(textureSample(t, s, coord).x);
+  let result = (v + atomicLoad(&(a)));
+  if (tint_discarded) {
+    discard;
+  }
+  return result;
+}
+)";
+
+    auto got = Run<DemoteToHelper>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(DemoteToHelperTest, PhonyAssignment) {
+    auto* src = R"(
+@fragment
+fn foo(@location(0) in : f32) {
+  if (in == 0.0) {
+    discard;
+  }
+  _ = in;
+}
+)";
+
+    auto* expect = R"(
+var<private> tint_discarded = false;
+
+@fragment
+fn foo(@location(0) in : f32) {
+  if ((in == 0.0)) {
+    tint_discarded = true;
+  }
+  _ = in;
+  if (tint_discarded) {
+    discard;
+  }
+}
+)";
+
+    auto got = Run<DemoteToHelper>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+}  // namespace
+}  // namespace tint::transform
diff --git a/src/tint/transform/for_loop_to_loop_test.cc b/src/tint/transform/for_loop_to_loop_test.cc
index 172e1fc..fa34f56 100644
--- a/src/tint/transform/for_loop_to_loop_test.cc
+++ b/src/tint/transform/for_loop_to_loop_test.cc
@@ -76,7 +76,7 @@
     auto* src = R"(
 fn f() {
   for (;;) {
-    discard;
+    return;
   }
 }
 )";
@@ -84,7 +84,7 @@
     auto* expect = R"(
 fn f() {
   loop {
-    discard;
+    return;
   }
 }
 )";
diff --git a/src/tint/transform/localize_struct_array_assignment.cc b/src/tint/transform/localize_struct_array_assignment.cc
index bfe8865..cb53645 100644
--- a/src/tint/transform/localize_struct_array_assignment.cc
+++ b/src/tint/transform/localize_struct_array_assignment.cc
@@ -174,15 +174,15 @@
     // 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) {
+        auto* root_ident = src->Sem().Get(assign_stmt->lhs)->RootIdentifier();
+        if (!root_ident) {
             TINT_ICE(Transform, b.Diagnostics())
                 << "Unable to determine originating variable for lhs of assignment "
                    "statement";
             return {};
         }
 
-        auto* type = source_var->Type();
+        auto* type = root_ident->Type();
         if (auto* ref = type->As<sem::Reference>()) {
             return {ref->StoreType(), ref->AddressSpace()};
         } else if (auto* ptr = type->As<sem::Pointer>()) {
diff --git a/src/tint/transform/manager.cc b/src/tint/transform/manager.cc
index 79603c8..f93c5d7 100644
--- a/src/tint/transform/manager.cc
+++ b/src/tint/transform/manager.cc
@@ -36,12 +36,16 @@
                                       DataMap& outputs) const {
 #if TINT_PRINT_PROGRAM_FOR_EACH_TRANSFORM
     auto print_program = [&](const char* msg, const Transform* transform) {
-        auto wgsl = Program::printer(in);
-        std::cout << "---------------------------------------------------------" << std::endl;
-        std::cout << "-- " << msg << " " << transform->TypeInfo().name << ":" << std::endl;
-        std::cout << "---------------------------------------------------------" << std::endl;
+        auto wgsl = Program::printer(program);
+        std::cout << "=========================================================" << std::endl;
+        std::cout << "== " << msg << " " << transform->TypeInfo().name << ":" << std::endl;
+        std::cout << "=========================================================" << std::endl;
         std::cout << wgsl << std::endl;
-        std::cout << "---------------------------------------------------------" << std::endl
+        if (!program->IsValid()) {
+            std::cout << "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --" << std::endl;
+            std::cout << program->Diagnostics().str() << std::endl;
+        }
+        std::cout << "=========================================================" << std::endl
                   << std::endl;
     };
 #endif
diff --git a/src/tint/transform/merge_return_test.cc b/src/tint/transform/merge_return_test.cc
index fb2377d..02a565c 100644
--- a/src/tint/transform/merge_return_test.cc
+++ b/src/tint/transform/merge_return_test.cc
@@ -688,7 +688,6 @@
         return 1;
       }
       bar = 6;
-      fallthrough;
     }
     case 2 {
       bar = 5;
@@ -726,7 +725,6 @@
         }
       }
       bar = 6;
-      fallthrough;
     }
     case 2: {
       bar = 5;
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 16a622e..8a69bd3 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
@@ -354,6 +354,7 @@
         for (auto* func_ast : functions_to_process) {
             auto* func_sem = ctx.src->Sem().Get(func_ast);
             bool is_entry_point = func_ast->IsEntryPoint();
+            bool needs_pointer_aliasing = false;
 
             // Map module-scope variables onto their replacement.
             struct NewVar {
@@ -424,6 +425,9 @@
                                                     is_wrapped);
                     } else {
                         ProcessVariableInUserFunction(func_ast, var, new_var_symbol, is_pointer);
+                        if (var->AddressSpace() == ast::AddressSpace::kWorkgroup) {
+                            needs_pointer_aliasing = true;
+                        }
                     }
 
                     // Record the replacement symbol.
@@ -434,6 +438,12 @@
                 ReplaceUsesInFunction(func_ast, var, new_var_symbol, is_pointer, is_wrapped);
             }
 
+            // Allow pointer aliasing if needed.
+            if (needs_pointer_aliasing) {
+                ctx.InsertBack(func_ast->attributes,
+                               ctx.dst->Disable(ast::DisabledValidation::kIgnorePointerAliasing));
+            }
+
             if (!workgroup_parameter_members.IsEmpty()) {
                 // Create the workgroup memory parameter.
                 // The parameter is a struct that contains members for each workgroup variable.
@@ -441,10 +451,12 @@
                     ctx.dst->Structure(ctx.dst->Sym(), std::move(workgroup_parameter_members));
                 auto* param_type =
                     ctx.dst->ty.pointer(ctx.dst->ty.Of(str), ast::AddressSpace::kWorkgroup);
-                auto* disable_validation =
-                    ctx.dst->Disable(ast::DisabledValidation::kEntryPointParameter);
-                auto* param = ctx.dst->Param(workgroup_param(), param_type,
-                                             utils::Vector{disable_validation});
+                auto* param = ctx.dst->Param(
+                    workgroup_param(), param_type,
+                    utils::Vector{
+                        ctx.dst->Disable(ast::DisabledValidation::kEntryPointParameter),
+                        ctx.dst->Disable(ast::DisabledValidation::kIgnoreAddressSpace),
+                    });
                 ctx.InsertFront(func_ast->params, param);
             }
 
diff --git a/src/tint/transform/module_scope_var_to_entry_point_param_test.cc b/src/tint/transform/module_scope_var_to_entry_point_param_test.cc
index 232737b..7ae15be 100644
--- a/src/tint/transform/module_scope_var_to_entry_point_param_test.cc
+++ b/src/tint/transform/module_scope_var_to_entry_point_param_test.cc
@@ -125,12 +125,14 @@
   *(tint_symbol) = (*(tint_symbol) * 2.0);
 }
 
+@internal(disable_validation__ignore_pointer_aliasing)
 fn bar(a : f32, b : f32, @internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_1 : ptr<private, f32>, @internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_2 : ptr<workgroup, f32>) {
   *(tint_symbol_1) = a;
   *(tint_symbol_2) = b;
   zoo(tint_symbol_1);
 }
 
+@internal(disable_validation__ignore_pointer_aliasing)
 fn foo(a : f32, @internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_3 : ptr<private, f32>, @internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_4 : ptr<workgroup, f32>) {
   let b : f32 = 2.0;
   bar(a, b, tint_symbol_3, tint_symbol_4);
@@ -188,6 +190,7 @@
   foo(1.0, &(tint_symbol_5), &(tint_symbol_6));
 }
 
+@internal(disable_validation__ignore_pointer_aliasing)
 fn foo(a : f32, @internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_3 : ptr<private, f32>, @internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_4 : ptr<workgroup, f32>) {
   let b : f32 = 2.0;
   bar(a, b, tint_symbol_3, tint_symbol_4);
@@ -197,6 +200,7 @@
 fn no_uses() {
 }
 
+@internal(disable_validation__ignore_pointer_aliasing)
 fn bar(a : f32, b : f32, @internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_1 : ptr<private, f32>, @internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_2 : ptr<workgroup, f32>) {
   *(tint_symbol_1) = a;
   *(tint_symbol_2) = b;
@@ -325,7 +329,9 @@
     EXPECT_EQ(expect, str(got));
 }
 
-TEST_F(ModuleScopeVarToEntryPointParamTest, FoldAddressOfDeref) {
+// TODO(crbug.com/tint/1758): Requires support for workgroup pointer parameters, which is
+// unsupported until WGSL 1.1
+TEST_F(ModuleScopeVarToEntryPointParamTest, DISABLED_FoldAddressOfDeref) {
     auto* src = R"(
 var<workgroup> v : f32;
 
@@ -364,7 +370,9 @@
     EXPECT_EQ(expect, str(got));
 }
 
-TEST_F(ModuleScopeVarToEntryPointParamTest, FoldAddressOfDeref_OutOfOrder) {
+// TODO(crbug.com/tint/1758): Requires support for workgroup pointer parameters, which is
+// unsupported until WGSL 1.1
+TEST_F(ModuleScopeVarToEntryPointParamTest, DISABLED_FoldAddressOfDeref_OutOfOrder) {
     auto* src = R"(
 @compute @workgroup_size(1)
 fn main() {
@@ -998,7 +1006,7 @@
 }
 
 @compute @workgroup_size(1)
-fn main(@internal(disable_validation__entry_point_parameter) tint_symbol_1 : ptr<workgroup, tint_symbol_2>) {
+fn main(@internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol_1 : ptr<workgroup, tint_symbol_2>) {
   let tint_symbol : ptr<workgroup, mat2x2<f32>> = &((*(tint_symbol_1)).m);
   let x = *(tint_symbol);
 }
@@ -1039,7 +1047,7 @@
 }
 
 @compute @workgroup_size(1)
-fn main(@internal(disable_validation__entry_point_parameter) tint_symbol_1 : ptr<workgroup, tint_symbol_2>) {
+fn main(@internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol_1 : ptr<workgroup, tint_symbol_2>) {
   let tint_symbol : ptr<workgroup, array<S2, 4u>> = &((*(tint_symbol_1)).m);
   let x = *(tint_symbol);
 }
@@ -1080,7 +1088,7 @@
 }
 
 @compute @workgroup_size(1)
-fn main(@internal(disable_validation__entry_point_parameter) tint_symbol_1 : ptr<workgroup, tint_symbol_3>) {
+fn main(@internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol_1 : ptr<workgroup, tint_symbol_3>) {
   let tint_symbol : ptr<workgroup, S> = &((*(tint_symbol_1)).a);
   let tint_symbol_2 : ptr<workgroup, S> = &((*(tint_symbol_1)).b);
   let x = *(tint_symbol);
@@ -1122,7 +1130,7 @@
 }
 
 @compute @workgroup_size(1)
-fn main(@internal(disable_validation__entry_point_parameter) tint_symbol_1 : ptr<workgroup, tint_symbol_3>) {
+fn main(@internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol_1 : ptr<workgroup, tint_symbol_3>) {
   let tint_symbol : ptr<workgroup, S> = &((*(tint_symbol_1)).a);
   let tint_symbol_2 : ptr<workgroup, S> = &((*(tint_symbol_1)).b);
   let x = *(tint_symbol);
diff --git a/src/tint/transform/multiplanar_external_texture.cc b/src/tint/transform/multiplanar_external_texture.cc
index c3ebf4a..0667ae9 100644
--- a/src/tint/transform/multiplanar_external_texture.cc
+++ b/src/tint/transform/multiplanar_external_texture.cc
@@ -211,8 +211,6 @@
                     switch (builtin->Type()) {
                         case sem::BuiltinType::kTextureLoad:
                             return createTextureLoad(call, syms);
-                        case sem::BuiltinType::kTextureSampleLevel:
-                            return createTextureSampleLevel(expr, syms);
                         case sem::BuiltinType::kTextureSampleBaseClampToEdge:
                             return createTextureSampleBaseClampToEdge(expr, syms);
                         default:
@@ -315,15 +313,6 @@
         const ast::CallExpression* plane_0_call = nullptr;
         const ast::CallExpression* plane_1_call = nullptr;
         switch (call_type) {
-            case sem::BuiltinType::kTextureSampleLevel:
-                // TODO(crbug.com/tint/1671): DEPRECATED
-                // textureSampleLevel(plane0, smp, coord, 0.0);
-                single_plane_call = b.Call("textureSampleLevel", "plane0", "smp", "coord", 0_a);
-                // textureSampleLevel(plane0, smp, coord, 0.0);
-                plane_0_call = b.Call("textureSampleLevel", "plane0", "smp", "coord", 0_a);
-                // textureSampleLevel(plane1, smp, coord, 0.0);
-                plane_1_call = b.Call("textureSampleLevel", "plane1", "smp", "coord", 0_a);
-                break;
             case sem::BuiltinType::kTextureSampleBaseClampToEdge:
                 stmts.Push(b.Decl(b.Let(
                     "plane0_dims",
@@ -400,54 +389,6 @@
     }
 
     /// Creates the textureSampleExternal function if needed and returns a call expression to it.
-    /// TODO(crbug.com/tint/1671): DEPRECATED: Replaced with createTextureSampleBaseClampToEdge().
-    /// @param expr the call expression being transformed
-    /// @param syms the expanded symbols to be used in the new call
-    /// @returns a call expression to textureSampleExternal
-    const ast::CallExpression* createTextureSampleLevel(const ast::CallExpression* expr,
-                                                        NewBindingSymbols syms) {
-        const ast::Expression* plane_0_binding_param = ctx.Clone(expr->args[0]);
-
-        if (expr->args.Length() != 3) {
-            TINT_ICE(Transform, b.Diagnostics()) << "expected textureSampleLevel call with a "
-                                                    "texture_external to have 3 parameters, found "
-                                                 << expr->args.Length() << " parameters";
-        }
-
-        // TextureSampleExternal calls the gammaCorrection function, so ensure it
-        // exists.
-        if (!gamma_correction_sym.IsValid()) {
-            createGammaCorrectionFn();
-        }
-
-        if (!texture_sample_external_sym.IsValid()) {
-            texture_sample_external_sym = b.Symbols().New("textureSampleExternal");
-
-            // Emit the textureSampleExternal function.
-            b.Func(
-                texture_sample_external_sym,
-                utils::Vector{
-                    b.Param("plane0", b.ty.sampled_texture(ast::TextureDimension::k2d, b.ty.f32())),
-                    b.Param("plane1", b.ty.sampled_texture(ast::TextureDimension::k2d, b.ty.f32())),
-                    b.Param("smp", b.ty.sampler(ast::SamplerKind::kSampler)),
-                    b.Param("coord", b.ty.vec2(b.ty.f32())),
-                    b.Param("params", b.ty.type_name(params_struct_sym)),
-                },
-                b.ty.vec4(b.ty.f32()),
-                buildTextureBuiltinBody(sem::BuiltinType::kTextureSampleLevel));
-        }
-
-        const ast::IdentifierExpression* exp = b.Expr(texture_sample_external_sym);
-        return b.Call(exp, utils::Vector{
-                               plane_0_binding_param,
-                               b.Expr(syms.plane_1),
-                               ctx.Clone(expr->args[1]),
-                               ctx.Clone(expr->args[2]),
-                               b.Expr(syms.params),
-                           });
-    }
-
-    /// Creates the textureSampleExternal function if needed and returns a call expression to it.
     /// @param expr the call expression being transformed
     /// @param syms the expanded symbols to be used in the new call
     /// @returns a call expression to textureSampleExternal
diff --git a/src/tint/transform/multiplanar_external_texture_test.cc b/src/tint/transform/multiplanar_external_texture_test.cc
index 8f2b014..dbfbc8d 100644
--- a/src/tint/transform/multiplanar_external_texture_test.cc
+++ b/src/tint/transform/multiplanar_external_texture_test.cc
@@ -66,24 +66,6 @@
 }
 
 // Running the transform without passing in data for the new bindings should result in an error.
-TEST_F(MultiplanarExternalTextureTest, ErrorNoPassedData_SampleLevel) {
-    auto* src = R"(
-@group(0) @binding(0) var s : sampler;
-@group(0) @binding(1) var ext_tex : texture_external;
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
-  return textureSampleLevel(ext_tex, s, coord.xy);
-}
-)";
-    auto* expect =
-        R"(error: missing new binding point data for tint::transform::MultiplanarExternalTexture)";
-
-    auto got = Run<MultiplanarExternalTexture>(src);
-    EXPECT_EQ(expect, str(got));
-}
-
-// Running the transform without passing in data for the new bindings should result in an error.
 TEST_F(MultiplanarExternalTextureTest, ErrorNoPassedData_SampleBaseClampToEdge) {
     auto* src = R"(
 @group(0) @binding(0) var s : sampler;
@@ -102,29 +84,6 @@
 }
 
 // Running the transform with incorrect binding data should result in an error.
-TEST_F(MultiplanarExternalTextureTest, ErrorIncorrectBindingPont_SampleLevel) {
-    auto* src = R"(
-@group(0) @binding(0) var s : sampler;
-@group(0) @binding(1) var ext_tex : texture_external;
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
-  return textureSampleLevel(ext_tex, s, coord.xy);
-}
-)";
-
-    auto* expect = R"(error: missing new binding points for texture_external at binding {0,1})";
-
-    DataMap data;
-    // This bindings map specifies 0,0 as the location of the texture_external,
-    // which is incorrect.
-    data.Add<MultiplanarExternalTexture::NewBindingPoints>(
-        MultiplanarExternalTexture::BindingsMap{{{0, 0}, {{0, 1}, {0, 2}}}});
-    auto got = Run<MultiplanarExternalTexture>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
-// Running the transform with incorrect binding data should result in an error.
 TEST_F(MultiplanarExternalTextureTest, ErrorIncorrectBindingPont_SampleBaseClampToEdge) {
     auto* src = R"(
 @group(0) @binding(0) var s : sampler;
@@ -257,82 +216,6 @@
     EXPECT_EQ(expect, str(got));
 }
 
-// Test that the transform works with a textureSampleLevel call.
-TEST_F(MultiplanarExternalTextureTest, BasicTextureSampleLevel) {
-    auto* src = R"(
-@group(0) @binding(0) var s : sampler;
-@group(0) @binding(1) var ext_tex : texture_external;
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
-  return textureSampleLevel(ext_tex, s, coord.xy);
-}
-)";
-
-    auto* expect = R"(
-struct GammaTransferParams {
-  G : f32,
-  A : f32,
-  B : f32,
-  C : f32,
-  D : f32,
-  E : f32,
-  F : f32,
-  padding : u32,
-}
-
-struct ExternalTextureParams {
-  numPlanes : u32,
-  doYuvToRgbConversionOnly : u32,
-  yuvToRgbConversionMatrix : mat3x4<f32>,
-  gammaDecodeParams : GammaTransferParams,
-  gammaEncodeParams : GammaTransferParams,
-  gamutConversionMatrix : mat3x3<f32>,
-}
-
-@group(0) @binding(2) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(3) var<uniform> ext_tex_params : ExternalTextureParams;
-
-@group(0) @binding(0) var s : sampler;
-
-@group(0) @binding(1) var ext_tex : texture_2d<f32>;
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
-  let cond = (abs(v) < vec3<f32>(params.D));
-  let t = (sign(v) * ((params.C * abs(v)) + params.F));
-  let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
-  return select(f, t, cond);
-}
-
-fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
-  var color : vec3<f32>;
-  if ((params.numPlanes == 1)) {
-    color = textureSampleLevel(plane0, smp, coord, 0).rgb;
-  } else {
-    color = (vec4<f32>(textureSampleLevel(plane0, smp, coord, 0).r, textureSampleLevel(plane1, smp, coord, 0).rg, 1) * params.yuvToRgbConversionMatrix);
-  }
-  if ((params.doYuvToRgbConversionOnly == 0)) {
-    color = gammaCorrection(color, params.gammaDecodeParams);
-    color = (params.gamutConversionMatrix * color);
-    color = gammaCorrection(color, params.gammaEncodeParams);
-  }
-  return vec4<f32>(color, 1);
-}
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
-  return textureSampleExternal(ext_tex, ext_tex_plane_1, s, coord.xy, ext_tex_params);
-}
-)";
-
-    DataMap data;
-    data.Add<MultiplanarExternalTexture::NewBindingPoints>(
-        MultiplanarExternalTexture::BindingsMap{{{0, 1}, {{0, 2}, {0, 3}}}});
-    auto got = Run<MultiplanarExternalTexture>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
 // Test that the transform works with a textureSampleBaseClampToEdge call.
 TEST_F(MultiplanarExternalTextureTest, BasicTextureSampleBaseClampToEdge) {
     auto* src = R"(
@@ -415,82 +298,6 @@
     EXPECT_EQ(expect, str(got));
 }
 
-// Test that the transform works with a textureSampleLevel call.
-TEST_F(MultiplanarExternalTextureTest, BasicTextureSampleLevel_OutOfOrder) {
-    auto* src = R"(
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
-  return textureSampleLevel(ext_tex, s, coord.xy);
-}
-
-@group(0) @binding(1) var ext_tex : texture_external;
-@group(0) @binding(0) var s : sampler;
-)";
-
-    auto* expect = R"(
-struct GammaTransferParams {
-  G : f32,
-  A : f32,
-  B : f32,
-  C : f32,
-  D : f32,
-  E : f32,
-  F : f32,
-  padding : u32,
-}
-
-struct ExternalTextureParams {
-  numPlanes : u32,
-  doYuvToRgbConversionOnly : u32,
-  yuvToRgbConversionMatrix : mat3x4<f32>,
-  gammaDecodeParams : GammaTransferParams,
-  gammaEncodeParams : GammaTransferParams,
-  gamutConversionMatrix : mat3x3<f32>,
-}
-
-@group(0) @binding(2) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(3) var<uniform> ext_tex_params : ExternalTextureParams;
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
-  let cond = (abs(v) < vec3<f32>(params.D));
-  let t = (sign(v) * ((params.C * abs(v)) + params.F));
-  let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
-  return select(f, t, cond);
-}
-
-fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
-  var color : vec3<f32>;
-  if ((params.numPlanes == 1)) {
-    color = textureSampleLevel(plane0, smp, coord, 0).rgb;
-  } else {
-    color = (vec4<f32>(textureSampleLevel(plane0, smp, coord, 0).r, textureSampleLevel(plane1, smp, coord, 0).rg, 1) * params.yuvToRgbConversionMatrix);
-  }
-  if ((params.doYuvToRgbConversionOnly == 0)) {
-    color = gammaCorrection(color, params.gammaDecodeParams);
-    color = (params.gamutConversionMatrix * color);
-    color = gammaCorrection(color, params.gammaEncodeParams);
-  }
-  return vec4<f32>(color, 1);
-}
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
-  return textureSampleExternal(ext_tex, ext_tex_plane_1, s, coord.xy, ext_tex_params);
-}
-
-@group(0) @binding(1) var ext_tex : texture_2d<f32>;
-
-@group(0) @binding(0) var s : sampler;
-)";
-
-    DataMap data;
-    data.Add<MultiplanarExternalTexture::NewBindingPoints>(
-        MultiplanarExternalTexture::BindingsMap{{{0, 1}, {{0, 2}, {0, 3}}}});
-    auto got = Run<MultiplanarExternalTexture>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
 // Test that the transform works with a textureSampleBaseClampToEdge call.
 TEST_F(MultiplanarExternalTextureTest, BasicTextureSampleBaseClampToEdge_OutOfOrder) {
     auto* src = R"(
@@ -757,97 +564,6 @@
     EXPECT_EQ(expect, str(got));
 }
 
-// Tests that the transform works with both a textureSampleLevel and textureLoad call.
-TEST_F(MultiplanarExternalTextureTest, TextureSampleAndTextureLoad) {
-    auto* src = R"(
-@group(0) @binding(0) var s : sampler;
-@group(0) @binding(1) var ext_tex : texture_external;
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
-  return textureSampleLevel(ext_tex, s, coord.xy) + textureLoad(ext_tex, vec2<i32>(1, 1));
-}
-)";
-
-    auto* expect = R"(
-struct GammaTransferParams {
-  G : f32,
-  A : f32,
-  B : f32,
-  C : f32,
-  D : f32,
-  E : f32,
-  F : f32,
-  padding : u32,
-}
-
-struct ExternalTextureParams {
-  numPlanes : u32,
-  doYuvToRgbConversionOnly : u32,
-  yuvToRgbConversionMatrix : mat3x4<f32>,
-  gammaDecodeParams : GammaTransferParams,
-  gammaEncodeParams : GammaTransferParams,
-  gamutConversionMatrix : mat3x3<f32>,
-}
-
-@group(0) @binding(2) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(3) var<uniform> ext_tex_params : ExternalTextureParams;
-
-@group(0) @binding(0) var s : sampler;
-
-@group(0) @binding(1) var ext_tex : texture_2d<f32>;
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
-  let cond = (abs(v) < vec3<f32>(params.D));
-  let t = (sign(v) * ((params.C * abs(v)) + params.F));
-  let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
-  return select(f, t, cond);
-}
-
-fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
-  var color : vec3<f32>;
-  if ((params.numPlanes == 1)) {
-    color = textureSampleLevel(plane0, smp, coord, 0).rgb;
-  } else {
-    color = (vec4<f32>(textureSampleLevel(plane0, smp, coord, 0).r, textureSampleLevel(plane1, smp, coord, 0).rg, 1) * params.yuvToRgbConversionMatrix);
-  }
-  if ((params.doYuvToRgbConversionOnly == 0)) {
-    color = gammaCorrection(color, params.gammaDecodeParams);
-    color = (params.gamutConversionMatrix * color);
-    color = gammaCorrection(color, params.gammaEncodeParams);
-  }
-  return vec4<f32>(color, 1);
-}
-
-fn textureLoadExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, coord : vec2<i32>, params : ExternalTextureParams) -> vec4<f32> {
-  var color : vec3<f32>;
-  if ((params.numPlanes == 1)) {
-    color = textureLoad(plane0, coord, 0).rgb;
-  } else {
-    color = (vec4<f32>(textureLoad(plane0, coord, 0).r, textureLoad(plane1, coord, 0).rg, 1) * params.yuvToRgbConversionMatrix);
-  }
-  if ((params.doYuvToRgbConversionOnly == 0)) {
-    color = gammaCorrection(color, params.gammaDecodeParams);
-    color = (params.gamutConversionMatrix * color);
-    color = gammaCorrection(color, params.gammaEncodeParams);
-  }
-  return vec4<f32>(color, 1);
-}
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
-  return (textureSampleExternal(ext_tex, ext_tex_plane_1, s, coord.xy, ext_tex_params) + textureLoadExternal(ext_tex, ext_tex_plane_1, vec2<i32>(1, 1), ext_tex_params));
-}
-)";
-
-    DataMap data;
-    data.Add<MultiplanarExternalTexture::NewBindingPoints>(
-        MultiplanarExternalTexture::BindingsMap{{{0, 1}, {{0, 2}, {0, 3}}}});
-    auto got = Run<MultiplanarExternalTexture>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
 // Tests that the transform works with both a textureSampleBaseClampToEdge and textureLoad call.
 TEST_F(MultiplanarExternalTextureTest, TextureSampleBaseClampToEdgeAndTextureLoad) {
     auto* src = R"(
@@ -945,97 +661,6 @@
     EXPECT_EQ(expect, str(got));
 }
 
-// Tests that the transform works with both a textureSampleLevel and textureLoad call.
-TEST_F(MultiplanarExternalTextureTest, TextureSampleAndTextureLoad_OutOfOrder) {
-    auto* src = R"(
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
-  return textureSampleLevel(ext_tex, s, coord.xy) + textureLoad(ext_tex, vec2<i32>(1, 1));
-}
-
-@group(0) @binding(0) var s : sampler;
-@group(0) @binding(1) var ext_tex : texture_external;
-)";
-
-    auto* expect = R"(
-struct GammaTransferParams {
-  G : f32,
-  A : f32,
-  B : f32,
-  C : f32,
-  D : f32,
-  E : f32,
-  F : f32,
-  padding : u32,
-}
-
-struct ExternalTextureParams {
-  numPlanes : u32,
-  doYuvToRgbConversionOnly : u32,
-  yuvToRgbConversionMatrix : mat3x4<f32>,
-  gammaDecodeParams : GammaTransferParams,
-  gammaEncodeParams : GammaTransferParams,
-  gamutConversionMatrix : mat3x3<f32>,
-}
-
-@group(0) @binding(2) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(3) var<uniform> ext_tex_params : ExternalTextureParams;
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
-  let cond = (abs(v) < vec3<f32>(params.D));
-  let t = (sign(v) * ((params.C * abs(v)) + params.F));
-  let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
-  return select(f, t, cond);
-}
-
-fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
-  var color : vec3<f32>;
-  if ((params.numPlanes == 1)) {
-    color = textureSampleLevel(plane0, smp, coord, 0).rgb;
-  } else {
-    color = (vec4<f32>(textureSampleLevel(plane0, smp, coord, 0).r, textureSampleLevel(plane1, smp, coord, 0).rg, 1) * params.yuvToRgbConversionMatrix);
-  }
-  if ((params.doYuvToRgbConversionOnly == 0)) {
-    color = gammaCorrection(color, params.gammaDecodeParams);
-    color = (params.gamutConversionMatrix * color);
-    color = gammaCorrection(color, params.gammaEncodeParams);
-  }
-  return vec4<f32>(color, 1);
-}
-
-fn textureLoadExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, coord : vec2<i32>, params : ExternalTextureParams) -> vec4<f32> {
-  var color : vec3<f32>;
-  if ((params.numPlanes == 1)) {
-    color = textureLoad(plane0, coord, 0).rgb;
-  } else {
-    color = (vec4<f32>(textureLoad(plane0, coord, 0).r, textureLoad(plane1, coord, 0).rg, 1) * params.yuvToRgbConversionMatrix);
-  }
-  if ((params.doYuvToRgbConversionOnly == 0)) {
-    color = gammaCorrection(color, params.gammaDecodeParams);
-    color = (params.gamutConversionMatrix * color);
-    color = gammaCorrection(color, params.gammaEncodeParams);
-  }
-  return vec4<f32>(color, 1);
-}
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
-  return (textureSampleExternal(ext_tex, ext_tex_plane_1, s, coord.xy, ext_tex_params) + textureLoadExternal(ext_tex, ext_tex_plane_1, vec2<i32>(1, 1), ext_tex_params));
-}
-
-@group(0) @binding(0) var s : sampler;
-
-@group(0) @binding(1) var ext_tex : texture_2d<f32>;
-)";
-
-    DataMap data;
-    data.Add<MultiplanarExternalTexture::NewBindingPoints>(
-        MultiplanarExternalTexture::BindingsMap{{{0, 1}, {{0, 2}, {0, 3}}}});
-    auto got = Run<MultiplanarExternalTexture>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
 // Tests that the transform works with both a textureSampleBaseClampToEdge and textureLoad call.
 TEST_F(MultiplanarExternalTextureTest, TextureSampleBaseClampToEdgeAndTextureLoad_OutOfOrder) {
     auto* src = R"(
@@ -1134,107 +759,6 @@
 }
 
 // Tests that the transform works with many instances of texture_external.
-TEST_F(MultiplanarExternalTextureTest, ManyTextureSampleLevel) {
-    auto* src = R"(
-@group(0) @binding(0) var s : sampler;
-@group(0) @binding(1) var ext_tex : texture_external;
-@group(0) @binding(2) var ext_tex_1 : texture_external;
-@group(0) @binding(3) var ext_tex_2 : texture_external;
-@group(1) @binding(0) var ext_tex_3 : texture_external;
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
-  return textureSampleLevel(ext_tex, s, coord.xy) + textureSampleLevel(ext_tex_1, s, coord.xy) + textureSampleLevel(ext_tex_2, s, coord.xy) + textureSampleLevel(ext_tex_3, s, coord.xy);
-}
-)";
-
-    auto* expect = R"(
-struct GammaTransferParams {
-  G : f32,
-  A : f32,
-  B : f32,
-  C : f32,
-  D : f32,
-  E : f32,
-  F : f32,
-  padding : u32,
-}
-
-struct ExternalTextureParams {
-  numPlanes : u32,
-  doYuvToRgbConversionOnly : u32,
-  yuvToRgbConversionMatrix : mat3x4<f32>,
-  gammaDecodeParams : GammaTransferParams,
-  gammaEncodeParams : GammaTransferParams,
-  gamutConversionMatrix : mat3x3<f32>,
-}
-
-@group(0) @binding(4) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(5) var<uniform> ext_tex_params : ExternalTextureParams;
-
-@group(0) @binding(6) var ext_tex_plane_1_1 : texture_2d<f32>;
-
-@group(0) @binding(7) var<uniform> ext_tex_params_1 : ExternalTextureParams;
-
-@group(0) @binding(8) var ext_tex_plane_1_2 : texture_2d<f32>;
-
-@group(0) @binding(9) var<uniform> ext_tex_params_2 : ExternalTextureParams;
-
-@group(1) @binding(1) var ext_tex_plane_1_3 : texture_2d<f32>;
-
-@group(1) @binding(2) var<uniform> ext_tex_params_3 : ExternalTextureParams;
-
-@group(0) @binding(0) var s : sampler;
-
-@group(0) @binding(1) var ext_tex : texture_2d<f32>;
-
-@group(0) @binding(2) var ext_tex_1 : texture_2d<f32>;
-
-@group(0) @binding(3) var ext_tex_2 : texture_2d<f32>;
-
-@group(1) @binding(0) var ext_tex_3 : texture_2d<f32>;
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
-  let cond = (abs(v) < vec3<f32>(params.D));
-  let t = (sign(v) * ((params.C * abs(v)) + params.F));
-  let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
-  return select(f, t, cond);
-}
-
-fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
-  var color : vec3<f32>;
-  if ((params.numPlanes == 1)) {
-    color = textureSampleLevel(plane0, smp, coord, 0).rgb;
-  } else {
-    color = (vec4<f32>(textureSampleLevel(plane0, smp, coord, 0).r, textureSampleLevel(plane1, smp, coord, 0).rg, 1) * params.yuvToRgbConversionMatrix);
-  }
-  if ((params.doYuvToRgbConversionOnly == 0)) {
-    color = gammaCorrection(color, params.gammaDecodeParams);
-    color = (params.gamutConversionMatrix * color);
-    color = gammaCorrection(color, params.gammaEncodeParams);
-  }
-  return vec4<f32>(color, 1);
-}
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
-  return (((textureSampleExternal(ext_tex, ext_tex_plane_1, s, coord.xy, ext_tex_params) + textureSampleExternal(ext_tex_1, ext_tex_plane_1_1, s, coord.xy, ext_tex_params_1)) + textureSampleExternal(ext_tex_2, ext_tex_plane_1_2, s, coord.xy, ext_tex_params_2)) + textureSampleExternal(ext_tex_3, ext_tex_plane_1_3, s, coord.xy, ext_tex_params_3));
-}
-)";
-
-    DataMap data;
-    data.Add<MultiplanarExternalTexture::NewBindingPoints>(MultiplanarExternalTexture::BindingsMap{
-        {{0, 1}, {{0, 4}, {0, 5}}},
-        {{0, 2}, {{0, 6}, {0, 7}}},
-        {{0, 3}, {{0, 8}, {0, 9}}},
-        {{1, 0}, {{1, 1}, {1, 2}}},
-    });
-    auto got = Run<MultiplanarExternalTexture>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
-// Tests that the transform works with many instances of texture_external.
 TEST_F(MultiplanarExternalTextureTest, ManyTextureSampleBaseClampToEdge) {
     auto* src = R"(
 @group(0) @binding(0) var s : sampler;
diff --git a/src/tint/transform/remove_unreachable_statements_test.cc b/src/tint/transform/remove_unreachable_statements_test.cc
index 4b0a265..9267e6f 100644
--- a/src/tint/transform/remove_unreachable_statements_test.cc
+++ b/src/tint/transform/remove_unreachable_statements_test.cc
@@ -113,187 +113,23 @@
     EXPECT_EQ(expect, str(got));
 }
 
+// Discard has "demote-to-helper" semantics, and so code following a discard statement is not
+// considered unreachable.
 TEST_F(RemoveUnreachableStatementsTest, Discard) {
     auto* src = R"(
 fn f() {
   discard;
-  var remove_me = 1;
-  if (true) {
-    var remove_me_too = 1;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  discard;
-}
-)";
-
-    auto got = Run<RemoveUnreachableStatements>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemoveUnreachableStatementsTest, NestedDiscard) {
-    auto* src = R"(
-fn f() {
-  {
-    {
-      discard;
-    }
-  }
-  var remove_me = 1;
-  if (true) {
-    var remove_me_too = 1;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  {
-    {
-      discard;
-    }
-  }
-}
-)";
-
-    auto got = Run<RemoveUnreachableStatements>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemoveUnreachableStatementsTest, CallToFuncWithDiscard) {
-    auto* src = R"(
-fn DISCARD() {
-  discard;
-}
-
-fn f() {
-  DISCARD();
-  var remove_me = 1;
-  if (true) {
-    var remove_me_too = 1;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn DISCARD() {
-  discard;
-}
-
-fn f() {
-  DISCARD();
-}
-)";
-
-    auto got = Run<RemoveUnreachableStatements>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemoveUnreachableStatementsTest, CallToFuncWithIfDiscard) {
-    auto* src = R"(
-fn DISCARD() {
-  if (true) {
-    discard;
-  }
-}
-
-fn f() {
-  DISCARD();
   var preserve_me = 1;
-  if (true) {
-    var preserve_me_too = 1;
-  }
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<RemoveUnreachableStatements>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemoveUnreachableStatementsTest, IfDiscardElseDiscard) {
-    auto* src = R"(
-fn f() {
-  if (true) {
-    discard;
-  } else {
-    discard;
-  }
-  var remove_me = 1;
-  if (true) {
-    var remove_me_too = 1;
-  }
 }
 )";
 
     auto* expect = R"(
 fn f() {
-  if (true) {
-    discard;
-  } else {
-    discard;
-  }
-}
-)";
-
-    auto got = Run<RemoveUnreachableStatements>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemoveUnreachableStatementsTest, IfDiscardElseReturn) {
-    auto* src = R"(
-fn f() {
-  if (true) {
-    discard;
-  } else {
-    return;
-  }
-  var remove_me = 1;
-  if (true) {
-    var remove_me_too = 1;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  if (true) {
-    discard;
-  } else {
-    return;
-  }
-}
-)";
-
-    auto got = Run<RemoveUnreachableStatements>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemoveUnreachableStatementsTest, IfDiscard) {
-    auto* src = R"(
-fn f() {
-  if (true) {
-    discard;
-  }
+  discard;
   var preserve_me = 1;
-  if (true) {
-    var preserve_me_too = 1;
-  }
 }
 )";
 
-    auto* expect = src;
-
     auto got = Run<RemoveUnreachableStatements>(src);
 
     EXPECT_EQ(expect, str(got));
@@ -319,27 +155,6 @@
     EXPECT_EQ(expect, str(got));
 }
 
-TEST_F(RemoveUnreachableStatementsTest, IfElseDiscard) {
-    auto* src = R"(
-fn f() {
-  if (true) {
-  } else {
-    discard;
-  }
-  var preserve_me = 1;
-  if (true) {
-    var preserve_me_too = 1;
-  }
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<RemoveUnreachableStatements>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
 TEST_F(RemoveUnreachableStatementsTest, IfElseReturn) {
     auto* src = R"(
 fn f() {
@@ -361,42 +176,6 @@
     EXPECT_EQ(expect, str(got));
 }
 
-TEST_F(RemoveUnreachableStatementsTest, LoopWithDiscard) {
-    auto* src = R"(
-fn f() {
-  loop {
-    var a = 1;
-    discard;
-
-    continuing {
-      var b = 2;
-    }
-  }
-  var remove_me = 1;
-  if (true) {
-    var remove_me_too = 1;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  loop {
-    var a = 1;
-    discard;
-
-    continuing {
-      var b = 2;
-    }
-  }
-}
-)";
-
-    auto got = Run<RemoveUnreachableStatements>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
 TEST_F(RemoveUnreachableStatementsTest, LoopWithConditionalBreak) {
     auto* src = R"(
 fn f() {
@@ -430,100 +209,7 @@
   loop {
 
     continuing {
-      if (true) {
-        break;
-      }
-    }
-  }
-  var preserve_me = 1;
-  if (true) {
-    var preserve_me_too = 1;
-  }
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<RemoveUnreachableStatements>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemoveUnreachableStatementsTest, SwitchDefaultDiscard) {
-    auto* src = R"(
-fn f() {
-  switch(1) {
-    default: {
-      discard;
-    }
-  }
-  var remove_me = 1;
-  if (true) {
-    var remove_me_too = 1;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  switch(1) {
-    default: {
-      discard;
-    }
-  }
-}
-)";
-
-    auto got = Run<RemoveUnreachableStatements>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemoveUnreachableStatementsTest, SwitchCaseReturnDefaultDiscard) {
-    auto* src = R"(
-fn f() {
-  switch(1) {
-    case 0: {
-      return;
-    }
-    default: {
-      discard;
-    }
-  }
-  var remove_me = 1;
-  if (true) {
-    var remove_me_too = 1;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  switch(1) {
-    case 0: {
-      return;
-    }
-    default: {
-      discard;
-    }
-  }
-}
-)";
-
-    auto got = Run<RemoveUnreachableStatements>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemoveUnreachableStatementsTest, SwitchCaseBreakDefaultDiscard) {
-    auto* src = R"(
-fn f() {
-  switch(1) {
-    case 0: {
-      break;
-    }
-    default: {
-      discard;
+      break if true;
     }
   }
   var preserve_me = 1;
diff --git a/src/tint/transform/single_entry_point.cc b/src/tint/transform/single_entry_point.cc
index 87787ae..386631e 100644
--- a/src/tint/transform/single_entry_point.cc
+++ b/src/tint/transform/single_entry_point.cc
@@ -61,12 +61,7 @@
     }
 
     auto& sem = src->Sem();
-
-    // Build set of referenced module-scope variables for faster lookups later.
-    std::unordered_set<const ast::Variable*> referenced_vars;
-    for (auto* var : sem.Get(entry_point)->TransitivelyReferencedGlobals()) {
-        referenced_vars.emplace(var->Declaration());
-    }
+    auto& referenced_vars = sem.Get(entry_point)->TransitivelyReferencedGlobals();
 
     // Clone any module-scope variables, types, and functions that are statically referenced by the
     // target entry point.
@@ -74,11 +69,20 @@
         Switch(
             decl,  //
             [&](const ast::TypeDecl* ty) {
-                // TODO(jrprice): Strip unused types.
+                // Strip aliases that reference unused override declarations.
+                if (auto* arr = sem.Get(ty)->As<sem::Array>()) {
+                    for (auto* o : arr->TransitivelyReferencedOverrides()) {
+                        if (!referenced_vars.Contains(o)) {
+                            return;
+                        }
+                    }
+                }
+
+                // TODO(jrprice): Strip other unused types.
                 b.AST().AddTypeDecl(ctx.Clone(ty));
             },
             [&](const ast::Override* override) {
-                if (referenced_vars.count(override)) {
+                if (referenced_vars.Contains(sem.Get(override))) {
                     if (!ast::HasAttribute<ast::IdAttribute>(override->attributes)) {
                         // If the override doesn't already have an @id() attribute, add one
                         // so that its allocated ID so that it won't be affected by other
@@ -91,7 +95,7 @@
                 }
             },
             [&](const ast::Var* var) {
-                if (referenced_vars.count(var)) {
+                if (referenced_vars.Contains(sem.Get<sem::GlobalVariable>(var))) {
                     b.AST().AddGlobalVariable(ctx.Clone(var));
                 }
             },
diff --git a/src/tint/transform/single_entry_point_test.cc b/src/tint/transform/single_entry_point_test.cc
index e9fa68f..b225008 100644
--- a/src/tint/transform/single_entry_point_test.cc
+++ b/src/tint/transform/single_entry_point_test.cc
@@ -374,6 +374,78 @@
     }
 }
 
+TEST_F(SingleEntryPointTest, OverridableConstants_TransitiveUses) {
+    // Make sure we do not strip away transitive uses of overridable constants.
+    auto* src = R"(
+@id(0) override c0 : u32;
+
+@id(1) override c1 : u32 = (2 * c0);
+
+@id(2) override c2 : u32;
+
+@id(3) override c3 : u32 = (2 * c2);
+
+@id(4) override c4 : u32;
+
+@id(5) override c5 : u32 = (2 * c4);
+
+type arr_ty = array<i32, (2 * c5)>;
+
+var<workgroup> arr : arr_ty;
+
+@compute @workgroup_size(1, 1, (2 * c3))
+fn main() {
+  let local_d = c1;
+  arr[0] = 42;
+}
+)";
+
+    auto* expect = src;
+
+    SingleEntryPoint::Config cfg("main");
+    DataMap data;
+    data.Add<SingleEntryPoint::Config>(cfg);
+    auto got = Run<SingleEntryPoint>(src, data);
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SingleEntryPointTest, OverridableConstants_UnusedAliasForOverrideSizedArray) {
+    // Make sure we strip away aliases that reference unused overridable constants.
+    auto* src = R"(
+@id(0) override c0 : u32;
+
+// This is all unused by the target entry point.
+@id(1) override c1 : u32;
+type arr_ty = array<i32, c1>;
+var<workgroup> arr : arr_ty;
+
+@compute @workgroup_size(64)
+fn unused() {
+  arr[0] = 42;
+}
+
+@compute @workgroup_size(64)
+fn main() {
+  let local_d = c0;
+}
+)";
+
+    auto* expect = R"(
+@id(0) override c0 : u32;
+
+@compute @workgroup_size(64)
+fn main() {
+  let local_d = c0;
+}
+)";
+
+    SingleEntryPoint::Config cfg("main");
+    DataMap data;
+    data.Add<SingleEntryPoint::Config>(cfg);
+    auto got = Run<SingleEntryPoint>(src, data);
+    EXPECT_EQ(expect, str(got));
+}
+
 TEST_F(SingleEntryPointTest, CalledFunctions) {
     auto* src = R"(
 fn inner1() {
diff --git a/src/tint/transform/spirv_atomic.cc b/src/tint/transform/spirv_atomic.cc
index eba6681..319975a 100644
--- a/src/tint/transform/spirv_atomic.cc
+++ b/src/tint/transform/spirv_atomic.cc
@@ -97,7 +97,7 @@
                                     b.Call(sem::str(stub->builtin), std::move(out_args)));
                     }
 
-                    // Keep track of this expression. We'll need to modify the source variable /
+                    // Keep track of this expression. We'll need to modify the root identifier /
                     // structure to be atomic.
                     atomic_expressions.Add(ctx.src->Sem().Get(args[0]));
                 }
@@ -230,8 +230,8 @@
     void ReplaceLoadsAndStores() {
         // Returns true if 'e' is a reference to an atomic variable or struct member
         auto is_ref_to_atomic_var = [&](const sem::Expression* e) {
-            if (tint::Is<sem::Reference>(e->Type()) && e->SourceVariable() &&
-                (atomic_variables.count(e->SourceVariable()) != 0)) {
+            if (tint::Is<sem::Reference>(e->Type()) && e->RootIdentifier() &&
+                (atomic_variables.count(e->RootIdentifier()) != 0)) {
                 // If it's a struct member, make sure it's one we marked as atomic
                 if (auto* ma = e->As<sem::StructMemberAccess>()) {
                     auto it = forked_structs.find(ma->Member()->Struct()->Declaration());
diff --git a/src/tint/transform/std140.cc b/src/tint/transform/std140.cc
index 0746bda..2116b0f 100644
--- a/src/tint/transform/std140.cc
+++ b/src/tint/transform/std140.cc
@@ -498,26 +498,26 @@
 
         AccessChain access;
 
-        // Start by looking at the source variable. This must be a std140-forked uniform buffer.
-        access.var = tint::As<sem::GlobalVariable>(expr->SourceVariable());
+        // Start by looking at the root identifier. This must be a std140-forked uniform buffer.
+        access.var = tint::As<sem::GlobalVariable>(expr->RootIdentifier());
         if (!access.var || !std140_uniforms.Contains(access.var)) {
             // Not at std140-forked uniform buffer access chain.
             return std::nullopt;
         }
 
-        // Walk from the outer-most expression, inwards towards the source variable.
+        // Walk from the outer-most expression, inwards towards the root identifier.
         while (true) {
             enum class Action { kStop, kContinue, kError };
             Action action = Switch(
                 expr,  //
                 [&](const sem::VariableUser* user) {
                     if (user->Variable() == access.var) {
-                        // Walked all the way to the source variable. We're done traversing.
+                        // Walked all the way to the root identifier. We're done traversing.
                         access.indices.Push(UniformVariable{});
                         return Action::kStop;
                     }
                     if (user->Variable()->Type()->Is<sem::Pointer>()) {
-                        // Found a pointer. As the source variable is a uniform buffer variable,
+                        // Found a pointer. As the root identifier is a uniform buffer variable,
                         // this must be a pointer-let. Continue traversing from the let
                         // initializer.
                         expr = user->Variable()->Initializer();
diff --git a/src/tint/transform/transform.cc b/src/tint/transform/transform.cc
index c37f3b4..5c8357c 100644
--- a/src/tint/transform/transform.cc
+++ b/src/tint/transform/transform.cc
@@ -114,6 +114,19 @@
             return ctx.dst->ty.array(el, count, std::move(attrs));
         }
         if (auto* override = std::get_if<sem::UnnamedOverrideArrayCount>(&a->Count())) {
+            // If the array count is an unnamed (complex) override expression, then its not safe to
+            // redeclare this type as we'd end up with two types that would not compare equal.
+            // See crbug.com/tint/1764.
+            // Look for a type alias for this array.
+            for (auto* type_decl : ctx.src->AST().TypeDecls()) {
+                if (auto* alias = type_decl->As<ast::Alias>()) {
+                    if (ty == ctx.src->Sem().Get(alias)) {
+                        // Alias found. Use the alias name to ensure types compare equal.
+                        return ctx.dst->ty.type_name(ctx.Clone(alias->name));
+                    }
+                }
+            }
+            // Array is not aliased. Rebuild the array.
             auto* count = ctx.Clone(override->expr->Declaration());
             return ctx.dst->ty.array(el, count, std::move(attrs));
         }
diff --git a/src/tint/transform/transform_test.cc b/src/tint/transform/transform_test.cc
index 82fdf6a..4b8ad53 100644
--- a/src/tint/transform/transform_test.cc
+++ b/src/tint/transform/transform_test.cc
@@ -21,6 +21,8 @@
 namespace tint::transform {
 namespace {
 
+using namespace tint::number_suffixes;  // NOLINT
+
 // Inherit from Transform so we have access to protected methods
 struct CreateASTTypeForTest : public testing::Test, public Transform {
     ApplyResult Apply(const Program*, const DataMap&, DataMap&) const override {
@@ -95,6 +97,28 @@
     EXPECT_EQ(size->value, 2);
 }
 
+// crbug.com/tint/1764
+TEST_F(CreateASTTypeForTest, AliasedArrayWithComplexOverrideLength) {
+    // override O = 123;
+    // type A = array<i32, O*2>;
+    //
+    // var<workgroup> W : A;
+    //
+    ProgramBuilder b;
+    auto* arr_len = b.Mul("O", 2_a);
+    b.Override("O", b.Expr(123_a));
+    auto* alias = b.Alias("A", b.ty.array(b.ty.i32(), arr_len));
+
+    Program program(std::move(b));
+
+    auto* arr_ty = program.Sem().Get(alias);
+
+    CloneContext ctx(&ast_type_builder, &program, false);
+    auto* ast_ty = tint::As<ast::TypeName>(CreateASTTypeFor(ctx, arr_ty));
+    ASSERT_NE(ast_ty, nullptr);
+    EXPECT_EQ(ast_type_builder.Symbols().NameFor(ast_ty->name), "A");
+}
+
 TEST_F(CreateASTTypeForTest, Struct) {
     auto* str = create([](ProgramBuilder& b) {
         auto* decl = b.Structure("S", {});
diff --git a/src/tint/transform/truncate_interstage_variables.cc b/src/tint/transform/truncate_interstage_variables.cc
new file mode 100644
index 0000000..a5e7256
--- /dev/null
+++ b/src/tint/transform/truncate_interstage_variables.cc
@@ -0,0 +1,199 @@
+// 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/transform/truncate_interstage_variables.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "src/tint/program_builder.h"
+#include "src/tint/sem/call.h"
+#include "src/tint/sem/function.h"
+#include "src/tint/sem/member_accessor_expression.h"
+#include "src/tint/sem/statement.h"
+#include "src/tint/sem/variable.h"
+#include "src/tint/text/unicode.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::transform::TruncateInterstageVariables);
+TINT_INSTANTIATE_TYPEINFO(tint::transform::TruncateInterstageVariables::Config);
+
+namespace tint::transform {
+
+namespace {
+
+struct TruncatedStructAndConverter {
+    /// The symbol of the truncated structure.
+    Symbol truncated_struct;
+    /// The symbol of the helper function that takes the original structure as a single argument and
+    /// returns the truncated structure type.
+    Symbol truncate_fn;
+};
+
+}  // anonymous namespace
+
+TruncateInterstageVariables::TruncateInterstageVariables() = default;
+TruncateInterstageVariables::~TruncateInterstageVariables() = default;
+
+Transform::ApplyResult TruncateInterstageVariables::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 == nullptr) {
+        b.Diagnostics().add_error(
+            diag::System::Transform,
+            "missing transform data for " +
+                std::string(TypeInfo::Of<TruncateInterstageVariables>().name));
+        return Program(std::move(b));
+    }
+
+    auto& sem = ctx.src->Sem();
+
+    bool should_run = false;
+
+    utils::Hashmap<const sem::Function*, Symbol, 4u> entry_point_functions_to_truncate_functions;
+    utils::Hashmap<const sem::Struct*, TruncatedStructAndConverter, 4u>
+        old_shader_io_structs_to_new_struct_and_truncate_functions;
+
+    for (auto* func_ast : ctx.src->AST().Functions()) {
+        if (!func_ast->IsEntryPoint()) {
+            continue;
+        }
+
+        if (func_ast->PipelineStage() != ast::PipelineStage::kVertex) {
+            // Currently only vertex stage could have interstage output variables that need
+            // truncated.
+            continue;
+        }
+
+        auto* func_sem = sem.Get(func_ast);
+        auto* str = func_sem->ReturnType()->As<sem::Struct>();
+
+        if (!str) {
+            TINT_ICE(Transform, ctx.dst->Diagnostics())
+                << "Entrypoint function return type is non-struct.\n"
+                << "TruncateInterstageVariables transform needs to run after "
+                   "CanonicalizeEntryPointIO transform.";
+            continue;
+        }
+
+        // This transform is run after CanonicalizeEntryPointIO transform,
+        // So it is guaranteed that entry point inputs are already grouped in a struct.
+        const ast::Struct* struct_ty = str->Declaration();
+
+        // A prepass to check if any interstage variable locations in the entry point needs
+        // truncating. If not we don't really need to handle this entry point.
+        utils::Hashset<const sem::StructMember*, 16u> omit_members;
+
+        for (auto* member : struct_ty->members) {
+            if (ast::GetAttribute<ast::LocationAttribute>(member->attributes)) {
+                auto* m = sem.Get(member);
+                uint32_t location = m->Location().value();
+                if (!data->interstage_locations.test(location)) {
+                    omit_members.Add(m);
+                }
+            }
+        }
+
+        if (omit_members.IsEmpty()) {
+            continue;
+        }
+
+        // Now we are sure the transform needs to be run.
+        should_run = true;
+
+        // Get or create a new truncated struct/truncate function for the interstage inputs &
+        // outputs.
+        auto entry =
+            old_shader_io_structs_to_new_struct_and_truncate_functions.GetOrCreate(str, [&] {
+                auto new_struct_sym = b.Symbols().New();
+
+                utils::Vector<const ast::StructMember*, 20> truncated_members;
+                utils::Vector<const ast::Expression*, 20> initializer_exprs;
+
+                for (auto* member : str->Members()) {
+                    if (omit_members.Contains(member)) {
+                        continue;
+                    }
+
+                    truncated_members.Push(ctx.Clone(member->Declaration()));
+                    initializer_exprs.Push(
+                        b.MemberAccessor("io", ctx.Clone(member->Declaration()->symbol)));
+                }
+
+                // Create the new shader io struct.
+                b.Structure(new_struct_sym, std::move(truncated_members));
+
+                // Create the mapping function to truncate the shader io.
+                auto mapping_fn_sym = b.Symbols().New("truncate_shader_output");
+                b.Func(mapping_fn_sym,
+                       utils::Vector{b.Param("io", ctx.Clone(func_ast->return_type))},
+                       b.ty.type_name(new_struct_sym),
+                       utils::Vector{b.Return(b.Construct(b.ty.type_name(new_struct_sym),
+                                                          std::move(initializer_exprs)))});
+                return TruncatedStructAndConverter{new_struct_sym, mapping_fn_sym};
+            });
+
+        ctx.Replace(func_ast->return_type, b.ty.type_name(entry.truncated_struct));
+
+        entry_point_functions_to_truncate_functions.Add(func_sem, entry.truncate_fn);
+    }
+
+    if (!should_run) {
+        return SkipTransform;
+    }
+
+    // Replace return statements with new truncated shader IO struct
+    ctx.ReplaceAll(
+        [&](const ast::ReturnStatement* return_statement) -> const ast::ReturnStatement* {
+            auto* return_sem = sem.Get(return_statement);
+            if (auto* mapping_fn_sym =
+                    entry_point_functions_to_truncate_functions.Find(return_sem->Function())) {
+                return b.Return(return_statement->source,
+                                b.Call(*mapping_fn_sym, ctx.Clone(return_statement->value)));
+            }
+            return nullptr;
+        });
+
+    // Remove IO attributes from old shader IO struct which is not used as entry point output
+    // anymore.
+    for (auto it : old_shader_io_structs_to_new_struct_and_truncate_functions) {
+        const ast::Struct* struct_ty = it.key->Declaration();
+        for (auto* member : struct_ty->members) {
+            for (auto* attr : member->attributes) {
+                if (attr->IsAnyOf<ast::BuiltinAttribute, ast::LocationAttribute,
+                                  ast::InterpolateAttribute, ast::InvariantAttribute>()) {
+                    ctx.Remove(member->attributes, attr);
+                }
+            }
+        }
+    }
+
+    ctx.Clone();
+    return Program(std::move(b));
+}
+
+TruncateInterstageVariables::Config::Config() = default;
+
+TruncateInterstageVariables::Config::Config(const Config&) = default;
+
+TruncateInterstageVariables::Config::~Config() = default;
+
+TruncateInterstageVariables::Config& TruncateInterstageVariables::Config::operator=(const Config&) =
+    default;
+
+}  // namespace tint::transform
diff --git a/src/tint/transform/truncate_interstage_variables.h b/src/tint/transform/truncate_interstage_variables.h
new file mode 100644
index 0000000..bed226c
--- /dev/null
+++ b/src/tint/transform/truncate_interstage_variables.h
@@ -0,0 +1,130 @@
+// 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_TRANSFORM_TRUNCATE_INTERSTAGE_VARIABLES_H_
+#define SRC_TINT_TRANSFORM_TRUNCATE_INTERSTAGE_VARIABLES_H_
+
+#include <bitset>
+
+#include "src/tint/sem/binding_point.h"
+#include "src/tint/transform/transform.h"
+
+namespace tint::transform {
+
+/// TruncateInterstageVariables is a transform that truncate interstage variables.
+/// It must be run after CanonicalizeEntryPointIO which guarantees all interstage variables of
+/// a given entry point are grouped into one shader IO struct.
+/// It replaces `original shader IO struct` with a `new wrapper struct` containing builtin IOs
+/// and user-defined IO whose locations are marked in the interstage_locations bitset from the
+/// config. The return statements of `original shader IO struct` are wrapped by a mapping function
+/// that initializes the members of `new wrapper struct` with values from `original shader IO
+/// struct`. IO attributes of members in `original shader IO struct` are removed, other attributes
+/// still preserve.
+///
+/// For example:
+///
+/// ```
+///  struct ShaderIO {
+///    @builtin(position) @invariant pos: vec4<f32>,
+///    @location(1) f_1: f32,
+///    @location(3) @align(16) f_3: f32,
+///    @location(5) @interpolate(flat) @align(16) @size(16) f_5: u32,
+///  }
+///  @vertex
+///  fn f() -> ShaderIO {
+///    var io: ShaderIO;
+///    io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
+///    io.f_1 = 1.0;
+///    io.f_3 = io.f_1 + 3.0;
+///    io.f_5 = 1u;
+///    return io;
+///  }
+/// ```
+///
+/// With config.interstage_locations[3] and [5] set to true, is transformed to:
+///
+/// ```
+///  struct tint_symbol {
+///    @builtin(position) @invariant
+///    pos : vec4<f32>,
+///    @location(3) @align(16)
+///    f_3 : f32,
+///    @location(5) @interpolate(flat) @align(16) @size(16)
+///    f_5 : u32,
+///  }
+///
+///  fn truncate_shader_output(io : ShaderIO) -> tint_symbol {
+///    return tint_symbol(io.pos, io.f_3, io.f_5);
+///  }
+///
+///  struct ShaderIO {
+///    pos : vec4<f32>,
+///    f_1 : f32,
+///    @align(16)
+///    f_3 : f32,
+///    @align(16) @size(16)
+///    f_5 : u32,
+///  }
+///
+///  @vertex
+///  fn f() -> tint_symbol {
+///    var io : ShaderIO;
+///    io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
+///    io.f_1 = 1.0;
+///    io.f_3 = (io.f_1 + 3.0);
+///    io.f_5 = 1u;
+///    return truncate_shader_output(io);
+///  }
+/// ```
+///
+class TruncateInterstageVariables final : public Castable<TruncateInterstageVariables, Transform> {
+  public:
+    /// Configuration options for the transform
+    struct Config final : public Castable<Config, Data> {
+        /// Constructor
+        Config();
+
+        /// Copy constructor
+        Config(const Config&);
+
+        /// Destructor
+        ~Config() override;
+
+        /// Assignment operator
+        /// @returns this Config
+        Config& operator=(const Config&);
+
+        /// Indicate which interstage io locations are actually used by the later stage.
+        /// There can be at most 16 user defined interstage variables with locations.
+        std::bitset<16> interstage_locations;
+
+        /// Reflect the fields of this class so that it can be used by tint::ForeachField()
+        TINT_REFLECT(interstage_variables);
+    };
+
+    /// Constructor using a the configuration provided in the input Data
+    TruncateInterstageVariables();
+
+    /// Destructor
+    ~TruncateInterstageVariables() override;
+
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
+};
+
+}  // namespace tint::transform
+
+#endif  // SRC_TINT_TRANSFORM_TRUNCATE_INTERSTAGE_VARIABLES_H_
diff --git a/src/tint/transform/truncate_interstage_variables_test.cc b/src/tint/transform/truncate_interstage_variables_test.cc
new file mode 100644
index 0000000..9ab8fa2
--- /dev/null
+++ b/src/tint/transform/truncate_interstage_variables_test.cc
@@ -0,0 +1,599 @@
+// 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/transform/truncate_interstage_variables.h"
+#include "src/tint/transform/canonicalize_entry_point_io.h"
+
+#include "gmock/gmock.h"
+#include "src/tint/transform/test_helper.h"
+
+namespace tint::transform {
+namespace {
+
+using ::testing::ContainerEq;
+
+using TruncateInterstageVariablesTest = TransformTest;
+
+TEST_F(TruncateInterstageVariablesTest, ShouldRunVertex) {
+    auto* src = R"(
+struct ShaderIO {
+  @builtin(position) pos: vec4<f32>,
+  @location(0) f_0: f32,
+  @location(2) f_2: f32,
+}
+@vertex
+fn f() -> ShaderIO {
+  var io: ShaderIO;
+  io.f_0 = 1.0;
+  io.f_2 = io.f_2 + 3.0;
+  return io;
+}
+)";
+
+    {
+        auto* expect =
+            "error: missing transform data for "
+            "tint::transform::TruncateInterstageVariables";
+        auto got = Run<TruncateInterstageVariables>(src);
+        EXPECT_EQ(expect, str(got));
+    }
+
+    {
+        // Empty interstage_locations: truncate all interstage variables, should run
+        TruncateInterstageVariables::Config cfg;
+        DataMap data;
+        data.Add<TruncateInterstageVariables::Config>(cfg);
+        EXPECT_TRUE(ShouldRun<TruncateInterstageVariables>(src, data));
+    }
+
+    {
+        // All existing interstage_locations are marked: should not run
+        TruncateInterstageVariables::Config cfg;
+        cfg.interstage_locations[0] = true;
+        cfg.interstage_locations[2] = true;
+        DataMap data;
+        data.Add<TruncateInterstageVariables::Config>(cfg);
+        EXPECT_FALSE(ShouldRun<TruncateInterstageVariables>(src, data));
+    }
+
+    {
+        // Partial interstage_locations are marked: should run
+        TruncateInterstageVariables::Config cfg;
+        cfg.interstage_locations[2] = true;
+        DataMap data;
+        data.Add<TruncateInterstageVariables::Config>(cfg);
+        EXPECT_TRUE(ShouldRun<TruncateInterstageVariables>(src, data));
+    }
+}
+
+TEST_F(TruncateInterstageVariablesTest, ShouldRunFragment) {
+    auto* src = R"(
+struct ShaderIO {
+  @location(0) f_0: f32,
+  @location(2) f_2: f32,
+}
+@fragment
+fn f(io: ShaderIO) -> @location(1) vec4<f32> {
+  return vec4<f32>(io.f_0, io.f_2, 0.0, 1.0);
+}
+)";
+
+    TruncateInterstageVariables::Config cfg;
+    cfg.interstage_locations[2] = true;
+
+    DataMap data;
+    data.Add<TruncateInterstageVariables::Config>(cfg);
+
+    EXPECT_FALSE(ShouldRun<TruncateInterstageVariables>(src, data));
+}
+
+// Test that this transform should run after canoicalize entry point io, where shader io is already
+// grouped into a struct.
+TEST_F(TruncateInterstageVariablesTest, ShouldRunAfterCanonicalizeEntryPointIO) {
+    auto* src = R"(
+@vertex
+fn f() -> @builtin(position) vec4<f32> {
+  return vec4<f32>(1.0, 1.0, 1.0, 1.0);
+}
+)";
+
+    TruncateInterstageVariables::Config cfg;
+    cfg.interstage_locations[0] = true;
+    DataMap data;
+    data.Add<TruncateInterstageVariables::Config>(cfg);
+
+    data.Add<CanonicalizeEntryPointIO::Config>(CanonicalizeEntryPointIO::ShaderStyle::kHlsl);
+    auto got = Run<CanonicalizeEntryPointIO>(src, data);
+
+    // Inevitably entry point can write only one variable if not using struct
+    // So the truncate won't run.
+    EXPECT_FALSE(ShouldRun<TruncateInterstageVariables>(str(got), data));
+}
+
+TEST_F(TruncateInterstageVariablesTest, BasicVertexTrimLocationInMid) {
+    auto* src = R"(
+struct ShaderIO {
+  @builtin(position) pos: vec4<f32>,
+  @location(1) f_1: f32,
+  @location(3) f_3: f32,
+}
+@vertex
+fn f() -> ShaderIO {
+  var io: ShaderIO;
+  io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
+  io.f_1 = 1.0;
+  io.f_3 = io.f_1 + 3.0;
+  return io;
+}
+)";
+
+    auto* expect = R"(
+struct tint_symbol {
+  @builtin(position)
+  pos : vec4<f32>,
+  @location(1)
+  f_1 : f32,
+}
+
+fn truncate_shader_output(io : ShaderIO) -> tint_symbol {
+  return tint_symbol(io.pos, io.f_1);
+}
+
+struct ShaderIO {
+  pos : vec4<f32>,
+  f_1 : f32,
+  f_3 : f32,
+}
+
+@vertex
+fn f() -> tint_symbol {
+  var io : ShaderIO;
+  io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
+  io.f_1 = 1.0;
+  io.f_3 = (io.f_1 + 3.0);
+  return truncate_shader_output(io);
+}
+)";
+
+    TruncateInterstageVariables::Config cfg;
+    // fragment has input at @location(1)
+    cfg.interstage_locations[1] = true;
+
+    DataMap data;
+    data.Add<TruncateInterstageVariables::Config>(cfg);
+
+    auto got = Run<TruncateInterstageVariables>(src, data);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(TruncateInterstageVariablesTest, BasicVertexTrimLocationAtEnd) {
+    auto* src = R"(
+struct ShaderIO {
+  @builtin(position) pos: vec4<f32>,
+  @location(1) f_1: f32,
+  @location(3) f_3: f32,
+}
+@vertex
+fn f() -> ShaderIO {
+  var io: ShaderIO;
+  io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
+  io.f_1 = 1.0;
+  io.f_3 = io.f_1 + 3.0;
+  return io;
+}
+)";
+
+    auto* expect = R"(
+struct tint_symbol {
+  @builtin(position)
+  pos : vec4<f32>,
+  @location(3)
+  f_3 : f32,
+}
+
+fn truncate_shader_output(io : ShaderIO) -> tint_symbol {
+  return tint_symbol(io.pos, io.f_3);
+}
+
+struct ShaderIO {
+  pos : vec4<f32>,
+  f_1 : f32,
+  f_3 : f32,
+}
+
+@vertex
+fn f() -> tint_symbol {
+  var io : ShaderIO;
+  io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
+  io.f_1 = 1.0;
+  io.f_3 = (io.f_1 + 3.0);
+  return truncate_shader_output(io);
+}
+)";
+
+    TruncateInterstageVariables::Config cfg;
+    // fragment has input at @location(3)
+    cfg.interstage_locations[3] = true;
+
+    DataMap data;
+    data.Add<TruncateInterstageVariables::Config>(cfg);
+
+    auto got = Run<TruncateInterstageVariables>(src, data);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(TruncateInterstageVariablesTest, TruncateAllLocations) {
+    auto* src = R"(
+struct ShaderIO {
+  @builtin(position) pos: vec4<f32>,
+  @location(1) f_1: f32,
+  @location(3) f_3: f32,
+}
+@vertex
+fn f() -> ShaderIO {
+  var io: ShaderIO;
+  io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
+  io.f_1 = 1.0;
+  io.f_3 = io.f_1 + 3.0;
+  return io;
+}
+)";
+
+    {
+        auto* expect = R"(
+struct tint_symbol {
+  @builtin(position)
+  pos : vec4<f32>,
+}
+
+fn truncate_shader_output(io : ShaderIO) -> tint_symbol {
+  return tint_symbol(io.pos);
+}
+
+struct ShaderIO {
+  pos : vec4<f32>,
+  f_1 : f32,
+  f_3 : f32,
+}
+
+@vertex
+fn f() -> tint_symbol {
+  var io : ShaderIO;
+  io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
+  io.f_1 = 1.0;
+  io.f_3 = (io.f_1 + 3.0);
+  return truncate_shader_output(io);
+}
+)";
+
+        TruncateInterstageVariables::Config cfg;
+        DataMap data;
+        data.Add<TruncateInterstageVariables::Config>(cfg);
+
+        auto got = Run<TruncateInterstageVariables>(src, data);
+
+        EXPECT_EQ(expect, str(got));
+    }
+}
+
+// Test that the transform only removes IO attributes and preserve other attributes in the old
+// Shader IO struct.
+TEST_F(TruncateInterstageVariablesTest, RemoveIOAttributes) {
+    auto* src = R"(
+struct ShaderIO {
+  @builtin(position) @invariant pos: vec4<f32>,
+  @location(1) f_1: f32,
+  @location(3) @align(16) f_3: f32,
+  @location(5) @interpolate(flat) @align(16) @size(16) f_5: u32,
+}
+@vertex
+fn f() -> ShaderIO {
+  var io: ShaderIO;
+  io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
+  io.f_1 = 1.0;
+  io.f_3 = io.f_1 + 3.0;
+  io.f_5 = 1u;
+  return io;
+}
+)";
+
+    {
+        auto* expect = R"(
+struct tint_symbol {
+  @builtin(position) @invariant
+  pos : vec4<f32>,
+  @location(3) @align(16)
+  f_3 : f32,
+  @location(5) @interpolate(flat) @align(16) @size(16)
+  f_5 : u32,
+}
+
+fn truncate_shader_output(io : ShaderIO) -> tint_symbol {
+  return tint_symbol(io.pos, io.f_3, io.f_5);
+}
+
+struct ShaderIO {
+  pos : vec4<f32>,
+  f_1 : f32,
+  @align(16)
+  f_3 : f32,
+  @align(16) @size(16)
+  f_5 : u32,
+}
+
+@vertex
+fn f() -> tint_symbol {
+  var io : ShaderIO;
+  io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
+  io.f_1 = 1.0;
+  io.f_3 = (io.f_1 + 3.0);
+  io.f_5 = 1u;
+  return truncate_shader_output(io);
+}
+)";
+
+        TruncateInterstageVariables::Config cfg;
+        // Missing @location[1] intentionally to make sure the transform run.
+        cfg.interstage_locations[3] = true;
+        cfg.interstage_locations[5] = true;
+
+        DataMap data;
+        data.Add<TruncateInterstageVariables::Config>(cfg);
+
+        auto got = Run<TruncateInterstageVariables>(src, data);
+
+        EXPECT_EQ(expect, str(got));
+    }
+}
+
+TEST_F(TruncateInterstageVariablesTest, MultipleEntryPointsSharingStruct) {
+    auto* src = R"(
+struct ShaderIO {
+  @builtin(position) pos: vec4<f32>,
+  @location(1) f_1: f32,
+  @location(3) f_3: f32,
+  @location(5) f_5: f32,
+}
+
+@vertex
+fn f1() -> ShaderIO {
+  var io: ShaderIO;
+  io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
+  io.f_1 = 1.0;
+  io.f_3 = 1.0;
+  return io;
+}
+
+@vertex
+fn f2() -> ShaderIO {
+  var io: ShaderIO;
+  io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
+  io.f_5 = 2.0;
+  return io;
+}
+)";
+
+    auto* expect = R"(
+struct tint_symbol {
+  @builtin(position)
+  pos : vec4<f32>,
+  @location(3)
+  f_3 : f32,
+}
+
+fn truncate_shader_output(io : ShaderIO) -> tint_symbol {
+  return tint_symbol(io.pos, io.f_3);
+}
+
+struct ShaderIO {
+  pos : vec4<f32>,
+  f_1 : f32,
+  f_3 : f32,
+  f_5 : f32,
+}
+
+@vertex
+fn f1() -> tint_symbol {
+  var io : ShaderIO;
+  io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
+  io.f_1 = 1.0;
+  io.f_3 = 1.0;
+  return truncate_shader_output(io);
+}
+
+@vertex
+fn f2() -> tint_symbol {
+  var io : ShaderIO;
+  io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
+  io.f_5 = 2.0;
+  return truncate_shader_output(io);
+}
+)";
+
+    TruncateInterstageVariables::Config cfg;
+    // fragment has input at @location(3)
+    cfg.interstage_locations[3] = true;
+
+    DataMap data;
+    data.Add<TruncateInterstageVariables::Config>(cfg);
+
+    auto got = Run<TruncateInterstageVariables>(src, data);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(TruncateInterstageVariablesTest, MultipleEntryPoints) {
+    auto* src = R"(
+struct ShaderIO1 {
+  @builtin(position) pos: vec4<f32>,
+  @location(1) f_1: f32,
+  @location(3) f_3: f32,
+  @location(5) f_5: f32,
+}
+
+@vertex
+fn f1() -> ShaderIO1 {
+  var io: ShaderIO1;
+  io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
+  io.f_1 = 1.0;
+  io.f_3 = 1.0;
+  return io;
+}
+
+struct ShaderIO2 {
+  @builtin(position) pos: vec4<f32>,
+  @location(2) f_2: f32,
+}
+
+@vertex
+fn f2() -> ShaderIO2 {
+  var io: ShaderIO2;
+  io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
+  io.f_2 = 2.0;
+  return io;
+}
+)";
+
+    auto* expect = R"(
+struct tint_symbol {
+  @builtin(position)
+  pos : vec4<f32>,
+  @location(3)
+  f_3 : f32,
+}
+
+fn truncate_shader_output(io : ShaderIO1) -> tint_symbol {
+  return tint_symbol(io.pos, io.f_3);
+}
+
+struct tint_symbol_1 {
+  @builtin(position)
+  pos : vec4<f32>,
+}
+
+fn truncate_shader_output_1(io : ShaderIO2) -> tint_symbol_1 {
+  return tint_symbol_1(io.pos);
+}
+
+struct ShaderIO1 {
+  pos : vec4<f32>,
+  f_1 : f32,
+  f_3 : f32,
+  f_5 : f32,
+}
+
+@vertex
+fn f1() -> tint_symbol {
+  var io : ShaderIO1;
+  io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
+  io.f_1 = 1.0;
+  io.f_3 = 1.0;
+  return truncate_shader_output(io);
+}
+
+struct ShaderIO2 {
+  pos : vec4<f32>,
+  f_2 : f32,
+}
+
+@vertex
+fn f2() -> tint_symbol_1 {
+  var io : ShaderIO2;
+  io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
+  io.f_2 = 2.0;
+  return truncate_shader_output_1(io);
+}
+)";
+
+    TruncateInterstageVariables::Config cfg;
+    // fragment has input at @location(3)
+    cfg.interstage_locations[3] = true;
+
+    DataMap data;
+    data.Add<TruncateInterstageVariables::Config>(cfg);
+
+    auto got = Run<TruncateInterstageVariables>(src, data);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(TruncateInterstageVariablesTest, MultipleReturnStatements) {
+    auto* src = R"(
+struct ShaderIO {
+  @builtin(position) pos: vec4<f32>,
+  @location(1) f_1: f32,
+  @location(3) f_3: f32,
+}
+@vertex
+fn f(@builtin(vertex_index) vid: u32) -> ShaderIO {
+  var io: ShaderIO;
+  if (vid > 10u) {
+    io.f_1 = 2.0;
+    return io;
+  }
+  io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
+  io.f_1 = 1.0;
+  io.f_3 = io.f_1 + 3.0;
+  return io;
+}
+)";
+
+    auto* expect = R"(
+struct tint_symbol {
+  @builtin(position)
+  pos : vec4<f32>,
+  @location(3)
+  f_3 : f32,
+}
+
+fn truncate_shader_output(io : ShaderIO) -> tint_symbol {
+  return tint_symbol(io.pos, io.f_3);
+}
+
+struct ShaderIO {
+  pos : vec4<f32>,
+  f_1 : f32,
+  f_3 : f32,
+}
+
+@vertex
+fn f(@builtin(vertex_index) vid : u32) -> tint_symbol {
+  var io : ShaderIO;
+  if ((vid > 10u)) {
+    io.f_1 = 2.0;
+    return truncate_shader_output(io);
+  }
+  io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
+  io.f_1 = 1.0;
+  io.f_3 = (io.f_1 + 3.0);
+  return truncate_shader_output(io);
+}
+)";
+
+    TruncateInterstageVariables::Config cfg;
+    // fragment has input at @location(3)
+    cfg.interstage_locations[3] = true;
+
+    DataMap data;
+    data.Add<TruncateInterstageVariables::Config>(cfg);
+
+    auto got = Run<TruncateInterstageVariables>(src, data);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+}  // namespace
+}  // namespace tint::transform
diff --git a/src/tint/transform/unwind_discard_functions.cc b/src/tint/transform/unwind_discard_functions.cc
deleted file mode 100644
index 068fe35..0000000
--- a/src/tint/transform/unwind_discard_functions.cc
+++ /dev/null
@@ -1,374 +0,0 @@
-// 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/transform/unwind_discard_functions.h"
-
-#include <memory>
-#include <string>
-#include <unordered_set>
-#include <utility>
-#include <vector>
-
-#include "src/tint/ast/discard_statement.h"
-#include "src/tint/ast/return_statement.h"
-#include "src/tint/ast/traverse_expressions.h"
-#include "src/tint/sem/block_statement.h"
-#include "src/tint/sem/call.h"
-#include "src/tint/sem/for_loop_statement.h"
-#include "src/tint/sem/function.h"
-#include "src/tint/sem/if_statement.h"
-#include "src/tint/transform/utils/get_insertion_point.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::transform::UnwindDiscardFunctions);
-
-namespace tint::transform {
-namespace {
-
-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;
-    const sem::Info& sem;
-    Symbol module_discard_var_name;   // Use ModuleDiscardVarName() to read
-    Symbol module_discard_func_name;  // Use ModuleDiscardFuncName() to read
-
-    // Returns true if `sem_expr` contains a call expression that may
-    // (transitively) execute a discard statement.
-    bool MayDiscard(const sem::Expression* sem_expr) {
-        return sem_expr && sem_expr->Behaviors().Contains(sem::Behavior::kDiscard);
-    }
-
-    // Lazily creates and returns the name of the module bool variable for whether
-    // to discard: "tint_discard".
-    Symbol ModuleDiscardVarName() {
-        if (!module_discard_var_name.IsValid()) {
-            module_discard_var_name = b.Symbols().New("tint_discard");
-            ctx.dst->GlobalVar(module_discard_var_name, b.ty.bool_(), b.Expr(false),
-                               ast::AddressSpace::kPrivate);
-        }
-        return module_discard_var_name;
-    }
-
-    // Lazily creates and returns the name of the function that contains a single
-    // discard statement: "tint_discard_func".
-    // We do this to avoid having multiple discard statements in a single program,
-    // which causes problems in certain backends (see crbug.com/1118).
-    Symbol ModuleDiscardFuncName() {
-        if (!module_discard_func_name.IsValid()) {
-            module_discard_func_name = b.Symbols().New("tint_discard_func");
-            b.Func(module_discard_func_name, tint::utils::Empty, b.ty.void_(),
-                   tint::utils::Vector{b.Discard()});
-        }
-        return module_discard_func_name;
-    }
-
-    // Creates "return <default return value>;" based on the return type of
-    // `stmt`'s owning function.
-    const ast::ReturnStatement* Return(const ast::Statement* stmt) {
-        const ast::Expression* ret_val = nullptr;
-        auto* ret_type = sem.Get(stmt)->Function()->Declaration()->return_type;
-        if (!ret_type->Is<ast::Void>()) {
-            ret_val = b.Construct(ctx.Clone(ret_type));
-        }
-        return b.Return(ret_val);
-    }
-
-    // Returns true if the function `stmt` is in is an entry point
-    bool IsInEntryPointFunc(const ast::Statement* stmt) {
-        return sem.Get(stmt)->Function()->Declaration()->IsEntryPoint();
-    }
-
-    // Creates "tint_discard_func();"
-    const ast::CallStatement* CallDiscardFunc() {
-        auto func_name = ModuleDiscardFuncName();
-        return b.CallStmt(b.Call(func_name));
-    }
-
-    // Creates and returns a new if-statement of the form:
-    //
-    //    if (tint_discard) {
-    //      return <default value>;
-    //    }
-    //
-    // or if `stmt` is in a entry point function:
-    //
-    //    if (tint_discard) {
-    //      tint_discard_func();
-    //      return <default value>;
-    //    }
-    //
-    const ast::IfStatement* IfDiscardReturn(const ast::Statement* stmt) {
-        tint::utils::Vector<const ast::Statement*, 2> stmts;
-
-        // For entry point functions, also emit the discard statement
-        if (IsInEntryPointFunc(stmt)) {
-            stmts.Push(CallDiscardFunc());
-        }
-
-        stmts.Push(Return(stmt));
-
-        auto var_name = ModuleDiscardVarName();
-        return b.If(var_name, b.Block(stmts));
-    }
-
-    // Hoists `sem_expr` to a let followed by an `IfDiscardReturn` before `stmt`.
-    // For example, if `stmt` is:
-    //
-    //    return f();
-    //
-    // This function will transform this to:
-    //
-    //    let t1 = f();
-    //    if (tint_discard) {
-    //      return;
-    //    }
-    //    return t1;
-    //
-    const ast::Statement* HoistAndInsertBefore(const ast::Statement* stmt,
-                                               const sem::Expression* sem_expr) {
-        auto* expr = sem_expr->Declaration();
-
-        auto ip = utils::GetInsertionPoint(ctx, stmt);
-        auto var_name = b.Sym();
-        auto* decl = b.Decl(b.Var(var_name, ctx.Clone(expr)));
-        ctx.InsertBefore(ip.first->Declaration()->statements, ip.second, decl);
-
-        ctx.InsertBefore(ip.first->Declaration()->statements, ip.second, IfDiscardReturn(stmt));
-
-        auto* var_expr = b.Expr(var_name);
-
-        // Special handling for CallStatement as we can only replace its expression
-        // with a CallExpression.
-        if (stmt->Is<ast::CallStatement>()) {
-            // We could replace the call statement with no statement, but we can't do
-            // that with transforms (yet), so just return a phony assignment.
-            return b.Assign(b.Phony(), var_expr);
-        }
-
-        ctx.Replace(expr, var_expr);
-        return ctx.CloneWithoutTransform(stmt);
-    }
-
-    // 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 = tint::As<sem::ForLoopStatement>(sem_stmt->Parent())) {
-                return sem_fl->Declaration()->initializer == stmt;
-            }
-        }
-        return false;
-    }
-
-    // Inserts an `IfDiscardReturn` after `stmt` if possible (i.e. `stmt` is not
-    // in a for-loop init), otherwise falls back to HoistAndInsertBefore, hoisting
-    // `sem_expr` to a let followed by an `IfDiscardReturn` before `stmt`.
-    //
-    // For example, if `stmt` is:
-    //
-    //    let r = f();
-    //
-    // This function will transform this to:
-    //
-    //    let r = f();
-    //    if (tint_discard) {
-    //      return;
-    //    }
-    const ast::Statement* TryInsertAfter(const ast::Statement* stmt,
-                                         const sem::Expression* sem_expr) {
-        // If `stmt` is the init of a for-loop, hoist and insert before instead.
-        if (IsForLoopInitStatement(stmt)) {
-            return HoistAndInsertBefore(stmt, sem_expr);
-        }
-
-        auto ip = utils::GetInsertionPoint(ctx, stmt);
-        ctx.InsertAfter(ip.first->Declaration()->statements, ip.second, IfDiscardReturn(stmt));
-        return nullptr;  // Don't replace current statement
-    }
-
-    // Replaces the input discard statement with either setting the module level
-    // discard bool ("tint_discard = true"), or calling the discard function
-    // ("tint_discard_func()"), followed by a default return statement.
-    //
-    // Replaces "discard;" with:
-    //
-    //    tint_discard = true;
-    //    return;
-    //
-    // Or if `stmt` is a entry point function, replaces with:
-    //
-    //    tint_discard_func();
-    //    return;
-    //
-    const ast::Statement* ReplaceDiscardStatement(const ast::DiscardStatement* stmt) {
-        const ast::Statement* to_insert = nullptr;
-        if (IsInEntryPointFunc(stmt)) {
-            to_insert = CallDiscardFunc();
-        } else {
-            auto var_name = ModuleDiscardVarName();
-            to_insert = b.Assign(var_name, true);
-        }
-
-        auto ip = utils::GetInsertionPoint(ctx, stmt);
-        ctx.InsertBefore(ip.first->Declaration()->statements, ip.second, to_insert);
-        return Return(stmt);
-    }
-
-    // Handle statement
-    const ast::Statement* Statement(const ast::Statement* stmt) {
-        return Switch(
-            stmt,
-            [&](const ast::DiscardStatement* s) -> const ast::Statement* {
-                return ReplaceDiscardStatement(s);
-            },
-            [&](const ast::AssignmentStatement* s) -> const ast::Statement* {
-                auto* sem_lhs = sem.Get(s->lhs);
-                auto* sem_rhs = sem.Get(s->rhs);
-                if (MayDiscard(sem_lhs)) {
-                    if (MayDiscard(sem_rhs)) {
-                        TINT_ICE(Transform, b.Diagnostics())
-                            << "Unexpected: both sides of assignment statement may "
-                               "discard. Make sure transform::PromoteSideEffectsToDecl "
-                               "was run first.";
-                    }
-                    return TryInsertAfter(s, sem_lhs);
-                } else if (MayDiscard(sem_rhs)) {
-                    return TryInsertAfter(s, sem_rhs);
-                }
-                return nullptr;
-            },
-            [&](const ast::CallStatement* s) -> const ast::Statement* {
-                auto* sem_expr = sem.Get(s->expr);
-                if (!MayDiscard(sem_expr)) {
-                    return nullptr;
-                }
-                return TryInsertAfter(s, sem_expr);
-            },
-            [&](const ast::ForLoopStatement* s) -> const ast::Statement* {
-                if (MayDiscard(sem.Get(s->condition))) {
-                    TINT_ICE(Transform, b.Diagnostics())
-                        << "Unexpected ForLoopStatement condition that may discard. "
-                           "Make sure transform::PromoteSideEffectsToDecl was run "
-                           "first.";
-                }
-                return nullptr;
-            },
-            [&](const ast::WhileStatement* s) -> const ast::Statement* {
-                if (MayDiscard(sem.Get(s->condition))) {
-                    TINT_ICE(Transform, b.Diagnostics())
-                        << "Unexpected WhileStatement condition that may discard. "
-                           "Make sure transform::PromoteSideEffectsToDecl was run "
-                           "first.";
-                }
-                return nullptr;
-            },
-            [&](const ast::IfStatement* s) -> const ast::Statement* {
-                auto* sem_expr = sem.Get(s->condition);
-                if (!MayDiscard(sem_expr)) {
-                    return nullptr;
-                }
-                return HoistAndInsertBefore(s, sem_expr);
-            },
-            [&](const ast::ReturnStatement* s) -> const ast::Statement* {
-                auto* sem_expr = sem.Get(s->value);
-                if (!MayDiscard(sem_expr)) {
-                    return nullptr;
-                }
-                return HoistAndInsertBefore(s, sem_expr);
-            },
-            [&](const ast::SwitchStatement* s) -> const ast::Statement* {
-                auto* sem_expr = sem.Get(s->condition);
-                if (!MayDiscard(sem_expr)) {
-                    return nullptr;
-                }
-                return HoistAndInsertBefore(s, sem_expr);
-            },
-            [&](const ast::VariableDeclStatement* s) -> const ast::Statement* {
-                auto* var = s->variable;
-                if (!var->initializer) {
-                    return nullptr;
-                }
-                auto* sem_expr = sem.Get(var->initializer);
-                if (!MayDiscard(sem_expr)) {
-                    return nullptr;
-                }
-                return TryInsertAfter(s, sem_expr);
-            });
-    }
-};
-
-UnwindDiscardFunctions::UnwindDiscardFunctions() = default;
-UnwindDiscardFunctions::~UnwindDiscardFunctions() = default;
-
-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();
-
-    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
deleted file mode 100644
index 7614c27..0000000
--- a/src/tint/transform/unwind_discard_functions.h
+++ /dev/null
@@ -1,58 +0,0 @@
-// 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_TRANSFORM_UNWIND_DISCARD_FUNCTIONS_H_
-#define SRC_TINT_TRANSFORM_UNWIND_DISCARD_FUNCTIONS_H_
-
-#include "src/tint/transform/transform.h"
-
-namespace tint::transform {
-
-/// This transform is responsible for implementing discard semantics as per the
-/// WGSL specification: https://gpuweb.github.io/gpuweb/wgsl/#discard-statement
-///
-/// Not all backend languages implement discard this way (e.g. HLSL), so this
-/// transform does the following:
-///
-/// * Replaces discard statements with setting a module-level boolean
-/// "tint_discard" to true and returning immediately.
-/// * Wherever calls are made to discarding functions (directly or
-/// transitively), it inserts a check afterwards for if "tint_discard" is true,
-/// to return immediately.
-/// * Finally, entry point functions that call discarding functions
-/// emit a call to "tint_discard_func()" that contains the sole discard
-/// statement.
-///
-/// @note Depends on the following transforms to have been run first:
-/// * PromoteSideEffectsToDecl
-class UnwindDiscardFunctions final : public Castable<UnwindDiscardFunctions, Transform> {
-  public:
-    /// Constructor
-    UnwindDiscardFunctions();
-
-    /// Destructor
-    ~UnwindDiscardFunctions() override;
-
-    /// @copydoc Transform::Apply
-    ApplyResult Apply(const Program* program,
-                      const DataMap& inputs,
-                      DataMap& outputs) const override;
-
-  private:
-    struct State;
-};
-
-}  // namespace tint::transform
-
-#endif  // SRC_TINT_TRANSFORM_UNWIND_DISCARD_FUNCTIONS_H_
diff --git a/src/tint/transform/unwind_discard_functions_test.cc b/src/tint/transform/unwind_discard_functions_test.cc
deleted file mode 100644
index 7d1fa27..0000000
--- a/src/tint/transform/unwind_discard_functions_test.cc
+++ /dev/null
@@ -1,1480 +0,0 @@
-// 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/transform/unwind_discard_functions.h"
-#include "src/tint/transform/promote_side_effects_to_decl.h"
-#include "src/tint/transform/test_helper.h"
-
-namespace tint::transform {
-namespace {
-
-using UnwindDiscardFunctionsTest = TransformTest;
-
-TEST_F(UnwindDiscardFunctionsTest, EmptyModule) {
-    auto* src = "";
-    auto* expect = src;
-
-    DataMap data;
-    auto got = Run<PromoteSideEffectsToDecl, UnwindDiscardFunctions>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(UnwindDiscardFunctionsTest, ShouldRun_NoDiscardFunc) {
-    auto* src = R"(
-fn f() {
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<UnwindDiscardFunctions>(src));
-}
-
-TEST_F(UnwindDiscardFunctionsTest, SingleDiscardFunc_NoCall) {
-    auto* src = R"(
-fn f() {
-  discard;
-}
-)";
-    auto* expect = R"(
-var<private> tint_discard : bool = false;
-
-fn f() {
-  tint_discard = true;
-  return;
-}
-)";
-
-    DataMap data;
-    auto got = Run<PromoteSideEffectsToDecl, UnwindDiscardFunctions>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(UnwindDiscardFunctionsTest, MultipleDiscardFuncs_NoCall) {
-    auto* src = R"(
-fn f() {
-  discard;
-  let marker1 = 0;
-}
-
-fn g() {
-  discard;
-  let marker1 = 0;
-}
-)";
-    auto* expect = R"(
-var<private> tint_discard : bool = false;
-
-fn f() {
-  tint_discard = true;
-  return;
-  let marker1 = 0;
-}
-
-fn g() {
-  tint_discard = true;
-  return;
-  let marker1 = 0;
-}
-)";
-
-    DataMap data;
-    auto got = Run<PromoteSideEffectsToDecl, UnwindDiscardFunctions>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(UnwindDiscardFunctionsTest, Call_VoidReturn) {
-    auto* src = R"(
-fn f() {
-  discard;
-  let marker1 = 0;
-}
-
-@fragment
-fn main(@builtin(position) coord_in: vec4<f32>) -> @location(0) vec4<f32> {
-  f();
-  let marker1 = 0;
-  return vec4<f32>();
-}
-)";
-    auto* expect = R"(
-var<private> tint_discard : bool = false;
-
-fn f() {
-  tint_discard = true;
-  return;
-  let marker1 = 0;
-}
-
-fn tint_discard_func() {
-  discard;
-}
-
-@fragment
-fn main(@builtin(position) coord_in : vec4<f32>) -> @location(0) vec4<f32> {
-  f();
-  if (tint_discard) {
-    tint_discard_func();
-    return vec4<f32>();
-  }
-  let marker1 = 0;
-  return vec4<f32>();
-}
-)";
-
-    DataMap data;
-    auto got = Run<PromoteSideEffectsToDecl, UnwindDiscardFunctions>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(UnwindDiscardFunctionsTest, Call_NonVoidReturn) {
-    auto* src = R"(
-struct S {
-  x : i32,
-  y : i32,
-};
-
-fn f() -> S {
-  if (true) {
-    discard;
-  }
-  let marker1 = 0;
-  var s : S;
-  return s;
-}
-
-@fragment
-fn main(@builtin(position) coord_in: vec4<f32>) -> @location(0) vec4<f32> {
-  let marker1 = 0;
-  f();
-  let marker2 = 0;
-  return vec4<f32>();
-}
-)";
-    auto* expect = R"(
-struct S {
-  x : i32,
-  y : i32,
-}
-
-var<private> tint_discard : bool = false;
-
-fn f() -> S {
-  if (true) {
-    tint_discard = true;
-    return S();
-  }
-  let marker1 = 0;
-  var s : S;
-  return s;
-}
-
-fn tint_discard_func() {
-  discard;
-}
-
-@fragment
-fn main(@builtin(position) coord_in : vec4<f32>) -> @location(0) vec4<f32> {
-  let marker1 = 0;
-  f();
-  if (tint_discard) {
-    tint_discard_func();
-    return vec4<f32>();
-  }
-  let marker2 = 0;
-  return vec4<f32>();
-}
-)";
-
-    DataMap data;
-    auto got = Run<PromoteSideEffectsToDecl, UnwindDiscardFunctions>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(UnwindDiscardFunctionsTest, Call_Nested) {
-    auto* src = R"(
-fn f() -> i32 {
-  let marker1 = 0;
-  if (true) {
-    discard;
-  }
-  let marker2 = 0;
-  return 0;
-}
-
-fn g() -> i32 {
-  let marker1 = 0;
-  f();
-  let marker2 = 0;
-  return 0;
-}
-
-fn h() -> i32{
-  let marker1 = 0;
-  g();
-  let marker2 = 0;
-  return 0;
-}
-
-@fragment
-fn main(@builtin(position) coord_in: vec4<f32>) -> @location(0) vec4<f32> {
-  let marker1 = 0;
-  h();
-  let marker2 = 0;
-  return vec4<f32>();
-}
-)";
-    auto* expect = R"(
-var<private> tint_discard : bool = false;
-
-fn f() -> i32 {
-  let marker1 = 0;
-  if (true) {
-    tint_discard = true;
-    return i32();
-  }
-  let marker2 = 0;
-  return 0;
-}
-
-fn g() -> i32 {
-  let marker1 = 0;
-  f();
-  if (tint_discard) {
-    return i32();
-  }
-  let marker2 = 0;
-  return 0;
-}
-
-fn h() -> i32 {
-  let marker1 = 0;
-  g();
-  if (tint_discard) {
-    return i32();
-  }
-  let marker2 = 0;
-  return 0;
-}
-
-fn tint_discard_func() {
-  discard;
-}
-
-@fragment
-fn main(@builtin(position) coord_in : vec4<f32>) -> @location(0) vec4<f32> {
-  let marker1 = 0;
-  h();
-  if (tint_discard) {
-    tint_discard_func();
-    return vec4<f32>();
-  }
-  let marker2 = 0;
-  return vec4<f32>();
-}
-)";
-
-    DataMap data;
-    auto got = Run<PromoteSideEffectsToDecl, UnwindDiscardFunctions>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(UnwindDiscardFunctionsTest, Call_Multiple) {
-    auto* src = R"(
-fn f() {
-  discard;
-  let marker1 = 0;
-}
-
-fn g() {
-  discard;
-  let marker1 = 0;
-}
-
-fn h() {
-  discard;
-  let marker1 = 0;
-}
-
-@fragment
-fn main(@builtin(position) coord_in: vec4<f32>) -> @location(0) vec4<f32> {
-  let marker1 = 0;
-  f();
-  let marker2 = 0;
-  g();
-  let marker3 = 0;
-  h();
-  let marker4 = 0;
-  return vec4<f32>();
-}
-)";
-    auto* expect = R"(
-var<private> tint_discard : bool = false;
-
-fn f() {
-  tint_discard = true;
-  return;
-  let marker1 = 0;
-}
-
-fn g() {
-  tint_discard = true;
-  return;
-  let marker1 = 0;
-}
-
-fn h() {
-  tint_discard = true;
-  return;
-  let marker1 = 0;
-}
-
-fn tint_discard_func() {
-  discard;
-}
-
-@fragment
-fn main(@builtin(position) coord_in : vec4<f32>) -> @location(0) vec4<f32> {
-  let marker1 = 0;
-  f();
-  if (tint_discard) {
-    tint_discard_func();
-    return vec4<f32>();
-  }
-  let marker2 = 0;
-  g();
-  if (tint_discard) {
-    tint_discard_func();
-    return vec4<f32>();
-  }
-  let marker3 = 0;
-  h();
-  if (tint_discard) {
-    tint_discard_func();
-    return vec4<f32>();
-  }
-  let marker4 = 0;
-  return vec4<f32>();
-}
-)";
-
-    DataMap data;
-    auto got = Run<PromoteSideEffectsToDecl, UnwindDiscardFunctions>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(UnwindDiscardFunctionsTest, Call_DiscardFuncDeclaredBelow) {
-    auto* src = R"(
-@fragment
-fn main(@builtin(position) coord_in: vec4<f32>) -> @location(0) vec4<f32> {
-  f();
-  let marker1 = 0;
-  return vec4<f32>();
-}
-
-fn f() {
-  discard;
-  let marker1 = 0;
-}
-)";
-    auto* expect = R"(
-fn tint_discard_func() {
-  discard;
-}
-
-var<private> tint_discard : bool = false;
-
-@fragment
-fn main(@builtin(position) coord_in : vec4<f32>) -> @location(0) vec4<f32> {
-  f();
-  if (tint_discard) {
-    tint_discard_func();
-    return vec4<f32>();
-  }
-  let marker1 = 0;
-  return vec4<f32>();
-}
-
-fn f() {
-  tint_discard = true;
-  return;
-  let marker1 = 0;
-}
-)";
-
-    DataMap data;
-    auto got = Run<PromoteSideEffectsToDecl, UnwindDiscardFunctions>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(UnwindDiscardFunctionsTest, If) {
-    auto* src = R"(
-fn f() -> i32 {
-  if (true) {
-    discard;
-  }
-  return 42;
-}
-
-@fragment
-fn main(@builtin(position) coord_in: vec4<f32>) -> @location(0) vec4<f32> {
-  if (f() == 42) {
-    let marker1 = 0;
-  }
-  return vec4<f32>();
-}
-)";
-    auto* expect = R"(
-var<private> tint_discard : bool = false;
-
-fn f() -> i32 {
-  if (true) {
-    tint_discard = true;
-    return i32();
-  }
-  return 42;
-}
-
-fn tint_discard_func() {
-  discard;
-}
-
-@fragment
-fn main(@builtin(position) coord_in : vec4<f32>) -> @location(0) vec4<f32> {
-  let tint_symbol = f();
-  if (tint_discard) {
-    tint_discard_func();
-    return vec4<f32>();
-  }
-  if ((tint_symbol == 42)) {
-    let marker1 = 0;
-  }
-  return vec4<f32>();
-}
-)";
-
-    DataMap data;
-    auto got = Run<PromoteSideEffectsToDecl, UnwindDiscardFunctions>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(UnwindDiscardFunctionsTest, ElseIf) {
-    auto* src = R"(
-fn f() -> i32 {
-  if (true) {
-    discard;
-  }
-  return 42;
-}
-
-@fragment
-fn main(@builtin(position) coord_in: vec4<f32>) -> @location(0) vec4<f32> {
-  if (true) {
-    let marker1 = 0;
-  } else if (f() == 42) {
-    let marker2 = 0;
-  } else if (true) {
-    let marker3 = 0;
-  }
-  return vec4<f32>();
-}
-)";
-    auto* expect = R"(
-var<private> tint_discard : bool = false;
-
-fn f() -> i32 {
-  if (true) {
-    tint_discard = true;
-    return i32();
-  }
-  return 42;
-}
-
-fn tint_discard_func() {
-  discard;
-}
-
-@fragment
-fn main(@builtin(position) coord_in : vec4<f32>) -> @location(0) vec4<f32> {
-  if (true) {
-    let marker1 = 0;
-  } else {
-    let tint_symbol = f();
-    if (tint_discard) {
-      tint_discard_func();
-      return vec4<f32>();
-    }
-    if ((tint_symbol == 42)) {
-      let marker2 = 0;
-    } else if (true) {
-      let marker3 = 0;
-    }
-  }
-  return vec4<f32>();
-}
-)";
-
-    DataMap data;
-    auto got = Run<PromoteSideEffectsToDecl, UnwindDiscardFunctions>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(UnwindDiscardFunctionsTest, ForLoop_Init_Assignment) {
-    auto* src = R"(
-fn f() -> i32 {
-  if (true) {
-    discard;
-  }
-  return 42;
-}
-
-@fragment
-fn main(@builtin(position) coord_in: vec4<f32>) -> @location(0) vec4<f32> {
-  let marker1 = 0;
-  var a = 0;
-  for (a = f(); ; ) {
-    let marker2 = 0;
-    break;
-  }
-  return vec4<f32>();
-}
-)";
-    auto* expect = R"(
-var<private> tint_discard : bool = false;
-
-fn f() -> i32 {
-  if (true) {
-    tint_discard = true;
-    return i32();
-  }
-  return 42;
-}
-
-fn tint_discard_func() {
-  discard;
-}
-
-@fragment
-fn main(@builtin(position) coord_in : vec4<f32>) -> @location(0) vec4<f32> {
-  let marker1 = 0;
-  var a = 0;
-  var tint_symbol = f();
-  if (tint_discard) {
-    tint_discard_func();
-    return vec4<f32>();
-  }
-  for(a = tint_symbol; ; ) {
-    let marker2 = 0;
-    break;
-  }
-  return vec4<f32>();
-}
-)";
-
-    DataMap data;
-    auto got = Run<PromoteSideEffectsToDecl, UnwindDiscardFunctions>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(UnwindDiscardFunctionsTest, ForLoop_Init_Call) {
-    auto* src = R"(
-fn f() -> i32 {
-  if (true) {
-    discard;
-  }
-  return 42;
-}
-
-@fragment
-fn main(@builtin(position) coord_in: vec4<f32>) -> @location(0) vec4<f32> {
-  let marker1 = 0;
-  for (f(); ; ) {
-    let marker2 = 0;
-    break;
-  }
-  return vec4<f32>();
-}
-)";
-    auto* expect = R"(
-var<private> tint_discard : bool = false;
-
-fn f() -> i32 {
-  if (true) {
-    tint_discard = true;
-    return i32();
-  }
-  return 42;
-}
-
-fn tint_discard_func() {
-  discard;
-}
-
-@fragment
-fn main(@builtin(position) coord_in : vec4<f32>) -> @location(0) vec4<f32> {
-  let marker1 = 0;
-  var tint_symbol = f();
-  if (tint_discard) {
-    tint_discard_func();
-    return vec4<f32>();
-  }
-  for(_ = tint_symbol; ; ) {
-    let marker2 = 0;
-    break;
-  }
-  return vec4<f32>();
-}
-)";
-
-    DataMap data;
-    auto got = Run<PromoteSideEffectsToDecl, UnwindDiscardFunctions>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(UnwindDiscardFunctionsTest, ForLoop_Init_VariableDecl) {
-    auto* src = R"(
-fn f() -> i32 {
-  if (true) {
-    discard;
-  }
-  return 42;
-}
-
-@fragment
-fn main(@builtin(position) coord_in: vec4<f32>) -> @location(0) vec4<f32> {
-  let marker1 = 0;
-  for (let i = f(); ; ) {
-    let marker2 = 0;
-    break;
-  }
-  return vec4<f32>();
-}
-)";
-    auto* expect = R"(
-var<private> tint_discard : bool = false;
-
-fn f() -> i32 {
-  if (true) {
-    tint_discard = true;
-    return i32();
-  }
-  return 42;
-}
-
-fn tint_discard_func() {
-  discard;
-}
-
-@fragment
-fn main(@builtin(position) coord_in : vec4<f32>) -> @location(0) vec4<f32> {
-  let marker1 = 0;
-  var tint_symbol = f();
-  if (tint_discard) {
-    tint_discard_func();
-    return vec4<f32>();
-  }
-  for(let i = tint_symbol; ; ) {
-    let marker2 = 0;
-    break;
-  }
-  return vec4<f32>();
-}
-)";
-
-    DataMap data;
-    auto got = Run<PromoteSideEffectsToDecl, UnwindDiscardFunctions>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(UnwindDiscardFunctionsTest, ForLoop_Cond) {
-    auto* src = R"(
-fn f() -> i32 {
-  if (true) {
-    discard;
-  }
-  return 42;
-}
-
-@fragment
-fn main(@builtin(position) coord_in: vec4<f32>) -> @location(0) vec4<f32> {
-  let marker1 = 0;
-  for (; f() == 42; ) {
-    let marker2 = 0;
-    break;
-  }
-  return vec4<f32>();
-}
-)";
-    auto* expect = R"(
-var<private> tint_discard : bool = false;
-
-fn f() -> i32 {
-  if (true) {
-    tint_discard = true;
-    return i32();
-  }
-  return 42;
-}
-
-fn tint_discard_func() {
-  discard;
-}
-
-@fragment
-fn main(@builtin(position) coord_in : vec4<f32>) -> @location(0) vec4<f32> {
-  let marker1 = 0;
-  loop {
-    let tint_symbol = f();
-    if (tint_discard) {
-      tint_discard_func();
-      return vec4<f32>();
-    }
-    if (!((tint_symbol == 42))) {
-      break;
-    }
-    {
-      let marker2 = 0;
-      break;
-    }
-  }
-  return vec4<f32>();
-}
-)";
-
-    DataMap data;
-    auto got = Run<PromoteSideEffectsToDecl, UnwindDiscardFunctions>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(UnwindDiscardFunctionsTest, ForLoop_Cont) {
-    auto* src = R"(
-fn f() -> i32 {
-  if (true) {
-    discard;
-  }
-  return 42;
-}
-
-@fragment
-fn main(@builtin(position) coord_in: vec4<f32>) -> @location(0) vec4<f32> {
-  let marker1 = 0;
-  for (; ; f()) {
-    let marker2 = 0;
-    break;
-  }
-  return vec4<f32>();
-}
-)";
-    auto* expect =
-        R"(test:12:12 error: cannot call a function that may discard inside a continuing block
-  for (; ; f()) {
-           ^
-)";
-
-    DataMap data;
-    auto got = Run<PromoteSideEffectsToDecl, UnwindDiscardFunctions>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(UnwindDiscardFunctionsTest, While_Cond) {
-    auto* src = R"(
-fn f() -> i32 {
-  if (true) {
-    discard;
-  }
-  return 42;
-}
-
-@fragment
-fn main(@builtin(position) coord_in: vec4<f32>) -> @location(0) vec4<f32> {
-  let marker1 = 0;
-  while (f() == 42) {
-    let marker2 = 0;
-    break;
-  }
-  return vec4<f32>();
-}
-)";
-    auto* expect = R"(
-var<private> tint_discard : bool = false;
-
-fn f() -> i32 {
-  if (true) {
-    tint_discard = true;
-    return i32();
-  }
-  return 42;
-}
-
-fn tint_discard_func() {
-  discard;
-}
-
-@fragment
-fn main(@builtin(position) coord_in : vec4<f32>) -> @location(0) vec4<f32> {
-  let marker1 = 0;
-  loop {
-    let tint_symbol = f();
-    if (tint_discard) {
-      tint_discard_func();
-      return vec4<f32>();
-    }
-    if (!((tint_symbol == 42))) {
-      break;
-    }
-    {
-      let marker2 = 0;
-      break;
-    }
-  }
-  return vec4<f32>();
-}
-)";
-
-    DataMap data;
-    auto got = Run<PromoteSideEffectsToDecl, UnwindDiscardFunctions>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(UnwindDiscardFunctionsTest, Switch) {
-    auto* src = R"(
-fn f() -> i32 {
-  if (true) {
-    discard;
-  }
-  return 42;
-}
-
-@fragment
-fn main(@builtin(position) coord_in: vec4<f32>) -> @location(0) vec4<f32> {
-  switch (f()) {
-    case 0: {
-      let marker1 = 0;
-    }
-    case 1: {
-      let marker2 = 0;
-    }
-    case 42: {
-      let marker3 = 0;
-    }
-    default: {
-      let marker4 = 0;
-    }
-  }
-  return vec4<f32>();
-}
-)";
-    auto* expect = R"(
-var<private> tint_discard : bool = false;
-
-fn f() -> i32 {
-  if (true) {
-    tint_discard = true;
-    return i32();
-  }
-  return 42;
-}
-
-fn tint_discard_func() {
-  discard;
-}
-
-@fragment
-fn main(@builtin(position) coord_in : vec4<f32>) -> @location(0) vec4<f32> {
-  var tint_symbol = f();
-  if (tint_discard) {
-    tint_discard_func();
-    return vec4<f32>();
-  }
-  switch(tint_symbol) {
-    case 0: {
-      let marker1 = 0;
-    }
-    case 1: {
-      let marker2 = 0;
-    }
-    case 42: {
-      let marker3 = 0;
-    }
-    default: {
-      let marker4 = 0;
-    }
-  }
-  return vec4<f32>();
-}
-)";
-
-    DataMap data;
-    auto got = Run<PromoteSideEffectsToDecl, UnwindDiscardFunctions>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(UnwindDiscardFunctionsTest, Return) {
-    auto* src = R"(
-struct S {
-  x : i32,
-  y : i32,
-};
-
-fn f() -> S {
-  if (true) {
-    discard;
-  }
-  var s : S;
-  return s;
-}
-
-fn g() -> S {
-  return f();
-}
-
-@fragment
-fn main(@builtin(position) coord_in: vec4<f32>) -> @location(0) vec4<f32> {
-  let marker1 = 0;
-  g();
-  return vec4<f32>();
-}
-)";
-    auto* expect = R"(
-struct S {
-  x : i32,
-  y : i32,
-}
-
-var<private> tint_discard : bool = false;
-
-fn f() -> S {
-  if (true) {
-    tint_discard = true;
-    return S();
-  }
-  var s : S;
-  return s;
-}
-
-fn g() -> S {
-  var tint_symbol = f();
-  if (tint_discard) {
-    return S();
-  }
-  return tint_symbol;
-}
-
-fn tint_discard_func() {
-  discard;
-}
-
-@fragment
-fn main(@builtin(position) coord_in : vec4<f32>) -> @location(0) vec4<f32> {
-  let marker1 = 0;
-  g();
-  if (tint_discard) {
-    tint_discard_func();
-    return vec4<f32>();
-  }
-  return vec4<f32>();
-}
-)";
-
-    DataMap data;
-    auto got = Run<PromoteSideEffectsToDecl, UnwindDiscardFunctions>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(UnwindDiscardFunctionsTest, VariableDecl) {
-    auto* src = R"(
-fn f() -> i32 {
-  if (true) {
-    discard;
-  }
-  return 42;
-}
-
-@fragment
-fn main(@builtin(position) coord_in: vec4<f32>) -> @location(0) vec4<f32> {
-  var a = f();
-  let marker1 = 0;
-  return vec4<f32>();
-}
-)";
-    auto* expect = R"(
-var<private> tint_discard : bool = false;
-
-fn f() -> i32 {
-  if (true) {
-    tint_discard = true;
-    return i32();
-  }
-  return 42;
-}
-
-fn tint_discard_func() {
-  discard;
-}
-
-@fragment
-fn main(@builtin(position) coord_in : vec4<f32>) -> @location(0) vec4<f32> {
-  var a = f();
-  if (tint_discard) {
-    tint_discard_func();
-    return vec4<f32>();
-  }
-  let marker1 = 0;
-  return vec4<f32>();
-}
-)";
-
-    DataMap data;
-    auto got = Run<PromoteSideEffectsToDecl, UnwindDiscardFunctions>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(UnwindDiscardFunctionsTest, Assignment_RightDiscard) {
-    auto* src = R"(
-fn f() -> i32 {
-  if (true) {
-    discard;
-  }
-  return 42;
-}
-
-@fragment
-fn main(@builtin(position) coord_in: vec4<f32>) -> @location(0) vec4<f32> {
-  var a : i32;
-  a = f();
-  let marker1 = 0;
-  return vec4<f32>();
-}
-)";
-    auto* expect = R"(
-var<private> tint_discard : bool = false;
-
-fn f() -> i32 {
-  if (true) {
-    tint_discard = true;
-    return i32();
-  }
-  return 42;
-}
-
-fn tint_discard_func() {
-  discard;
-}
-
-@fragment
-fn main(@builtin(position) coord_in : vec4<f32>) -> @location(0) vec4<f32> {
-  var a : i32;
-  a = f();
-  if (tint_discard) {
-    tint_discard_func();
-    return vec4<f32>();
-  }
-  let marker1 = 0;
-  return vec4<f32>();
-}
-)";
-
-    DataMap data;
-    auto got = Run<PromoteSideEffectsToDecl, UnwindDiscardFunctions>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(UnwindDiscardFunctionsTest, Assignment_LeftDiscard) {
-    auto* src = R"(
-fn f() -> i32 {
-  if (true) {
-    discard;
-  }
-  return 0;
-}
-
-@fragment
-fn main(@builtin(position) coord_in: vec4<f32>) -> @location(0) vec4<f32> {
-  var b = array<i32, 10>();
-  b[f()] = 10;
-  let marker1 = 0;
-  return vec4<f32>();
-}
-)";
-    auto* expect = R"(
-var<private> tint_discard : bool = false;
-
-fn f() -> i32 {
-  if (true) {
-    tint_discard = true;
-    return i32();
-  }
-  return 0;
-}
-
-fn tint_discard_func() {
-  discard;
-}
-
-@fragment
-fn main(@builtin(position) coord_in : vec4<f32>) -> @location(0) vec4<f32> {
-  var b = array<i32, 10>();
-  let tint_symbol = f();
-  if (tint_discard) {
-    tint_discard_func();
-    return vec4<f32>();
-  }
-  b[tint_symbol] = 10;
-  let marker1 = 0;
-  return vec4<f32>();
-}
-)";
-
-    DataMap data;
-    auto got = Run<PromoteSideEffectsToDecl, UnwindDiscardFunctions>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(UnwindDiscardFunctionsTest, Assignment_BothDiscard) {
-    auto* src = R"(
-fn f() -> i32 {
-  if (true) {
-    discard;
-  }
-  return 0;
-}
-
-fn g() -> i32 {
-  if (true) {
-    discard;
-  }
-  return 0;
-}
-
-@fragment
-fn main(@builtin(position) coord_in: vec4<f32>) -> @location(0) vec4<f32> {
-  var b = array<i32, 10>();
-  b[f()] = g();
-  let marker1 = 0;
-  return vec4<f32>();
-}
-)";
-    auto* expect = R"(
-var<private> tint_discard : bool = false;
-
-fn f() -> i32 {
-  if (true) {
-    tint_discard = true;
-    return i32();
-  }
-  return 0;
-}
-
-fn g() -> i32 {
-  if (true) {
-    tint_discard = true;
-    return i32();
-  }
-  return 0;
-}
-
-fn tint_discard_func() {
-  discard;
-}
-
-@fragment
-fn main(@builtin(position) coord_in : vec4<f32>) -> @location(0) vec4<f32> {
-  var b = array<i32, 10>();
-  let tint_symbol = g();
-  if (tint_discard) {
-    tint_discard_func();
-    return vec4<f32>();
-  }
-  let tint_symbol_1 = f();
-  if (tint_discard) {
-    tint_discard_func();
-    return vec4<f32>();
-  }
-  b[tint_symbol_1] = tint_symbol;
-  let marker1 = 0;
-  return vec4<f32>();
-}
-)";
-
-    DataMap data;
-    auto got = Run<PromoteSideEffectsToDecl, UnwindDiscardFunctions>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(UnwindDiscardFunctionsTest, Binary_Arith_MultipleDiscardFuncs) {
-    auto* src = R"(
-fn f() -> i32 {
-  if (true) {
-    discard;
-  }
-  return 0;
-}
-
-fn g() -> i32 {
-  if (true) {
-    discard;
-  }
-  return 0;
-}
-
-fn h() -> i32{
-  if (true) {
-    discard;
-  }
-  return 0;
-}
-
-@fragment
-fn main(@builtin(position) coord_in: vec4<f32>) -> @location(0) vec4<f32> {
-  if ((f() + g() + h()) == 0) {
-    let marker1 = 0;
-  }
-  return vec4<f32>();
-}
-)";
-    auto* expect = R"(
-var<private> tint_discard : bool = false;
-
-fn f() -> i32 {
-  if (true) {
-    tint_discard = true;
-    return i32();
-  }
-  return 0;
-}
-
-fn g() -> i32 {
-  if (true) {
-    tint_discard = true;
-    return i32();
-  }
-  return 0;
-}
-
-fn h() -> i32 {
-  if (true) {
-    tint_discard = true;
-    return i32();
-  }
-  return 0;
-}
-
-fn tint_discard_func() {
-  discard;
-}
-
-@fragment
-fn main(@builtin(position) coord_in : vec4<f32>) -> @location(0) vec4<f32> {
-  let tint_symbol = f();
-  if (tint_discard) {
-    tint_discard_func();
-    return vec4<f32>();
-  }
-  let tint_symbol_1 = g();
-  if (tint_discard) {
-    tint_discard_func();
-    return vec4<f32>();
-  }
-  let tint_symbol_2 = h();
-  if (tint_discard) {
-    tint_discard_func();
-    return vec4<f32>();
-  }
-  if ((((tint_symbol + tint_symbol_1) + tint_symbol_2) == 0)) {
-    let marker1 = 0;
-  }
-  return vec4<f32>();
-}
-)";
-
-    DataMap data;
-    auto got = Run<PromoteSideEffectsToDecl, UnwindDiscardFunctions>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(UnwindDiscardFunctionsTest, Binary_Logical_MultipleDiscardFuncs) {
-    auto* src = R"(
-fn f() -> i32 {
-  if (true) {
-    discard;
-  }
-  return 0;
-}
-
-fn g() -> i32 {
-  if (true) {
-    discard;
-  }
-  return 0;
-}
-
-fn h() -> i32{
-  if (true) {
-    discard;
-  }
-  return 0;
-}
-
-@fragment
-fn main(@builtin(position) coord_in: vec4<f32>) -> @location(0) vec4<f32> {
-  if (f() == 1 && g() == 2 && h() == 3) {
-    let marker1 = 0;
-  }
-  return vec4<f32>();
-}
-)";
-    auto* expect = R"(
-var<private> tint_discard : bool = false;
-
-fn f() -> i32 {
-  if (true) {
-    tint_discard = true;
-    return i32();
-  }
-  return 0;
-}
-
-fn g() -> i32 {
-  if (true) {
-    tint_discard = true;
-    return i32();
-  }
-  return 0;
-}
-
-fn h() -> i32 {
-  if (true) {
-    tint_discard = true;
-    return i32();
-  }
-  return 0;
-}
-
-fn tint_discard_func() {
-  discard;
-}
-
-@fragment
-fn main(@builtin(position) coord_in : vec4<f32>) -> @location(0) vec4<f32> {
-  let tint_symbol_2 = f();
-  if (tint_discard) {
-    tint_discard_func();
-    return vec4<f32>();
-  }
-  var tint_symbol_1 = (tint_symbol_2 == 1);
-  if (tint_symbol_1) {
-    let tint_symbol_3 = g();
-    if (tint_discard) {
-      tint_discard_func();
-      return vec4<f32>();
-    }
-    tint_symbol_1 = (tint_symbol_3 == 2);
-  }
-  var tint_symbol = tint_symbol_1;
-  if (tint_symbol) {
-    let tint_symbol_4 = h();
-    if (tint_discard) {
-      tint_discard_func();
-      return vec4<f32>();
-    }
-    tint_symbol = (tint_symbol_4 == 3);
-  }
-  if (tint_symbol) {
-    let marker1 = 0;
-  }
-  return vec4<f32>();
-}
-)";
-
-    DataMap data;
-    auto got = Run<PromoteSideEffectsToDecl, UnwindDiscardFunctions>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(UnwindDiscardFunctionsTest, EnsureNoSymbolCollision) {
-    auto* src = R"(
-var<private> tint_discard_func : i32;
-var<private> tint_discard : i32;
-
-fn f() {
-  discard;
-  let marker1 = 0;
-}
-
-@fragment
-fn main(@builtin(position) coord_in: vec4<f32>) -> @location(0) vec4<f32> {
-  f();
-  let marker1 = 0;
-  return vec4<f32>();
-}
-)";
-    auto* expect = R"(
-var<private> tint_discard_func : i32;
-
-var<private> tint_discard : i32;
-
-var<private> tint_discard_1 : bool = false;
-
-fn f() {
-  tint_discard_1 = true;
-  return;
-  let marker1 = 0;
-}
-
-fn tint_discard_func_1() {
-  discard;
-}
-
-@fragment
-fn main(@builtin(position) coord_in : vec4<f32>) -> @location(0) vec4<f32> {
-  f();
-  if (tint_discard_1) {
-    tint_discard_func_1();
-    return vec4<f32>();
-  }
-  let marker1 = 0;
-  return vec4<f32>();
-}
-)";
-
-    DataMap data;
-    auto got = Run<PromoteSideEffectsToDecl, UnwindDiscardFunctions>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-}  // namespace
-}  // namespace tint::transform
diff --git a/src/tint/utils/bitset.h b/src/tint/utils/bitset.h
index 86dccd6..a37f9a2 100644
--- a/src/tint/utils/bitset.h
+++ b/src/tint/utils/bitset.h
@@ -92,6 +92,25 @@
         return Bit{word, mask};
     }
 
+    /// Const index operator
+    /// @param index the index of the bit to access
+    /// @return bool value of the indexed bit
+    bool operator[](size_t index) const {
+        const auto& word = vec_[index / kWordBits];
+        auto mask = static_cast<Word>(1) << (index % kWordBits);
+        return word & mask;
+    }
+
+    /// @returns true iff the all bits are unset (0)
+    bool AllBitsZero() const {
+        for (auto word : vec_) {
+            if (word) {
+                return false;
+            }
+        }
+        return true;
+    }
+
   private:
     Vector<size_t, NumWords(N)> vec_;
     size_t len_ = 0;
diff --git a/src/tint/utils/bitset_test.cc b/src/tint/utils/bitset_test.cc
index b07cf74..550d946 100644
--- a/src/tint/utils/bitset_test.cc
+++ b/src/tint/utils/bitset_test.cc
@@ -26,6 +26,32 @@
     EXPECT_EQ(bits.Length(), 100u);
 }
 
+TEST(Bitset, AllBitsZero) {
+    Bitset<8> bits;
+    EXPECT_TRUE(bits.AllBitsZero());
+
+    bits.Resize(4u);
+    EXPECT_TRUE(bits.AllBitsZero());
+
+    bits.Resize(100u);
+    EXPECT_TRUE(bits.AllBitsZero());
+
+    bits[63] = true;
+    EXPECT_FALSE(bits.AllBitsZero());
+
+    bits.Resize(60);
+    EXPECT_TRUE(bits.AllBitsZero());
+
+    bits.Resize(64);
+    EXPECT_TRUE(bits.AllBitsZero());
+
+    bits[4] = true;
+    EXPECT_FALSE(bits.AllBitsZero());
+
+    bits.Resize(8);
+    EXPECT_FALSE(bits.AllBitsZero());
+}
+
 TEST(Bitset, InitCleared_NoSpill) {
     Bitset<256> bits;
     bits.Resize(256);
diff --git a/src/tint/utils/compiler_macros.h b/src/tint/utils/compiler_macros.h
index b3cca3c..b0df241 100644
--- a/src/tint/utils/compiler_macros.h
+++ b/src/tint/utils/compiler_macros.h
@@ -50,7 +50,7 @@
 #define TINT_DISABLE_WARNING_OLD_STYLE_CAST _Pragma("clang diagnostic ignored \"-Wold-style-cast\"")
 #define TINT_DISABLE_WARNING_SIGN_CONVERSION \
     _Pragma("clang diagnostic ignored \"-Wsign-conversion\"")
-#define TINT_DISABLE_WARNING_UNREACHABLE_CODE    /* currently no-op */
+#define TINT_DISABLE_WARNING_UNREACHABLE_CODE /* currently no-op */
 #define TINT_DISABLE_WARNING_WEAK_VTABLES _Pragma("clang diagnostic ignored \"-Wweak-vtables\"")
 
 // clang-format off
diff --git a/src/tint/utils/crc32.h b/src/tint/utils/crc32.h
index 5123612..c9697b7 100644
--- a/src/tint/utils/crc32.h
+++ b/src/tint/utils/crc32.h
@@ -16,56 +16,62 @@
 #define SRC_TINT_UTILS_CRC32_H_
 
 #include <stdint.h>
+#include <cstddef>
 
 namespace tint::utils {
 
-/// @returns the CRC32 of the string `s`.
+constexpr uint32_t kCRC32LUT[] = {
+    0,          0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
+    0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
+    0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+    0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
+    0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+    0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+    0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
+    0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
+    0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+    0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+    0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
+    0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+    0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
+    0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
+    0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+    0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
+    0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
+    0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+    0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
+    0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+    0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+    0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
+    0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
+    0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+    0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+    0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
+    0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+    0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
+    0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
+    0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+    0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
+    0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d};
+
+/// @returns the CRC32 of the string @p s.
 /// @note this function can be used to calculate the CRC32 of a string literal
 /// at compile time.
 /// @see https://en.wikipedia.org/wiki/Cyclic_redundancy_check#CRC-32_algorithm
 constexpr uint32_t CRC32(const char* s) {
-    constexpr uint32_t kLUT[] = {
-        0,          0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535,
-        0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd,
-        0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d,
-        0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
-        0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
-        0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
-        0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac,
-        0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
-        0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab,
-        0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
-        0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb,
-        0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
-        0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea,
-        0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce,
-        0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
-        0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
-        0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409,
-        0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
-        0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739,
-        0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
-        0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268,
-        0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0,
-        0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8,
-        0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
-        0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
-        0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703,
-        0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7,
-        0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
-        0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae,
-        0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
-        0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6,
-        0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
-        0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d,
-        0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5,
-        0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
-        0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
-        0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d};
-
     uint32_t crc = 0xffffffff;
     for (auto* p = s; *p != '\0'; ++p) {
-        crc = (crc >> 8) ^ kLUT[static_cast<uint8_t>(crc) ^ static_cast<uint8_t>(*p)];
+        crc = (crc >> 8) ^ kCRC32LUT[static_cast<uint8_t>(crc) ^ static_cast<uint8_t>(*p)];
+    }
+    return crc ^ 0xffffffff;
+}
+
+/// @returns the CRC32 of the data at @p ptr of size @p size.
+inline uint32_t CRC32(const void* ptr, size_t size) {
+    auto* p = static_cast<const uint8_t*>(ptr);
+    uint32_t crc = 0xffffffff;
+    while (size--) {
+        crc = (crc >> 8) ^ kCRC32LUT[static_cast<uint8_t>(crc) ^ *p++];
     }
     return crc ^ 0xffffffff;
 }
diff --git a/src/tint/utils/hash.h b/src/tint/utils/hash.h
index 717b35f..89cf0f0 100644
--- a/src/tint/utils/hash.h
+++ b/src/tint/utils/hash.h
@@ -157,9 +157,9 @@
 template <typename T>
 struct UnorderedKeyWrapper {
     /// The wrapped value
-    const T value;
+    T value;
     /// The hash of value
-    const size_t hash;
+    size_t hash;
 
     /// Constructor
     /// @param v the value to wrap
diff --git a/src/tint/utils/hashmap_base.h b/src/tint/utils/hashmap_base.h
index ca0712a..ee52dad 100644
--- a/src/tint/utils/hashmap_base.h
+++ b/src/tint/utils/hashmap_base.h
@@ -524,7 +524,7 @@
     /// 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) {
+    void InsertShuffle(size_t start, Slot&& evicted) {
         Scan(start, [&](size_t, size_t index) {
             auto& slot = slots_[index];
 
diff --git a/src/tint/utils/string.cc b/src/tint/utils/string.cc
index a0d3b85..354ad96 100644
--- a/src/tint/utils/string.cc
+++ b/src/tint/utils/string.cc
@@ -19,7 +19,7 @@
 
 namespace tint::utils {
 
-size_t Distance(const std::string& str_a, const std::string& str_b) {
+size_t Distance(std::string_view str_a, std::string_view str_b) {
     const auto len_a = str_a.size();
     const auto len_b = str_b.size();
 
diff --git a/src/tint/utils/string.h b/src/tint/utils/string.h
index 9f65395..dd37396 100644
--- a/src/tint/utils/string.h
+++ b/src/tint/utils/string.h
@@ -25,10 +25,10 @@
 /// @param replacement the replacement string to use instead of `substr`
 /// @returns `str` with all occurrences of `substr` replaced with `replacement`
 [[nodiscard]] inline std::string ReplaceAll(std::string str,
-                                            const std::string& substr,
-                                            const std::string& replacement) {
+                                            std::string_view substr,
+                                            std::string_view replacement) {
     size_t pos = 0;
-    while ((pos = str.find(substr, pos)) != std::string::npos) {
+    while ((pos = str.find(substr, pos)) != std::string_view::npos) {
         str.replace(pos, substr.length(), replacement);
         pos += replacement.length();
     }
@@ -44,10 +44,17 @@
     return s.str();
 }
 
+/// @param str the input string
+/// @param prefix the prefix string
+/// @returns true iff @p str has the prefix @p prefix
+inline size_t HasPrefix(std::string_view str, std::string_view prefix) {
+    return str.compare(0, prefix.size(), prefix) == 0;
+}
+
 /// @param a the first string
 /// @param b the second string
 /// @returns the Levenshtein distance between @p a and @p b
-size_t Distance(const std::string& a, const std::string& b);
+size_t Distance(std::string_view a, std::string_view b);
 
 }  // namespace tint::utils
 
diff --git a/src/tint/utils/string_test.cc b/src/tint/utils/string_test.cc
index 6c8008c..bbf8e0f 100644
--- a/src/tint/utils/string_test.cc
+++ b/src/tint/utils/string_test.cc
@@ -37,6 +37,15 @@
     EXPECT_EQ("hello", ToString("hello"));
 }
 
+TEST(StringTest, HasPrefix) {
+    EXPECT_TRUE(HasPrefix("abc", "a"));
+    EXPECT_TRUE(HasPrefix("abc", "ab"));
+    EXPECT_TRUE(HasPrefix("abc", "abc"));
+    EXPECT_FALSE(HasPrefix("abc", "abc1"));
+    EXPECT_FALSE(HasPrefix("abc", "ac"));
+    EXPECT_FALSE(HasPrefix("abc", "b"));
+}
+
 TEST(StringTest, Distance) {
     EXPECT_EQ(Distance("hello world", "hello world"), 0u);
     EXPECT_EQ(Distance("hello world", "helloworld"), 1u);
diff --git a/src/tint/writer/glsl/generator_impl.cc b/src/tint/writer/glsl/generator_impl.cc
index 50cdbcf..86ba657 100644
--- a/src/tint/writer/glsl/generator_impl.cc
+++ b/src/tint/writer/glsl/generator_impl.cc
@@ -22,7 +22,6 @@
 #include <vector>
 
 #include "src/tint/ast/call_statement.h"
-#include "src/tint/ast/fallthrough_statement.h"
 #include "src/tint/ast/id_attribute.h"
 #include "src/tint/ast/internal_attribute.h"
 #include "src/tint/ast/interpolate_attribute.h"
@@ -54,6 +53,7 @@
 #include "src/tint/transform/canonicalize_entry_point_io.h"
 #include "src/tint/transform/combine_samplers.h"
 #include "src/tint/transform/decompose_memory_access.h"
+#include "src/tint/transform/demote_to_helper.h"
 #include "src/tint/transform/disable_uniformity_analysis.h"
 #include "src/tint/transform/expand_compound_assignment.h"
 #include "src/tint/transform/manager.h"
@@ -66,7 +66,6 @@
 #include "src/tint/transform/single_entry_point.h"
 #include "src/tint/transform/std140.h"
 #include "src/tint/transform/unshadow.h"
-#include "src/tint/transform/unwind_discard_functions.h"
 #include "src/tint/transform/zero_init_workgroup_memory.h"
 #include "src/tint/utils/defer.h"
 #include "src/tint/utils/map.h"
@@ -104,8 +103,8 @@
 
 const char kTempNamePrefix[] = "tint_tmp";
 
-bool last_is_break_or_fallthrough(const ast::BlockStatement* stmts) {
-    return IsAnyOf<ast::BreakStatement, ast::FallthroughStatement>(stmts->Last());
+bool last_is_break(const ast::BlockStatement* stmts) {
+    return IsAnyOf<ast::BreakStatement>(stmts->Last());
 }
 
 const char* convert_texel_format_to_glsl(const ast::TexelFormat format) {
@@ -182,6 +181,9 @@
 
     manager.Add<transform::DisableUniformityAnalysis>();
 
+    // ExpandCompoundAssignment must come before BuiltinPolyfill
+    manager.Add<transform::ExpandCompoundAssignment>();
+
     {  // Builtin polyfills
         transform::BuiltinPolyfill::Builtins polyfills;
         polyfills.acosh = transform::BuiltinPolyfill::Level::kRangeCheck;
@@ -193,6 +195,7 @@
         polyfills.first_leading_bit = true;
         polyfills.first_trailing_bit = true;
         polyfills.insert_bits = transform::BuiltinPolyfill::Level::kClampParameters;
+        polyfills.int_div_mod = true;
         polyfills.saturate = true;
         polyfills.texture_sample_base_clamp_to_edge_2d_f32 = true;
         data.Add<transform::BuiltinPolyfill::Config>(polyfills);
@@ -214,10 +217,11 @@
         manager.Add<transform::ZeroInitWorkgroupMemory>();
     }
     manager.Add<transform::CanonicalizeEntryPointIO>();
-    manager.Add<transform::ExpandCompoundAssignment>();
     manager.Add<transform::PromoteSideEffectsToDecl>();
     manager.Add<transform::PadStructs>();
-    manager.Add<transform::UnwindDiscardFunctions>();
+
+    // DemoteToHelper must come after PromoteSideEffectsToDecl and ExpandCompoundAssignment.
+    manager.Add<transform::DemoteToHelper>();
 
     manager.Add<transform::RemovePhonies>();
 
@@ -935,9 +939,7 @@
             return true;
         }
         case sem::BuiltinType::kAtomicCompareExchangeWeak: {
-            // Emit the builtin return type unique to this overload. This does not
-            // exist in the AST, so it will not be generated in Generate().
-            if (!EmitStructTypeOnce(&helpers_, builtin->ReturnType()->As<sem::Struct>())) {
+            if (!EmitStructType(&helpers_, builtin->ReturnType()->As<sem::Struct>())) {
                 return false;
             }
 
@@ -1259,7 +1261,7 @@
                 }
                 l << " result;";
             }
-            line(b) << "result.sig = frexp(" << params[0] << ", result.exp);";
+            line(b) << "result.fract = frexp(" << params[0] << ", result.exp);";
             line(b) << "return result;";
             return true;
         });
@@ -1816,7 +1818,7 @@
         if (!EmitStatements(stmt->body->statements)) {
             return false;
         }
-        if (!last_is_break_or_fallthrough(stmt->body)) {
+        if (!last_is_break(stmt->body)) {
             line() << "break;";
         }
     }
@@ -2369,10 +2371,12 @@
             return true;
         },
         [&](const sem::Struct* s) {
-            if (!EmitType(out, s, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
+            if (!EmitStructType(&helpers_, s)) {
                 return false;
             }
 
+            out << StructName(s);
+
             ScopedParen sp(out);
 
             for (size_t i = 0; i < s->Members().size(); i++) {
@@ -2692,14 +2696,24 @@
     }
     out << ".";
 
-    // Swizzles output the name directly
-    if (builder_.Sem().Get(expr)->Is<sem::Swizzle>()) {
-        out << builder_.Symbols().NameFor(expr->member->symbol);
-    } else if (!EmitExpression(out, expr->member)) {
-        return false;
-    }
+    auto* sem = builder_.Sem().Get(expr);
 
-    return true;
+    return Switch(
+        sem,
+        [&](const sem::Swizzle*) {
+            // Swizzles output the name directly
+            out << builder_.Symbols().NameFor(expr->member->symbol);
+            return true;
+        },
+        [&](const sem::StructMemberAccess* member_access) {
+            out << program_->Symbols().NameFor(member_access->Member()->Name());
+            return true;
+        },
+        [&](Default) {
+            TINT_ICE(Writer, diagnostics_)
+                << "unknown member access type: " << sem->TypeInfo().name;
+            return false;
+        });
 }
 
 bool GeneratorImpl::EmitReturn(const ast::ReturnStatement* stmt) {
@@ -2733,10 +2747,6 @@
         },
         [&](const ast::ContinueStatement* c) { return EmitContinue(c); },
         [&](const ast::DiscardStatement* d) { return EmitDiscard(d); },
-        [&](const ast::FallthroughStatement*) {
-            line() << "/* fallthrough */";
-            return true;
-        },
         [&](const ast::IfStatement* i) { return EmitIf(i); },
         [&](const ast::LoopStatement* l) { return EmitLoop(l); },
         [&](const ast::ForLoopStatement* l) { return EmitForLoop(l); },
@@ -2988,6 +2998,11 @@
 }
 
 bool GeneratorImpl::EmitStructType(TextBuffer* b, const sem::Struct* str) {
+    auto it = emitted_structs_.emplace(str);
+    if (!it.second) {
+        return true;
+    }
+
     auto address_space_uses = str->AddressSpaceUsage();
     line(b) << "struct " << StructName(str) << " {";
     EmitStructMembers(b, str);
@@ -2997,14 +3012,6 @@
     return true;
 }
 
-bool GeneratorImpl::EmitStructTypeOnce(TextBuffer* buffer, const sem::Struct* str) {
-    auto it = emitted_structs_.emplace(str);
-    if (!it.second) {
-        return true;
-    }
-    return EmitStructType(buffer, str);
-}
-
 bool GeneratorImpl::EmitStructMembers(TextBuffer* b, const sem::Struct* str) {
     ScopedIndent si(b);
     for (auto* mem : str->Members()) {
diff --git a/src/tint/writer/glsl/generator_impl.h b/src/tint/writer/glsl/generator_impl.h
index c34880d..24d8357 100644
--- a/src/tint/writer/glsl/generator_impl.h
+++ b/src/tint/writer/glsl/generator_impl.h
@@ -430,17 +430,12 @@
                          ast::AddressSpace address_space,
                          ast::Access access,
                          const std::string& name);
-    /// Handles generating a structure declaration
+    /// Handles generating a structure declaration. If the structure has already been emitted, then
+    /// this function will simply return `true` without emitting anything.
     /// @param buffer the text buffer that the type declaration will be written to
     /// @param ty the struct to generate
     /// @returns true if the struct is emitted
     bool EmitStructType(TextBuffer* buffer, const sem::Struct* ty);
-    /// Handles generating a structure declaration only the first time called. Subsequent calls are
-    /// a no-op and return true.
-    /// @param buffer the text buffer that the type declaration will be written to
-    /// @param ty the struct to generate
-    /// @returns true if the struct is emitted
-    bool EmitStructTypeOnce(TextBuffer* buffer, const sem::Struct* ty);
     /// Handles generating the members of a structure
     /// @param buffer the text buffer that the struct members will be written to
     /// @param ty the struct to generate
diff --git a/src/tint/writer/glsl/generator_impl_builtin_test.cc b/src/tint/writer/glsl/generator_impl_builtin_test.cc
index 61d97b0..7d0a72e 100644
--- a/src/tint/writer/glsl/generator_impl_builtin_test.cc
+++ b/src/tint/writer/glsl/generator_impl_builtin_test.cc
@@ -416,9 +416,9 @@
     EXPECT_EQ(out.str(), "((a) * (b) + (c))");
 }
 
-TEST_F(GlslGeneratorImplTest_Builtin, Modf_Scalar_f32) {
-    auto* call = Call("modf", 1_f);
-    WrapInFunction(CallStmt(call));
+TEST_F(GlslGeneratorImplTest_Builtin, Runtime_Modf_Scalar_f32) {
+    WrapInFunction(Decl(Let("f", Expr(1.5_f))),  //
+                   Decl(Let("v", Call("modf", "f"))));
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
@@ -438,7 +438,8 @@
 
 
 void test_function() {
-  tint_modf(1.0f);
+  float f = 1.5f;
+  modf_result v = tint_modf(f);
 }
 
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
@@ -449,11 +450,11 @@
 )");
 }
 
-TEST_F(GlslGeneratorImplTest_Builtin, Modf_Scalar_f16) {
+TEST_F(GlslGeneratorImplTest_Builtin, Runtime_Modf_Scalar_f16) {
     Enable(ast::Extension::kF16);
 
-    auto* call = Call("modf", 1_h);
-    WrapInFunction(CallStmt(call));
+    WrapInFunction(Decl(Let("f", Expr(1.5_h))),  //
+                   Decl(Let("v", Call("modf", "f"))));
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
@@ -474,7 +475,8 @@
 
 
 void test_function() {
-  tint_modf(1.0hf);
+  float16_t f = 1.5hf;
+  modf_result_f16 v = tint_modf(f);
 }
 
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
@@ -485,9 +487,9 @@
 )");
 }
 
-TEST_F(GlslGeneratorImplTest_Builtin, Modf_Vector_f32) {
-    auto* call = Call("modf", vec3<f32>());
-    WrapInFunction(CallStmt(call));
+TEST_F(GlslGeneratorImplTest_Builtin, Runtime_Modf_Vector_f32) {
+    WrapInFunction(Decl(Let("f", vec3<f32>(1.5_f, 2.5_f, 3.5_f))),  //
+                   Decl(Let("v", Call("modf", "f"))));
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
@@ -507,7 +509,8 @@
 
 
 void test_function() {
-  tint_modf(vec3(0.0f));
+  vec3 f = vec3(1.5f, 2.5f, 3.5f);
+  modf_result_vec3 v = tint_modf(f);
 }
 
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
@@ -518,11 +521,11 @@
 )");
 }
 
-TEST_F(GlslGeneratorImplTest_Builtin, Modf_Vector_f16) {
+TEST_F(GlslGeneratorImplTest_Builtin, Runtime_Modf_Vector_f16) {
     Enable(ast::Extension::kF16);
 
-    auto* call = Call("modf", vec3<f16>());
-    WrapInFunction(CallStmt(call));
+    WrapInFunction(Decl(Let("f", vec3<f16>(1.5_h, 2.5_h, 3.5_h))),  //
+                   Decl(Let("v", Call("modf", "f"))));
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
@@ -543,7 +546,118 @@
 
 
 void test_function() {
-  tint_modf(f16vec3(0.0hf));
+  f16vec3 f = f16vec3(1.5hf, 2.5hf, 3.5hf);
+  modf_result_vec3_f16 v = tint_modf(f);
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  test_function();
+  return;
+}
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_Builtin, Const_Modf_Scalar_f32) {
+    WrapInFunction(Decl(Let("v", Call("modf", 1.5_f))));
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+struct modf_result {
+  float fract;
+  float whole;
+};
+
+
+void test_function() {
+  modf_result v = modf_result(0.5f, 1.0f);
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  test_function();
+  return;
+}
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_Builtin, Const_Modf_Scalar_f16) {
+    Enable(ast::Extension::kF16);
+
+    WrapInFunction(Decl(Let("v", Call("modf", 1.5_h))));
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+#extension GL_AMD_gpu_shader_half_float : require
+
+struct modf_result_f16 {
+  float16_t fract;
+  float16_t whole;
+};
+
+
+void test_function() {
+  modf_result_f16 v = modf_result_f16(0.5hf, 1.0hf);
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  test_function();
+  return;
+}
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_Builtin, Const_Modf_Vector_f32) {
+    WrapInFunction(Decl(Let("v", Call("modf", vec3<f32>(1.5_f, 2.5_f, 3.5_f)))));
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+struct modf_result_vec3 {
+  vec3 fract;
+  vec3 whole;
+};
+
+
+void test_function() {
+  modf_result_vec3 v = modf_result_vec3(vec3(0.5f), vec3(1.0f, 2.0f, 3.0f));
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  test_function();
+  return;
+}
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_Builtin, Const_Modf_Vector_f16) {
+    Enable(ast::Extension::kF16);
+
+    WrapInFunction(Decl(Let("v", Call("modf", vec3<f16>(1.5_h, 2.5_h, 3.5_h)))));
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+#extension GL_AMD_gpu_shader_half_float : require
+
+struct modf_result_vec3_f16 {
+  f16vec3 fract;
+  f16vec3 whole;
+};
+
+
+void test_function() {
+  modf_result_vec3_f16 v = modf_result_vec3_f16(f16vec3(0.5hf), f16vec3(1.0hf, 2.0hf, 3.0hf));
 }
 
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
@@ -561,14 +675,16 @@
     GeneratorImpl& gen = SanitizeAndBuild();
 
     ASSERT_TRUE(gen.Generate()) << gen.error();
-    EXPECT_THAT(gen.result(), HasSubstr(R"(
-  float sig;
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+struct frexp_result {
+  float fract;
   int exp;
 };
 
 frexp_result tint_frexp(float param_0) {
   frexp_result result;
-  result.sig = frexp(param_0, result.exp);
+  result.fract = frexp(param_0, result.exp);
   return result;
 }
 
@@ -578,7 +694,11 @@
 }
 
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
-)"));
+void main() {
+  test_function();
+  return;
+}
+)");
 }
 
 TEST_F(GlslGeneratorImplTest_Builtin, Frexp_Scalar_f16) {
@@ -590,17 +710,17 @@
     GeneratorImpl& gen = SanitizeAndBuild();
 
     ASSERT_TRUE(gen.Generate()) << gen.error();
-    EXPECT_THAT(gen.result(), HasSubstr(R"(#version 310 es
+    EXPECT_EQ(gen.result(), R"(#version 310 es
 #extension GL_AMD_gpu_shader_half_float : require
 
 struct frexp_result_f16 {
-  float16_t sig;
+  float16_t fract;
   int exp;
 };
 
 frexp_result_f16 tint_frexp(float16_t param_0) {
   frexp_result_f16 result;
-  result.sig = frexp(param_0, result.exp);
+  result.fract = frexp(param_0, result.exp);
   return result;
 }
 
@@ -613,7 +733,8 @@
 void main() {
   test_function();
   return;
-)"));
+}
+)");
 }
 
 TEST_F(GlslGeneratorImplTest_Builtin, Frexp_Vector_f32) {
@@ -623,16 +744,16 @@
     GeneratorImpl& gen = SanitizeAndBuild();
 
     ASSERT_TRUE(gen.Generate()) << gen.error();
-    EXPECT_THAT(gen.result(), HasSubstr(R"(
+    EXPECT_EQ(gen.result(), R"(#version 310 es
 
 struct frexp_result_vec3 {
-  vec3 sig;
+  vec3 fract;
   ivec3 exp;
 };
 
 frexp_result_vec3 tint_frexp(vec3 param_0) {
   frexp_result_vec3 result;
-  result.sig = frexp(param_0, result.exp);
+  result.fract = frexp(param_0, result.exp);
   return result;
 }
 
@@ -645,7 +766,8 @@
 void main() {
   test_function();
   return;
-)"));
+}
+)");
 }
 
 TEST_F(GlslGeneratorImplTest_Builtin, Frexp_Vector_f16) {
@@ -657,17 +779,17 @@
     GeneratorImpl& gen = SanitizeAndBuild();
 
     ASSERT_TRUE(gen.Generate()) << gen.error();
-    EXPECT_THAT(gen.result(), HasSubstr(R"(#version 310 es
+    EXPECT_EQ(gen.result(), R"(#version 310 es
 #extension GL_AMD_gpu_shader_half_float : require
 
 struct frexp_result_vec3_f16 {
-  f16vec3 sig;
+  f16vec3 fract;
   ivec3 exp;
 };
 
 frexp_result_vec3_f16 tint_frexp(f16vec3 param_0) {
   frexp_result_vec3_f16 result;
-  result.sig = frexp(param_0, result.exp);
+  result.fract = frexp(param_0, result.exp);
   return result;
 }
 
@@ -681,7 +803,40 @@
   test_function();
   return;
 }
-)"));
+)");
+}
+
+// TODO(crbug.com/tint/1757): Remove once deprecation period for `frexp().sig` is over
+TEST_F(GlslGeneratorImplTest_Builtin, Frexp_Sig_Deprecation) {
+    WrapInFunction(MemberAccessor(Call("frexp", 1_f), "sig"));
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+struct frexp_result {
+  float fract;
+  int exp;
+};
+
+frexp_result tint_frexp(float param_0) {
+  frexp_result result;
+  result.fract = frexp(param_0, result.exp);
+  return result;
+}
+
+
+void test_function() {
+  float tint_symbol = tint_frexp(1.0f).fract;
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  test_function();
+  return;
+}
+)");
 }
 
 TEST_F(GlslGeneratorImplTest_Builtin, Degrees_Scalar_f32) {
@@ -1349,7 +1504,6 @@
 )");
 }
 
-
 TEST_F(GlslGeneratorImplTest_Builtin, QuantizeToF16_Vec4) {
     GlobalVar("v", vec4<f32>(2_f), ast::AddressSpace::kPrivate);
     WrapInFunction(Call("quantizeToF16", "v"));
diff --git a/src/tint/writer/glsl/generator_impl_case_test.cc b/src/tint/writer/glsl/generator_impl_case_test.cc
index 4f1c6f3..f12defd 100644
--- a/src/tint/writer/glsl/generator_impl_case_test.cc
+++ b/src/tint/writer/glsl/generator_impl_case_test.cc
@@ -12,7 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "src/tint/ast/fallthrough_statement.h"
 #include "src/tint/writer/glsl/test_helper.h"
 
 using namespace tint::number_suffixes;  // NOLINT
@@ -53,22 +52,6 @@
 )");
 }
 
-TEST_F(GlslGeneratorImplTest_Case, Emit_Case_WithFallthrough) {
-    auto* s = Switch(1_i, Case(CaseSelector(5_i), Block(create<ast::FallthroughStatement>())),
-                     DefaultCase());
-    WrapInFunction(s);
-
-    GeneratorImpl& gen = Build();
-
-    gen.increment_indent();
-
-    ASSERT_TRUE(gen.EmitCase(s->body[0])) << gen.error();
-    EXPECT_EQ(gen.result(), R"(  case 5: {
-    /* fallthrough */
-  }
-)");
-}
-
 TEST_F(GlslGeneratorImplTest_Case, Emit_Case_MultipleSelectors) {
     auto* s = Switch(1_i,
                      Case(
diff --git a/src/tint/writer/glsl/generator_impl_loop_test.cc b/src/tint/writer/glsl/generator_impl_loop_test.cc
index 7aa94be..639107b 100644
--- a/src/tint/writer/glsl/generator_impl_loop_test.cc
+++ b/src/tint/writer/glsl/generator_impl_loop_test.cc
@@ -23,7 +23,7 @@
 using GlslGeneratorImplTest_Loop = TestHelper;
 
 TEST_F(GlslGeneratorImplTest_Loop, Emit_Loop) {
-    auto* body = Block(create<ast::DiscardStatement>());
+    auto* body = Block(Break());
     auto* continuing = Block();
     auto* l = Loop(body, continuing);
 
@@ -36,7 +36,7 @@
 
     ASSERT_TRUE(gen.EmitStatement(l)) << gen.error();
     EXPECT_EQ(gen.result(), R"(  while (true) {
-    discard;
+    break;
   }
 )");
 }
@@ -44,7 +44,7 @@
 TEST_F(GlslGeneratorImplTest_Loop, Emit_LoopWithContinuing) {
     Func("a_statement", {}, ty.void_(), {});
 
-    auto* body = Block(create<ast::DiscardStatement>());
+    auto* body = Block(Break());
     auto* continuing = Block(CallStmt(Call("a_statement")));
     auto* l = Loop(body, continuing);
 
@@ -57,7 +57,7 @@
 
     ASSERT_TRUE(gen.EmitStatement(l)) << gen.error();
     EXPECT_EQ(gen.result(), R"(  while (true) {
-    discard;
+    break;
     {
       a_statement();
     }
@@ -68,7 +68,7 @@
 TEST_F(GlslGeneratorImplTest_Loop, Emit_LoopWithContinuing_BreakIf) {
     Func("a_statement", {}, ty.void_(), {});
 
-    auto* body = Block(create<ast::DiscardStatement>());
+    auto* body = Block(Break());
     auto* continuing = Block(CallStmt(Call("a_statement")), BreakIf(true));
     auto* l = Loop(body, continuing);
 
@@ -81,7 +81,7 @@
 
     ASSERT_TRUE(gen.EmitStatement(l)) << gen.error();
     EXPECT_EQ(gen.result(), R"(  while (true) {
-    discard;
+    break;
     {
       a_statement();
       if (true) { break; }
@@ -96,7 +96,7 @@
     GlobalVar("lhs", ty.f32(), ast::AddressSpace::kPrivate);
     GlobalVar("rhs", ty.f32(), ast::AddressSpace::kPrivate);
 
-    auto* body = Block(create<ast::DiscardStatement>());
+    auto* body = Block(Break());
     auto* continuing = Block(CallStmt(Call("a_statement")));
     auto* inner = Loop(body, continuing);
 
@@ -105,7 +105,7 @@
     auto* lhs = Expr("lhs");
     auto* rhs = Expr("rhs");
 
-    continuing = Block(Assign(lhs, rhs));
+    continuing = Block(Assign(lhs, rhs), BreakIf(true));
 
     auto* outer = Loop(body, continuing);
 
@@ -119,13 +119,14 @@
     ASSERT_TRUE(gen.EmitStatement(outer)) << gen.error();
     EXPECT_EQ(gen.result(), R"(  while (true) {
     while (true) {
-      discard;
+      break;
       {
         a_statement();
       }
     }
     {
       lhs = rhs;
+      if (true) { break; }
     }
   }
 )");
diff --git a/src/tint/writer/hlsl/generator.h b/src/tint/writer/hlsl/generator.h
index 9974cde..c624943 100644
--- a/src/tint/writer/hlsl/generator.h
+++ b/src/tint/writer/hlsl/generator.h
@@ -15,6 +15,7 @@
 #ifndef SRC_TINT_WRITER_HLSL_GENERATOR_H_
 #define SRC_TINT_WRITER_HLSL_GENERATOR_H_
 
+#include <bitset>
 #include <memory>
 #include <optional>
 #include <string>
@@ -25,6 +26,7 @@
 #include "src/tint/ast/pipeline_stage.h"
 #include "src/tint/reflection.h"
 #include "src/tint/sem/binding_point.h"
+#include "src/tint/utils/bitset.h"
 #include "src/tint/writer/array_length_from_uniform_options.h"
 #include "src/tint/writer/text.h"
 
@@ -56,6 +58,9 @@
     /// Options used to specify a mapping of binding points to indices into a UBO
     /// from which to load buffer sizes.
     ArrayLengthFromUniformOptions array_length_from_uniform = {};
+    /// Interstage locations actually used as inputs in the next stage of the pipeline.
+    /// This is potentially used for truncating unused interstage outputs at current shader stage.
+    std::bitset<16> interstage_locations;
 
     /// Reflect the fields of this class so that it can be used by tint::ForeachField()
     TINT_REFLECT(root_constant_binding_point,
diff --git a/src/tint/writer/hlsl/generator_impl.cc b/src/tint/writer/hlsl/generator_impl.cc
index 92e0cc5..dcad1b5 100644
--- a/src/tint/writer/hlsl/generator_impl.cc
+++ b/src/tint/writer/hlsl/generator_impl.cc
@@ -23,7 +23,6 @@
 #include <vector>
 
 #include "src/tint/ast/call_statement.h"
-#include "src/tint/ast/fallthrough_statement.h"
 #include "src/tint/ast/id_attribute.h"
 #include "src/tint/ast/internal_attribute.h"
 #include "src/tint/ast/interpolate_attribute.h"
@@ -54,6 +53,7 @@
 #include "src/tint/transform/calculate_array_length.h"
 #include "src/tint/transform/canonicalize_entry_point_io.h"
 #include "src/tint/transform/decompose_memory_access.h"
+#include "src/tint/transform/demote_to_helper.h"
 #include "src/tint/transform/disable_uniformity_analysis.h"
 #include "src/tint/transform/expand_compound_assignment.h"
 #include "src/tint/transform/localize_struct_array_assignment.h"
@@ -64,8 +64,8 @@
 #include "src/tint/transform/remove_continue_in_switch.h"
 #include "src/tint/transform/remove_phonies.h"
 #include "src/tint/transform/simplify_pointers.h"
+#include "src/tint/transform/truncate_interstage_variables.h"
 #include "src/tint/transform/unshadow.h"
-#include "src/tint/transform/unwind_discard_functions.h"
 #include "src/tint/transform/vectorize_scalar_matrix_initializers.h"
 #include "src/tint/transform/zero_init_workgroup_memory.h"
 #include "src/tint/utils/defer.h"
@@ -157,6 +157,9 @@
 
     manager.Add<transform::DisableUniformityAnalysis>();
 
+    // ExpandCompoundAssignment must come before BuiltinPolyfill
+    manager.Add<transform::ExpandCompoundAssignment>();
+
     {  // Builtin polyfills
         transform::BuiltinPolyfill::Builtins polyfills;
         polyfills.acosh = transform::BuiltinPolyfill::Level::kFull;
@@ -172,6 +175,7 @@
         polyfills.first_leading_bit = true;
         polyfills.first_trailing_bit = true;
         polyfills.insert_bits = transform::BuiltinPolyfill::Level::kFull;
+        polyfills.int_div_mod = true;
         polyfills.texture_sample_base_clamp_to_edge_2d_f32 = true;
         data.Add<transform::BuiltinPolyfill::Config>(polyfills);
         manager.Add<transform::BuiltinPolyfill>();
@@ -207,16 +211,41 @@
         manager.Add<transform::ZeroInitWorkgroupMemory>();
     }
     manager.Add<transform::CanonicalizeEntryPointIO>();
+
+    if (options.interstage_locations.any()) {
+        // When interstage_locations is empty, it means there's no user-defined interstage variables
+        // being used in the next stage. This is treated as a special case.
+        // TruncateInterstageVariables transform is trying to solve the HLSL compiler register
+        // mismatch issue. So it is not needed if no register is assigned to any interstage
+        // variables. As a result we only add this transform when there is at least one interstage
+        // locations being used.
+
+        // TruncateInterstageVariables itself will skip when interstage_locations matches exactly
+        // with the current stage output.
+
+        // Build the config for internal TruncateInterstageVariables transform.
+        transform::TruncateInterstageVariables::Config truncate_interstage_variables_cfg;
+        truncate_interstage_variables_cfg.interstage_locations =
+            std::move(options.interstage_locations);
+        manager.Add<transform::TruncateInterstageVariables>();
+        data.Add<transform::TruncateInterstageVariables::Config>(
+            std::move(truncate_interstage_variables_cfg));
+    }
+
     // NumWorkgroupsFromUniform must come after CanonicalizeEntryPointIO, as it
     // assumes that num_workgroups builtins only appear as struct members and are
     // only accessed directly via member accessors.
     manager.Add<transform::NumWorkgroupsFromUniform>();
-    manager.Add<transform::ExpandCompoundAssignment>();
     manager.Add<transform::PromoteSideEffectsToDecl>();
-    manager.Add<transform::UnwindDiscardFunctions>();
     manager.Add<transform::VectorizeScalarMatrixInitializers>();
     manager.Add<transform::SimplifyPointers>();
     manager.Add<transform::RemovePhonies>();
+
+    // DemoteToHelper must come after CanonicalizeEntryPointIO, PromoteSideEffectsToDecl, and
+    // ExpandCompoundAssignment.
+    // TODO(crbug.com/tint/1752): This is only necessary when FXC is being used.
+    manager.Add<transform::DemoteToHelper>();
+
     // ArrayLengthFromUniform must come after InlinePointerLets and Simplify, as
     // it assumes that the form of the array length argument is &var.array.
     manager.Add<transform::ArrayLengthFromUniform>();
@@ -656,117 +685,6 @@
     return true;
 }
 
-bool GeneratorImpl::EmitExpressionOrOneIfZero(std::ostream& out, const ast::Expression* expr) {
-    // For constants, replace literal 0 with 1.
-    if (const auto* val = builder_.Sem().Get(expr)->ConstantValue()) {
-        if (!val->AnyZero()) {
-            return EmitExpression(out, expr);
-        }
-
-        auto* ty = val->Type();
-
-        if (ty->IsAnyOf<sem::I32, sem::U32>()) {
-            return EmitValue(out, ty, 1);
-        }
-
-        if (auto* vec = ty->As<sem::Vector>()) {
-            auto* elem_ty = vec->type();
-
-            if (!EmitType(out, ty, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
-                return false;
-            }
-
-            out << "(";
-            for (size_t i = 0; i < vec->Width(); ++i) {
-                if (i != 0) {
-                    out << ", ";
-                }
-                auto s = val->Index(i)->As<AInt>();
-                if (!EmitValue(out, elem_ty, (s == 0) ? 1 : static_cast<int>(s))) {
-                    return false;
-                }
-            }
-            out << ")";
-            return true;
-        }
-
-        TINT_ICE(Writer, diagnostics_)
-            << "EmitExpressionOrOneIfZero expects integer scalar or vector";
-        return false;
-    }
-
-    auto* ty = TypeOf(expr)->UnwrapRef();
-
-    // For non-constants, we need to emit runtime code to check if the value is 0,
-    // and return 1 in that case.
-    std::string zero;
-    {
-        std::ostringstream ss;
-        EmitValue(ss, ty, 0);
-        zero = ss.str();
-    }
-    std::string one;
-    {
-        std::ostringstream ss;
-        EmitValue(ss, ty, 1);
-        one = ss.str();
-    }
-
-    // For identifiers, no need for a function call as it's fine to evaluate
-    // `expr` more than once.
-    if (expr->Is<ast::IdentifierExpression>()) {
-        out << "(";
-        if (!EmitExpression(out, expr)) {
-            return false;
-        }
-        out << " == " << zero << " ? " << one << " : ";
-        if (!EmitExpression(out, expr)) {
-            return false;
-        }
-        out << ")";
-        return true;
-    }
-
-    // For non-identifier expressions, call a function to make sure `expr` is only
-    // evaluated once.
-    auto name = utils::GetOrCreate(value_or_one_if_zero_, ty, [&]() -> std::string {
-        // Example:
-        // int4 tint_value_or_one_if_zero_int4(int4 value) {
-        //   return value == 0 ? 0 : value;
-        // }
-        std::string ty_name;
-        {
-            std::ostringstream ss;
-            if (!EmitType(ss, ty, tint::ast::AddressSpace::kUndefined, ast::Access::kUndefined,
-                          "")) {
-                return "";
-            }
-            ty_name = ss.str();
-        }
-
-        std::string fn = UniqueIdentifier("value_or_one_if_zero_" + ty_name);
-        line(&helpers_) << ty_name << " " << fn << "(" << ty_name << " value) {";
-        {
-            ScopedIndent si(&helpers_);
-            line(&helpers_) << "return value == " << zero << " ? " << one << " : value;";
-        }
-        line(&helpers_) << "}";
-        line(&helpers_);
-        return fn;
-    });
-
-    if (name.empty()) {
-        return false;
-    }
-
-    out << name << "(";
-    if (!EmitExpression(out, expr)) {
-        return false;
-    }
-    out << ")";
-    return true;
-}
-
 bool GeneratorImpl::EmitBinary(std::ostream& out, const ast::BinaryExpression* expr) {
     if (expr->op == ast::BinaryOp::kLogicalAnd || expr->op == ast::BinaryOp::kLogicalOr) {
         auto name = UniqueIdentifier(kTempNamePrefix);
@@ -887,21 +805,9 @@
             break;
         case ast::BinaryOp::kDivide:
             out << "/";
-            // BUG(crbug.com/tint/1083): Integer divide/modulo by zero is a FXC
-            // compile error, and undefined behavior in WGSL.
-            if (TypeOf(expr->rhs)->UnwrapRef()->is_integer_scalar_or_vector()) {
-                out << " ";
-                return EmitExpressionOrOneIfZero(out, expr->rhs);
-            }
             break;
         case ast::BinaryOp::kModulo:
             out << "%";
-            // BUG(crbug.com/tint/1083): Integer divide/modulo by zero is a FXC
-            // compile error, and undefined behavior in WGSL.
-            if (TypeOf(expr->rhs)->UnwrapRef()->is_integer_scalar_or_vector()) {
-                out << " ";
-                return EmitExpressionOrOneIfZero(out, expr->rhs);
-            }
             break;
         case ast::BinaryOp::kNone:
             diagnostics_.add_error(diag::System::Writer, "missing binary operation type");
@@ -1745,9 +1651,7 @@
             return true;
         }
         case sem::BuiltinType::kAtomicCompareExchangeWeak: {
-            // Emit the builtin return type unique to this overload. This does not
-            // exist in the AST, so it will not be generated in Generate().
-            if (!EmitStructTypeOnce(&helpers_, builtin->ReturnType()->As<sem::Struct>())) {
+            if (!EmitStructType(&helpers_, builtin->ReturnType()->As<sem::Struct>())) {
                 return false;
             }
 
@@ -1908,14 +1812,14 @@
             }
 
             line(b) << member_type << " exp;";
-            line(b) << member_type << " sig = frexp(" << in << ", exp);";
+            line(b) << member_type << " fract = frexp(" << in << ", exp);";
             {
                 auto l = line(b);
                 if (!EmitType(l, builtin->ReturnType(), ast::AddressSpace::kNone,
                               ast::Access::kUndefined, "")) {
                     return false;
                 }
-                l << " result = {sig, int" << width << "(exp)};";
+                l << " result = {fract, int" << width << "(exp)};";
             }
             line(b) << "return result;";
             return true;
@@ -2600,7 +2504,7 @@
             out << "default";
         } else {
             out << "case ";
-            if (!EmitConstant(out, selector->Value())) {
+            if (!EmitConstant(out, selector->Value(), /* is_variable_initializer */ false)) {
                 return false;
             }
         }
@@ -2621,18 +2525,7 @@
         return false;
     }
 
-    // Inline all fallthrough case statements. FXC cannot handle fallthroughs.
-    while (tint::Is<ast::FallthroughStatement>(stmt->body->Last())) {
-        case_idx++;
-        stmt = s->body[case_idx];
-        // Generate each fallthrough case statement in a new block. This is done to
-        // prevent symbol collision of variables declared in these cases statements.
-        if (!EmitBlock(stmt->body)) {
-            return false;
-        }
-    }
-
-    if (!tint::IsAnyOf<ast::BreakStatement, ast::FallthroughStatement>(stmt->body->Last())) {
+    if (!tint::IsAnyOf<ast::BreakStatement>(stmt->body->Last())) {
         line() << "break;";
     }
 
@@ -2657,7 +2550,13 @@
 bool GeneratorImpl::EmitExpression(std::ostream& out, const ast::Expression* expr) {
     if (auto* sem = builder_.Sem().Get(expr)) {
         if (auto* constant = sem->ConstantValue()) {
-            return EmitConstant(out, constant);
+            bool is_variable_initializer = false;
+            if (auto* stmt = sem->Stmt()) {
+                if (auto* decl = As<ast::VariableDeclStatement>(stmt->Declaration())) {
+                    is_variable_initializer = decl->variable->initializer == expr;
+                }
+            }
+            return EmitConstant(out, constant, is_variable_initializer);
         }
     }
     return Switch(
@@ -3147,7 +3046,9 @@
     return true;
 }
 
-bool GeneratorImpl::EmitConstant(std::ostream& out, const sem::Constant* constant) {
+bool GeneratorImpl::EmitConstant(std::ostream& out,
+                                 const sem::Constant* constant,
+                                 bool is_variable_initializer) {
     return Switch(
         constant->Type(),  //
         [&](const sem::Bool*) {
@@ -3177,7 +3078,7 @@
             if (constant->AllEqual()) {
                 {
                     ScopedParen sp(out);
-                    if (!EmitConstant(out, constant->Index(0))) {
+                    if (!EmitConstant(out, constant->Index(0), is_variable_initializer)) {
                         return false;
                     }
                 }
@@ -3198,7 +3099,7 @@
                 if (i > 0) {
                     out << ", ";
                 }
-                if (!EmitConstant(out, constant->Index(i))) {
+                if (!EmitConstant(out, constant->Index(i), is_variable_initializer)) {
                     return false;
                 }
             }
@@ -3215,7 +3116,7 @@
                 if (i > 0) {
                     out << ", ";
                 }
-                if (!EmitConstant(out, constant->Index(i))) {
+                if (!EmitConstant(out, constant->Index(i), is_variable_initializer)) {
                     return false;
                 }
             }
@@ -3244,7 +3145,7 @@
                 if (i > 0) {
                     out << ", ";
                 }
-                if (!EmitConstant(out, constant->Index(i))) {
+                if (!EmitConstant(out, constant->Index(i), is_variable_initializer)) {
                     return false;
                 }
             }
@@ -3252,25 +3153,45 @@
             return true;
         },
         [&](const sem::Struct* s) {
+            if (!EmitStructType(&helpers_, s)) {
+                return false;
+            }
+
             if (constant->AllZero()) {
-                out << "(";
-                if (!EmitType(out, s, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
-                    return false;
-                }
-                out << ")0";
+                out << "(" << StructName(s) << ")0";
                 return true;
             }
 
-            out << "{";
-            TINT_DEFER(out << "}");
-
-            for (size_t i = 0; i < s->Members().size(); i++) {
-                if (i > 0) {
-                    out << ", ";
+            auto emit_member_values = [&](std::ostream& o) {
+                o << "{";
+                for (size_t i = 0; i < s->Members().size(); i++) {
+                    if (i > 0) {
+                        o << ", ";
+                    }
+                    if (!EmitConstant(o, constant->Index(i), is_variable_initializer)) {
+                        return false;
+                    }
                 }
-                if (!EmitConstant(out, constant->Index(i))) {
+                o << "}";
+                return true;
+            };
+
+            if (is_variable_initializer) {
+                if (!emit_member_values(out)) {
                     return false;
                 }
+            } else {
+                // HLSL requires structure initializers to be assigned directly to a variable.
+                auto name = UniqueIdentifier("c");
+                {
+                    auto decl = line();
+                    decl << "const " << StructName(s) << " " << name << " = ";
+                    if (!emit_member_values(decl)) {
+                        return false;
+                    }
+                    decl << ";";
+                }
+                out << name;
             }
 
             return true;
@@ -3586,14 +3507,24 @@
     }
     out << ".";
 
-    // Swizzles output the name directly
-    if (builder_.Sem().Get(expr)->Is<sem::Swizzle>()) {
-        out << builder_.Symbols().NameFor(expr->member->symbol);
-    } else if (!EmitExpression(out, expr->member)) {
-        return false;
-    }
+    auto* sem = builder_.Sem().Get(expr);
 
-    return true;
+    return Switch(
+        sem,
+        [&](const sem::Swizzle*) {
+            // Swizzles output the name directly
+            out << builder_.Symbols().NameFor(expr->member->symbol);
+            return true;
+        },
+        [&](const sem::StructMemberAccess* member_access) {
+            out << program_->Symbols().NameFor(member_access->Member()->Name());
+            return true;
+        },
+        [&](Default) {
+            TINT_ICE(Writer, diagnostics_)
+                << "unknown member access type: " << sem->TypeInfo().name;
+            return false;
+        });
 }
 
 bool GeneratorImpl::EmitReturn(const ast::ReturnStatement* stmt) {
@@ -3639,10 +3570,6 @@
         [&](const ast::DiscardStatement* d) {  //
             return EmitDiscard(d);
         },
-        [&](const ast::FallthroughStatement*) {  //
-            line() << "/* fallthrough */";
-            return true;
-        },
         [&](const ast::IfStatement* i) {  //
             return EmitIf(i);
         },
@@ -3990,6 +3917,11 @@
 }
 
 bool GeneratorImpl::EmitStructType(TextBuffer* b, const sem::Struct* str) {
+    auto it = emitted_structs_.emplace(str);
+    if (!it.second) {
+        return true;
+    }
+
     line(b) << "struct " << StructName(str) << " {";
     {
         ScopedIndent si(b);
@@ -4066,14 +3998,6 @@
     return true;
 }
 
-bool GeneratorImpl::EmitStructTypeOnce(TextBuffer* buffer, const sem::Struct* str) {
-    auto it = emitted_structs_.emplace(str);
-    if (!it.second) {
-        return true;
-    }
-    return EmitStructType(buffer, str);
-}
-
 bool GeneratorImpl::EmitUnaryOp(std::ostream& out, const ast::UnaryOpExpression* expr) {
     switch (expr->op) {
         case ast::UnaryOp::kIndirection:
diff --git a/src/tint/writer/hlsl/generator_impl.h b/src/tint/writer/hlsl/generator_impl.h
index eca7734..74a172d 100644
--- a/src/tint/writer/hlsl/generator_impl.h
+++ b/src/tint/writer/hlsl/generator_impl.h
@@ -93,12 +93,6 @@
     /// @param stmt the statement to emit
     /// @returns true if the statement was emitted successfully
     bool EmitAssign(const ast::AssignmentStatement* stmt);
-    /// Emits code such that if `expr` is zero, it emits one, else `expr`.
-    /// Used to avoid divide-by-zeros by substituting constant zeros with ones.
-    /// @param out the output of the expression stream
-    /// @param expr the expression
-    /// @returns true if the expression was emitted, false otherwise
-    bool EmitExpressionOrOneIfZero(std::ostream& out, const ast::Expression* expr);
     /// Handles generating a binary expression
     /// @param out the output of the expression stream
     /// @param expr the binary expression
@@ -354,8 +348,12 @@
     /// Handles a constant value
     /// @param out the output stream
     /// @param constant the constant value to emit
+    /// @param is_variable_initializer true if the constant is used as the RHS of a variable
+    /// initializer
     /// @returns true if the constant value was successfully emitted
-    bool EmitConstant(std::ostream& out, const sem::Constant* constant);
+    bool EmitConstant(std::ostream& out,
+                      const sem::Constant* constant,
+                      bool is_variable_initializer);
     /// Handles a literal
     /// @param out the output stream
     /// @param lit the literal to emit
@@ -426,17 +424,12 @@
                          ast::AddressSpace address_space,
                          ast::Access access,
                          const std::string& name);
-    /// Handles generating a structure declaration
+    /// Handles generating a structure declaration. If the structure has already been emitted, then
+    /// this function will simply return `true` without emitting anything.
     /// @param buffer the text buffer that the type declaration will be written to
     /// @param ty the struct to generate
     /// @returns true if the struct is emitted
     bool EmitStructType(TextBuffer* buffer, const sem::Struct* ty);
-    /// Handles generating a structure declaration only the first time called. Subsequent calls are
-    /// a no-op and return true.
-    /// @param buffer the text buffer that the type declaration will be written to
-    /// @param ty the struct to generate
-    /// @returns true if the struct is emitted
-    bool EmitStructTypeOnce(TextBuffer* buffer, const sem::Struct* ty);
     /// Handles a unary op expression
     /// @param out the output of the expression stream
     /// @param expr the expression to emit
diff --git a/src/tint/writer/hlsl/generator_impl_binary_test.cc b/src/tint/writer/hlsl/generator_impl_binary_test.cc
index 1163078..6e2b3c7 100644
--- a/src/tint/writer/hlsl/generator_impl_binary_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_binary_test.cc
@@ -653,295 +653,5 @@
 )");
 }
 
-namespace HlslGeneratorDivMod {
-
-struct Params {
-    enum class Type { Div, Mod };
-    Type type;
-};
-
-struct HlslGeneratorDivModTest : TestParamHelper<Params> {
-    std::string Token() { return GetParam().type == Params::Type::Div ? "/" : "%"; }
-
-    template <typename... Args>
-    auto Op(Args... args) {
-        return GetParam().type == Params::Type::Div ? Div(std::forward<Args>(args)...)
-                                                    : Mod(std::forward<Args>(args)...);
-    }
-};
-
-INSTANTIATE_TEST_SUITE_P(HlslGeneratorImplTest,
-                         HlslGeneratorDivModTest,
-                         testing::Values(Params{Params::Type::Div}, Params{Params::Type::Mod}));
-
-TEST_P(HlslGeneratorDivModTest, DivOrModByLiteralZero_i32) {
-    Func("fn", utils::Empty, ty.void_(),
-         utils::Vector{
-             Decl(Var("a", ty.i32())),
-             Decl(Let("r", Op("a", 0_i))),
-         });
-
-    GeneratorImpl& gen = Build();
-
-    ASSERT_TRUE(gen.Generate());
-    EXPECT_EQ(gen.result(), R"(void fn() {
-  int a = 0;
-  const int r = (a )" + Token() +
-                                R"( 1);
-}
-)");
-}
-
-TEST_P(HlslGeneratorDivModTest, DivOrModByLiteralZero_u32) {
-    Func("fn", utils::Empty, ty.void_(),
-         utils::Vector{
-             Decl(Var("a", ty.u32())),
-             Decl(Let("r", Op("a", 0_u))),
-         });
-
-    GeneratorImpl& gen = Build();
-
-    ASSERT_TRUE(gen.Generate());
-    EXPECT_EQ(gen.result(), R"(void fn() {
-  uint a = 0u;
-  const uint r = (a )" + Token() +
-                                R"( 1u);
-}
-)");
-}
-
-TEST_P(HlslGeneratorDivModTest, DivOrModByLiteralZero_vec_by_vec_i32) {
-    Func("fn", utils::Empty, ty.void_(),
-         utils::Vector{
-             Decl(Var("a", vec4<i32>(100_i, 100_i, 100_i, 100_i))),
-             Decl(Let("r", Op("a", vec4<i32>(50_i, 0_i, 25_i, 0_i)))),
-         });
-
-    GeneratorImpl& gen = Build();
-
-    ASSERT_TRUE(gen.Generate());
-    EXPECT_EQ(gen.result(), R"(void fn() {
-  int4 a = (100).xxxx;
-  const int4 r = (a )" + Token() +
-                                R"( int4(50, 1, 25, 1));
-}
-)");
-}
-
-TEST_P(HlslGeneratorDivModTest, DivOrModByLiteralZero_vec_by_scalar_i32) {
-    Func("fn", utils::Empty, ty.void_(),
-         utils::Vector{
-             Decl(Var("a", vec4<i32>(100_i, 100_i, 100_i, 100_i))),
-             Decl(Let("r", Op("a", 0_i))),
-         });
-
-    GeneratorImpl& gen = Build();
-
-    ASSERT_TRUE(gen.Generate());
-    EXPECT_EQ(gen.result(), R"(void fn() {
-  int4 a = (100).xxxx;
-  const int4 r = (a )" + Token() +
-                                R"( 1);
-}
-)");
-}
-
-TEST_P(HlslGeneratorDivModTest, DivOrModByIdentifier_i32) {
-    Func("fn", utils::Vector{Param("b", ty.i32())}, ty.void_(),
-         utils::Vector{
-             Decl(Var("a", ty.i32())),
-             Decl(Let("r", Op("a", "b"))),
-         });
-
-    GeneratorImpl& gen = Build();
-
-    ASSERT_TRUE(gen.Generate());
-    EXPECT_EQ(gen.result(), R"(void fn(int b) {
-  int a = 0;
-  const int r = (a )" + Token() +
-                                R"( (b == 0 ? 1 : b));
-}
-)");
-}
-
-TEST_P(HlslGeneratorDivModTest, DivOrModByIdentifier_u32) {
-    Func("fn", utils::Vector{Param("b", ty.u32())}, ty.void_(),
-         utils::Vector{
-             Decl(Var("a", ty.u32())),
-             Decl(Let("r", Op("a", "b"))),
-         });
-
-    GeneratorImpl& gen = Build();
-
-    ASSERT_TRUE(gen.Generate());
-    EXPECT_EQ(gen.result(), R"(void fn(uint b) {
-  uint a = 0u;
-  const uint r = (a )" + Token() +
-                                R"( (b == 0u ? 1u : b));
-}
-)");
-}
-
-TEST_P(HlslGeneratorDivModTest, DivOrModByIdentifier_vec_by_vec_i32) {
-    Func("fn", utils::Vector{Param("b", ty.vec3<i32>())}, ty.void_(),
-         utils::Vector{
-             Decl(Var("a", ty.vec3<i32>())),
-             Decl(Let("r", Op("a", "b"))),
-         });
-
-    GeneratorImpl& gen = Build();
-
-    ASSERT_TRUE(gen.Generate());
-    EXPECT_EQ(gen.result(), R"(void fn(int3 b) {
-  int3 a = int3(0, 0, 0);
-  const int3 r = (a )" + Token() +
-                                R"( (b == int3(0, 0, 0) ? int3(1, 1, 1) : b));
-}
-)");
-}
-
-TEST_P(HlslGeneratorDivModTest, DivOrModByIdentifier_vec_by_scalar_i32) {
-    Func("fn", utils::Vector{Param("b", ty.i32())}, ty.void_(),
-         utils::Vector{
-             Decl(Var("a", ty.vec3<i32>())),
-             Decl(Let("r", Op("a", "b"))),
-         });
-
-    GeneratorImpl& gen = Build();
-
-    ASSERT_TRUE(gen.Generate());
-    EXPECT_EQ(gen.result(), R"(void fn(int b) {
-  int3 a = int3(0, 0, 0);
-  const int3 r = (a )" + Token() +
-                                R"( (b == 0 ? 1 : b));
-}
-)");
-}
-
-TEST_P(HlslGeneratorDivModTest, DivOrModByExpression_i32) {
-    Func("zero", utils::Empty, ty.i32(),
-         utils::Vector{
-             Return(Expr(0_i)),
-         });
-
-    Func("fn", utils::Empty, ty.void_(),
-         utils::Vector{
-             Decl(Var("a", ty.i32())),
-             Decl(Let("r", Op("a", Call("zero")))),
-         });
-
-    GeneratorImpl& gen = Build();
-
-    ASSERT_TRUE(gen.Generate());
-    EXPECT_EQ(gen.result(), R"(int value_or_one_if_zero_int(int value) {
-  return value == 0 ? 1 : value;
-}
-
-int zero() {
-  return 0;
-}
-
-void fn() {
-  int a = 0;
-  const int r = (a )" + Token() +
-                                R"( value_or_one_if_zero_int(zero()));
-}
-)");
-}
-
-TEST_P(HlslGeneratorDivModTest, DivOrModByExpression_u32) {
-    Func("zero", utils::Empty, ty.u32(),
-         utils::Vector{
-             Return(Expr(0_u)),
-         });
-
-    Func("fn", utils::Empty, ty.void_(),
-         utils::Vector{
-             Decl(Var("a", ty.u32())),
-             Decl(Let("r", Op("a", Call("zero")))),
-         });
-
-    GeneratorImpl& gen = Build();
-
-    ASSERT_TRUE(gen.Generate());
-    EXPECT_EQ(gen.result(), R"(uint value_or_one_if_zero_uint(uint value) {
-  return value == 0u ? 1u : value;
-}
-
-uint zero() {
-  return 0u;
-}
-
-void fn() {
-  uint a = 0u;
-  const uint r = (a )" + Token() +
-                                R"( value_or_one_if_zero_uint(zero()));
-}
-)");
-}
-
-TEST_P(HlslGeneratorDivModTest, DivOrModByExpression_vec_by_vec_i32) {
-    Func("zero", utils::Empty, ty.vec3<i32>(),
-         utils::Vector{
-             Return(vec3<i32>(0_i, 0_i, 0_i)),
-         });
-
-    Func("fn", utils::Empty, ty.void_(),
-         utils::Vector{
-             Decl(Var("a", ty.vec3<i32>())),
-             Decl(Let("r", Op("a", Call("zero")))),
-         });
-
-    GeneratorImpl& gen = Build();
-
-    ASSERT_TRUE(gen.Generate());
-    EXPECT_EQ(gen.result(), R"(int3 value_or_one_if_zero_int3(int3 value) {
-  return value == int3(0, 0, 0) ? int3(1, 1, 1) : value;
-}
-
-int3 zero() {
-  return (0).xxx;
-}
-
-void fn() {
-  int3 a = int3(0, 0, 0);
-  const int3 r = (a )" + Token() +
-                                R"( value_or_one_if_zero_int3(zero()));
-}
-)");
-}
-
-TEST_P(HlslGeneratorDivModTest, DivOrModByExpression_vec_by_scalar_i32) {
-    Func("zero", utils::Empty, ty.i32(),
-         utils::Vector{
-             Return(0_i),
-         });
-
-    Func("fn", utils::Empty, ty.void_(),
-         utils::Vector{
-             Decl(Var("a", ty.vec3<i32>())),
-             Decl(Let("r", Op("a", Call("zero")))),
-         });
-
-    GeneratorImpl& gen = Build();
-
-    ASSERT_TRUE(gen.Generate());
-    EXPECT_EQ(gen.result(), R"(int value_or_one_if_zero_int(int value) {
-  return value == 0 ? 1 : value;
-}
-
-int zero() {
-  return 0;
-}
-
-void fn() {
-  int3 a = int3(0, 0, 0);
-  const int3 r = (a )" + Token() +
-                                R"( value_or_one_if_zero_int(zero()));
-}
-)");
-}
-}  // namespace HlslGeneratorDivMod
-
 }  // namespace
 }  // namespace tint::writer::hlsl
diff --git a/src/tint/writer/hlsl/generator_impl_builtin_test.cc b/src/tint/writer/hlsl/generator_impl_builtin_test.cc
index 360c8f1..3e9b169 100644
--- a/src/tint/writer/hlsl/generator_impl_builtin_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_builtin_test.cc
@@ -381,9 +381,9 @@
     EXPECT_EQ(out.str(), "(bool2(true, false) ? b : a)");
 }
 
-TEST_F(HlslGeneratorImplTest_Builtin, Modf_Scalar_f32) {
-    auto* call = Call("modf", 1_f);
-    WrapInFunction(CallStmt(call));
+TEST_F(HlslGeneratorImplTest_Builtin, Runtime_Modf_Scalar_f32) {
+    WrapInFunction(Decl(Let("f", Expr(1.5_f))),  //
+                   Decl(Let("v", Call("modf", "f"))));
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
@@ -400,17 +400,18 @@
 
 [numthreads(1, 1, 1)]
 void test_function() {
-  tint_modf(1.0f);
+  const float f = 1.5f;
+  const modf_result v = tint_modf(f);
   return;
 }
 )");
 }
 
-TEST_F(HlslGeneratorImplTest_Builtin, Modf_Scalar_f16) {
+TEST_F(HlslGeneratorImplTest_Builtin, Runtime_Modf_Scalar_f16) {
     Enable(ast::Extension::kF16);
 
-    auto* call = Call("modf", 1_h);
-    WrapInFunction(CallStmt(call));
+    WrapInFunction(Decl(Let("f", Expr(1.5_h))),  //
+                   Decl(Let("v", Call("modf", "f"))));
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
@@ -427,15 +428,16 @@
 
 [numthreads(1, 1, 1)]
 void test_function() {
-  tint_modf(float16_t(1.0h));
+  const float16_t f = float16_t(1.5h);
+  const modf_result_f16 v = tint_modf(f);
   return;
 }
 )");
 }
 
-TEST_F(HlslGeneratorImplTest_Builtin, Modf_Vector_f32) {
-    auto* call = Call("modf", vec3<f32>());
-    WrapInFunction(CallStmt(call));
+TEST_F(HlslGeneratorImplTest_Builtin, Runtime_Modf_Vector_f32) {
+    WrapInFunction(Decl(Let("f", vec3<f32>(1.5_f, 2.5_f, 3.5_f))),  //
+                   Decl(Let("v", Call("modf", "f"))));
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
@@ -452,17 +454,18 @@
 
 [numthreads(1, 1, 1)]
 void test_function() {
-  tint_modf((0.0f).xxx);
+  const float3 f = float3(1.5f, 2.5f, 3.5f);
+  const modf_result_vec3 v = tint_modf(f);
   return;
 }
 )");
 }
 
-TEST_F(HlslGeneratorImplTest_Builtin, Modf_Vector_f16) {
+TEST_F(HlslGeneratorImplTest_Builtin, Runtime_Modf_Vector_f16) {
     Enable(ast::Extension::kF16);
 
-    auto* call = Call("modf", vec3<f16>());
-    WrapInFunction(CallStmt(call));
+    WrapInFunction(Decl(Let("f", vec3<f16>(1.5_h, 2.5_h, 3.5_h))),  //
+                   Decl(Let("v", Call("modf", "f"))));
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
@@ -479,7 +482,110 @@
 
 [numthreads(1, 1, 1)]
 void test_function() {
-  tint_modf((float16_t(0.0h)).xxx);
+  const vector<float16_t, 3> f = vector<float16_t, 3>(float16_t(1.5h), float16_t(2.5h), float16_t(3.5h));
+  const modf_result_vec3_f16 v = tint_modf(f);
+  return;
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_Builtin, Const_Modf_Scalar_f32) {
+    WrapInFunction(Decl(Let("v", Call("modf", 1.5_f))));
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_EQ(gen.result(), R"(struct modf_result {
+  float fract;
+  float whole;
+};
+[numthreads(1, 1, 1)]
+void test_function() {
+  const modf_result v = {0.5f, 1.0f};
+  return;
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_Builtin, Const_Modf_Scalar_f16) {
+    Enable(ast::Extension::kF16);
+
+    WrapInFunction(Decl(Let("v", Call("modf", 1.5_h))));
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_EQ(gen.result(), R"(struct modf_result_f16 {
+  float16_t fract;
+  float16_t whole;
+};
+[numthreads(1, 1, 1)]
+void test_function() {
+  const modf_result_f16 v = {float16_t(0.5h), float16_t(1.0h)};
+  return;
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_Builtin, Const_Modf_Vector_f32) {
+    WrapInFunction(Decl(Let("v", Call("modf", vec3<f32>(1.5_f, 2.5_f, 3.5_f)))));
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_EQ(gen.result(), R"(struct modf_result_vec3 {
+  float3 fract;
+  float3 whole;
+};
+[numthreads(1, 1, 1)]
+void test_function() {
+  const modf_result_vec3 v = {(0.5f).xxx, float3(1.0f, 2.0f, 3.0f)};
+  return;
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_Builtin, Const_Modf_Vector_f16) {
+    Enable(ast::Extension::kF16);
+
+    WrapInFunction(Decl(Let("v", Call("modf", vec3<f16>(1.5_h, 2.5_h, 3.5_h)))));
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_EQ(gen.result(), R"(struct modf_result_vec3_f16 {
+  vector<float16_t, 3> fract;
+  vector<float16_t, 3> whole;
+};
+[numthreads(1, 1, 1)]
+void test_function() {
+  const modf_result_vec3_f16 v = {(float16_t(0.5h)).xxx, vector<float16_t, 3>(float16_t(1.0h), float16_t(2.0h), float16_t(3.0h))};
+  return;
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_Builtin, NonInitializer_Modf_Vector_f32) {
+    WrapInFunction(
+        // Declare a variable with the result of a modf call.
+        // This is required to infer the 'var' type.
+        Decl(Var("v", Call("modf", vec3<f32>(1.5_f, 2.5_f, 3.5_f)))),
+        // Now assign 'v' again with another modf call.
+        // This requires generating a temporary variable for the struct initializer.
+        Assign("v", Call("modf", vec3<f32>(4.5_f, 5.5_f, 6.5_f))));
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_EQ(gen.result(), R"(struct modf_result_vec3 {
+  float3 fract;
+  float3 whole;
+};
+[numthreads(1, 1, 1)]
+void test_function() {
+  modf_result_vec3 v = {(0.5f).xxx, float3(1.0f, 2.0f, 3.0f)};
+  const modf_result_vec3 c = {(0.5f).xxx, float3(4.0f, 5.0f, 6.0f)};
+  v = c;
   return;
 }
 )");
@@ -493,13 +599,13 @@
 
     ASSERT_TRUE(gen.Generate()) << gen.error();
     EXPECT_EQ(gen.result(), R"(struct frexp_result {
-  float sig;
+  float fract;
   int exp;
 };
 frexp_result tint_frexp(float param_0) {
   float exp;
-  float sig = frexp(param_0, exp);
-  frexp_result result = {sig, int(exp)};
+  float fract = frexp(param_0, exp);
+  frexp_result result = {fract, int(exp)};
   return result;
 }
 
@@ -521,13 +627,13 @@
 
     ASSERT_TRUE(gen.Generate()) << gen.error();
     EXPECT_EQ(gen.result(), R"(struct frexp_result_f16 {
-  float16_t sig;
+  float16_t fract;
   int exp;
 };
 frexp_result_f16 tint_frexp(float16_t param_0) {
   float16_t exp;
-  float16_t sig = frexp(param_0, exp);
-  frexp_result_f16 result = {sig, int(exp)};
+  float16_t fract = frexp(param_0, exp);
+  frexp_result_f16 result = {fract, int(exp)};
   return result;
 }
 
@@ -547,13 +653,13 @@
 
     ASSERT_TRUE(gen.Generate()) << gen.error();
     EXPECT_EQ(gen.result(), R"(struct frexp_result_vec3 {
-  float3 sig;
+  float3 fract;
   int3 exp;
 };
 frexp_result_vec3 tint_frexp(float3 param_0) {
   float3 exp;
-  float3 sig = frexp(param_0, exp);
-  frexp_result_vec3 result = {sig, int3(exp)};
+  float3 fract = frexp(param_0, exp);
+  frexp_result_vec3 result = {fract, int3(exp)};
   return result;
 }
 
@@ -575,13 +681,13 @@
 
     ASSERT_TRUE(gen.Generate()) << gen.error();
     EXPECT_EQ(gen.result(), R"(struct frexp_result_vec3_f16 {
-  vector<float16_t, 3> sig;
+  vector<float16_t, 3> fract;
   int3 exp;
 };
 frexp_result_vec3_f16 tint_frexp(vector<float16_t, 3> param_0) {
   vector<float16_t, 3> exp;
-  vector<float16_t, 3> sig = frexp(param_0, exp);
-  frexp_result_vec3_f16 result = {sig, int3(exp)};
+  vector<float16_t, 3> fract = frexp(param_0, exp);
+  frexp_result_vec3_f16 result = {fract, int3(exp)};
   return result;
 }
 
@@ -593,6 +699,32 @@
 )");
 }
 
+// TODO(crbug.com/tint/1757): Remove once deprecation period for `frexp().sig` is over
+TEST_F(HlslGeneratorImplTest_Builtin, Frexp_Sig_Deprecation) {
+    WrapInFunction(MemberAccessor(Call("frexp", 1_f), "sig"));
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_EQ(gen.result(), R"(struct frexp_result {
+  float fract;
+  int exp;
+};
+frexp_result tint_frexp(float param_0) {
+  float exp;
+  float fract = frexp(param_0, exp);
+  frexp_result result = {fract, int(exp)};
+  return result;
+}
+
+[numthreads(1, 1, 1)]
+void test_function() {
+  const float tint_symbol = tint_frexp(1.0f).fract;
+  return;
+}
+)");
+}
+
 TEST_F(HlslGeneratorImplTest_Builtin, Degrees_Scalar_f32) {
     auto* val = Var("val", ty.f32());
     auto* call = Call("degrees", val);
diff --git a/src/tint/writer/hlsl/generator_impl_case_test.cc b/src/tint/writer/hlsl/generator_impl_case_test.cc
index bfcbcaf..2352bbc 100644
--- a/src/tint/writer/hlsl/generator_impl_case_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_case_test.cc
@@ -12,7 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "src/tint/ast/fallthrough_statement.h"
 #include "src/tint/writer/hlsl/test_helper.h"
 
 using namespace tint::number_suffixes;  // NOLINT
@@ -53,28 +52,6 @@
 )");
 }
 
-TEST_F(HlslGeneratorImplTest_Case, Emit_Case_WithFallthrough) {
-    auto* s = Switch(1_i,                                                                  //
-                     Case(CaseSelector(4_i), Block(create<ast::FallthroughStatement>())),  //
-                     Case(CaseSelector(5_i), Block(create<ast::ReturnStatement>())),       //
-                     DefaultCase());
-    WrapInFunction(s);
-
-    GeneratorImpl& gen = Build();
-
-    gen.increment_indent();
-
-    ASSERT_TRUE(gen.EmitCase(s, 0)) << gen.error();
-    EXPECT_EQ(gen.result(), R"(  case 4: {
-    /* fallthrough */
-    {
-      return;
-    }
-    break;
-  }
-)");
-}
-
 TEST_F(HlslGeneratorImplTest_Case, Emit_Case_MultipleSelectors) {
     auto* s = Switch(1_i,
                      Case(utils::Vector{CaseSelector(5_i), CaseSelector(6_i)},
diff --git a/src/tint/writer/hlsl/generator_impl_loop_test.cc b/src/tint/writer/hlsl/generator_impl_loop_test.cc
index 3d8219b..9d2a4de 100644
--- a/src/tint/writer/hlsl/generator_impl_loop_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_loop_test.cc
@@ -23,7 +23,7 @@
 using HlslGeneratorImplTest_Loop = TestHelper;
 
 TEST_F(HlslGeneratorImplTest_Loop, Emit_Loop) {
-    auto* body = Block(create<ast::DiscardStatement>());
+    auto* body = Block(Break());
     auto* continuing = Block();
     auto* l = Loop(body, continuing);
 
@@ -36,7 +36,7 @@
 
     ASSERT_TRUE(gen.EmitStatement(l)) << gen.error();
     EXPECT_EQ(gen.result(), R"(  while (true) {
-    discard;
+    break;
   }
 )");
 }
@@ -44,7 +44,7 @@
 TEST_F(HlslGeneratorImplTest_Loop, Emit_LoopWithContinuing) {
     Func("a_statement", {}, ty.void_(), {});
 
-    auto* body = Block(create<ast::DiscardStatement>());
+    auto* body = Block(Break());
     auto* continuing = Block(CallStmt(Call("a_statement")));
     auto* l = Loop(body, continuing);
 
@@ -57,7 +57,7 @@
 
     ASSERT_TRUE(gen.EmitStatement(l)) << gen.error();
     EXPECT_EQ(gen.result(), R"(  while (true) {
-    discard;
+    break;
     {
       a_statement();
     }
@@ -68,7 +68,7 @@
 TEST_F(HlslGeneratorImplTest_Loop, Emit_LoopWithContinuing_BreakIf) {
     Func("a_statement", {}, ty.void_(), {});
 
-    auto* body = Block(create<ast::DiscardStatement>());
+    auto* body = Block(Break());
     auto* continuing = Block(CallStmt(Call("a_statement")), BreakIf(true));
     auto* l = Loop(body, continuing);
 
@@ -81,7 +81,7 @@
 
     ASSERT_TRUE(gen.EmitStatement(l)) << gen.error();
     EXPECT_EQ(gen.result(), R"(  while (true) {
-    discard;
+    break;
     {
       a_statement();
       if (true) { break; }
@@ -96,7 +96,7 @@
     GlobalVar("lhs", ty.f32(), ast::AddressSpace::kPrivate);
     GlobalVar("rhs", ty.f32(), ast::AddressSpace::kPrivate);
 
-    auto* body = Block(create<ast::DiscardStatement>());
+    auto* body = Block(Break());
     auto* continuing = Block(CallStmt(Call("a_statement")));
     auto* inner = Loop(body, continuing);
 
@@ -105,7 +105,7 @@
     auto* lhs = Expr("lhs");
     auto* rhs = Expr("rhs");
 
-    continuing = Block(Assign(lhs, rhs));
+    continuing = Block(Assign(lhs, rhs), BreakIf(true));
 
     auto* outer = Loop(body, continuing);
 
@@ -119,13 +119,14 @@
     ASSERT_TRUE(gen.EmitStatement(outer)) << gen.error();
     EXPECT_EQ(gen.result(), R"(  while (true) {
     while (true) {
-      discard;
+      break;
       {
         a_statement();
       }
     }
     {
       lhs = rhs;
+      if (true) { break; }
     }
   }
 )");
diff --git a/src/tint/writer/msl/generator_impl.cc b/src/tint/writer/msl/generator_impl.cc
index 65b5275..10efd59 100644
--- a/src/tint/writer/msl/generator_impl.cc
+++ b/src/tint/writer/msl/generator_impl.cc
@@ -25,7 +25,6 @@
 #include "src/tint/ast/bool_literal_expression.h"
 #include "src/tint/ast/call_statement.h"
 #include "src/tint/ast/disable_validation_attribute.h"
-#include "src/tint/ast/fallthrough_statement.h"
 #include "src/tint/ast/float_literal_expression.h"
 #include "src/tint/ast/id_attribute.h"
 #include "src/tint/ast/interpolate_attribute.h"
@@ -62,6 +61,7 @@
 #include "src/tint/transform/array_length_from_uniform.h"
 #include "src/tint/transform/builtin_polyfill.h"
 #include "src/tint/transform/canonicalize_entry_point_io.h"
+#include "src/tint/transform/demote_to_helper.h"
 #include "src/tint/transform/disable_uniformity_analysis.h"
 #include "src/tint/transform/expand_compound_assignment.h"
 #include "src/tint/transform/manager.h"
@@ -72,7 +72,6 @@
 #include "src/tint/transform/remove_phonies.h"
 #include "src/tint/transform/simplify_pointers.h"
 #include "src/tint/transform/unshadow.h"
-#include "src/tint/transform/unwind_discard_functions.h"
 #include "src/tint/transform/vectorize_scalar_matrix_initializers.h"
 #include "src/tint/transform/zero_init_workgroup_memory.h"
 #include "src/tint/utils/defer.h"
@@ -85,8 +84,8 @@
 namespace tint::writer::msl {
 namespace {
 
-bool last_is_break_or_fallthrough(const ast::BlockStatement* stmts) {
-    return IsAnyOf<ast::BreakStatement, ast::FallthroughStatement>(stmts->Last());
+bool last_is_break(const ast::BlockStatement* stmts) {
+    return IsAnyOf<ast::BreakStatement>(stmts->Last());
 }
 
 void PrintF32(std::ostream& out, float value) {
@@ -167,6 +166,9 @@
 
     manager.Add<transform::DisableUniformityAnalysis>();
 
+    // ExpandCompoundAssignment must come before BuiltinPolyfill
+    manager.Add<transform::ExpandCompoundAssignment>();
+
     {  // Builtin polyfills
         transform::BuiltinPolyfill::Builtins polyfills;
         polyfills.acosh = transform::BuiltinPolyfill::Level::kRangeCheck;
@@ -177,6 +179,7 @@
         polyfills.first_leading_bit = true;
         polyfills.first_trailing_bit = true;
         polyfills.insert_bits = transform::BuiltinPolyfill::Level::kClampParameters;
+        polyfills.int_div_mod = true;
         polyfills.texture_sample_base_clamp_to_edge_2d_f32 = true;
         data.Add<transform::BuiltinPolyfill::Config>(polyfills);
         manager.Add<transform::BuiltinPolyfill>();
@@ -224,11 +227,13 @@
         manager.Add<transform::ZeroInitWorkgroupMemory>();
     }
     manager.Add<transform::CanonicalizeEntryPointIO>();
-    manager.Add<transform::ExpandCompoundAssignment>();
     manager.Add<transform::PromoteSideEffectsToDecl>();
-    manager.Add<transform::UnwindDiscardFunctions>();
     manager.Add<transform::PromoteInitializersToLet>();
 
+    // DemoteToHelper must come after PromoteSideEffectsToDecl and ExpandCompoundAssignment.
+    // TODO(crbug.com/tint/1752): This is only necessary for Metal versions older than 2.3.
+    manager.Add<transform::DemoteToHelper>();
+
     manager.Add<transform::VectorizeScalarMatrixInitializers>();
     manager.Add<transform::RemovePhonies>();
     manager.Add<transform::SimplifyPointers>();
@@ -902,9 +907,7 @@
 
             auto func = utils::GetOrCreate(
                 atomicCompareExchangeWeak_, ACEWKeyType{{sc, str}}, [&]() -> std::string {
-                    // Emit the builtin return type unique to this overload. This does not
-                    // exist in the AST, so it will not be generated in Generate().
-                    if (!EmitStructTypeOnce(&helpers_, builtin->ReturnType()->As<sem::Struct>())) {
+                    if (!EmitStructType(&helpers_, builtin->ReturnType()->As<sem::Struct>())) {
                         return "";
                     }
 
@@ -1371,7 +1374,7 @@
             }
 
             line(b) << StructName(builtin->ReturnType()->As<sem::Struct>()) << " result;";
-            line(b) << "result.sig = frexp(" << in << ", result.exp);";
+            line(b) << "result.fract = frexp(" << in << ", result.exp);";
             line(b) << "return result;";
             return true;
         });
@@ -1581,7 +1584,7 @@
             }
         }
 
-        if (!last_is_break_or_fallthrough(stmt->body)) {
+        if (!last_is_break(stmt->body)) {
             line() << "break;";
         }
     }
@@ -1743,7 +1746,11 @@
             return true;
         },
         [&](const sem::Struct* s) {
-            out << program_->Symbols().NameFor(s->Name()) << "{";
+            if (!EmitStructType(&helpers_, s)) {
+                return false;
+            }
+
+            out << StructName(s) << "{";
             TINT_DEFER(out << "}");
 
             if (constant->AllZero()) {
@@ -2325,40 +2332,45 @@
         return true;
     };
 
-    auto& sem = program_->Sem();
+    auto* sem = builder_.Sem().Get(expr);
 
-    if (auto* swizzle = sem.Get(expr)->As<sem::Swizzle>()) {
-        // Metal 1.x does not support swizzling of packed vector types.
-        // For single element swizzles, we can use the index operator.
-        // For multi-element swizzles, we need to cast to a regular vector type
-        // first. Note that we do not currently allow assignments to swizzles, so
-        // the casting which will convert the l-value to r-value is fine.
-        if (swizzle->Indices().Length() == 1) {
+    return Switch(
+        sem,
+        [&](const sem::Swizzle* swizzle) {
+            // Metal 1.x does not support swizzling of packed vector types.
+            // For single element swizzles, we can use the index operator.
+            // For multi-element swizzles, we need to cast to a regular vector type
+            // first. Note that we do not currently allow assignments to swizzles, so
+            // the casting which will convert the l-value to r-value is fine.
+            if (swizzle->Indices().Length() == 1) {
+                if (!write_lhs()) {
+                    return false;
+                }
+                out << "[" << swizzle->Indices()[0] << "]";
+            } else {
+                if (!EmitType(out, swizzle->Object()->Type()->UnwrapRef(), "")) {
+                    return false;
+                }
+                out << "(";
+                if (!write_lhs()) {
+                    return false;
+                }
+                out << ")." << program_->Symbols().NameFor(expr->member->symbol);
+            }
+            return true;
+        },
+        [&](const sem::StructMemberAccess* member_access) {
             if (!write_lhs()) {
                 return false;
             }
-            out << "[" << swizzle->Indices()[0] << "]";
-        } else {
-            if (!EmitType(out, sem.Get(expr->structure)->Type()->UnwrapRef(), "")) {
-                return false;
-            }
-            out << "(";
-            if (!write_lhs()) {
-                return false;
-            }
-            out << ")." << program_->Symbols().NameFor(expr->member->symbol);
-        }
-    } else {
-        if (!write_lhs()) {
+            out << "." << program_->Symbols().NameFor(member_access->Member()->Name());
+            return true;
+        },
+        [&](Default) {
+            TINT_ICE(Writer, diagnostics_)
+                << "unknown member access type: " << sem->TypeInfo().name;
             return false;
-        }
-        out << ".";
-        if (!EmitExpression(out, expr->member)) {
-            return false;
-        }
-    }
-
-    return true;
+        });
 }
 
 bool GeneratorImpl::EmitReturn(const ast::ReturnStatement* stmt) {
@@ -2415,10 +2427,6 @@
         [&](const ast::DiscardStatement* d) {  //
             return EmitDiscard(d);
         },
-        [&](const ast::FallthroughStatement*) {  //
-            line() << "/* fallthrough */";
-            return true;
-        },
         [&](const ast::IfStatement* i) {  //
             return EmitIf(i);
         },
@@ -2741,6 +2749,11 @@
 }
 
 bool GeneratorImpl::EmitStructType(TextBuffer* b, const sem::Struct* str) {
+    auto it = emitted_structs_.emplace(str);
+    if (!it.second) {
+        return true;
+    }
+
     line(b) << "struct " << StructName(str) << " {";
 
     bool is_host_shareable = str->IsHostShareable();
@@ -2897,14 +2910,6 @@
     return true;
 }
 
-bool GeneratorImpl::EmitStructTypeOnce(TextBuffer* buffer, const sem::Struct* str) {
-    auto it = emitted_structs_.emplace(str);
-    if (!it.second) {
-        return true;
-    }
-    return EmitStructType(buffer, str);
-}
-
 bool GeneratorImpl::EmitUnaryOp(std::ostream& out, const ast::UnaryOpExpression* expr) {
     // Handle `-e` when `e` is signed, so that we ensure that if `e` is the
     // largest negative value, it returns `e`.
diff --git a/src/tint/writer/msl/generator_impl.h b/src/tint/writer/msl/generator_impl.h
index c3e1ac0..829088d 100644
--- a/src/tint/writer/msl/generator_impl.h
+++ b/src/tint/writer/msl/generator_impl.h
@@ -328,17 +328,12 @@
     /// @param sc the address space to generate
     /// @returns true if the address space is emitted
     bool EmitAddressSpace(std::ostream& out, ast::AddressSpace sc);
-    /// Handles generating a struct declaration
+    /// Handles generating a struct declaration. If the structure has already been emitted, then
+    /// this function will simply return `true` without emitting anything.
     /// @param buffer the text buffer that the type declaration will be written to
     /// @param str the struct to generate
     /// @returns true if the struct is emitted
     bool EmitStructType(TextBuffer* buffer, const sem::Struct* str);
-    /// Handles generating a structure declaration only the first time called. Subsequent calls are
-    /// a no-op and return true.
-    /// @param buffer the text buffer that the type declaration will be written to
-    /// @param ty the struct to generate
-    /// @returns true if the struct is emitted
-    bool EmitStructTypeOnce(TextBuffer* buffer, const sem::Struct* ty);
     /// Handles a unary op expression
     /// @param out the output of the expression stream
     /// @param expr the expression to emit
diff --git a/src/tint/writer/msl/generator_impl_builtin_test.cc b/src/tint/writer/msl/generator_impl_builtin_test.cc
index 0c7d0ff..c611d6b 100644
--- a/src/tint/writer/msl/generator_impl_builtin_test.cc
+++ b/src/tint/writer/msl/generator_impl_builtin_test.cc
@@ -405,9 +405,9 @@
     EXPECT_EQ(out.str(), "threadgroup_barrier(mem_flags::mem_threadgroup)");
 }
 
-TEST_F(MslGeneratorImplTest, Modf_Scalar_f32) {
-    auto* call = Call("modf", 1_f);
-    WrapInFunction(CallStmt(call));
+TEST_F(MslGeneratorImplTest, Runtime_Modf_Scalar_f32) {
+    WrapInFunction(Decl(Let("f", Expr(1.5_f))),  //
+                   Decl(Let("v", Call("modf", "f"))));
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
@@ -427,18 +427,19 @@
 }
 
 kernel void test_function() {
-  tint_modf(1.0f);
+  float const f = 1.5f;
+  modf_result const v = tint_modf(f);
   return;
 }
 
 )");
 }
 
-TEST_F(MslGeneratorImplTest, Modf_Scalar_f16) {
+TEST_F(MslGeneratorImplTest, Runtime_Modf_Scalar_f16) {
     Enable(ast::Extension::kF16);
 
-    auto* call = Call("modf", 1_h);
-    WrapInFunction(CallStmt(call));
+    WrapInFunction(Decl(Let("f", Expr(1.5_h))),  //
+                   Decl(Let("v", Call("modf", "f"))));
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
@@ -458,16 +459,17 @@
 }
 
 kernel void test_function() {
-  tint_modf(1.0h);
+  half const f = 1.5h;
+  modf_result_f16 const v = tint_modf(f);
   return;
 }
 
 )");
 }
 
-TEST_F(MslGeneratorImplTest, Modf_Vector_f32) {
-    auto* call = Call("modf", vec3<f32>());
-    WrapInFunction(CallStmt(call));
+TEST_F(MslGeneratorImplTest, Runtime_Modf_Vector_f32) {
+    WrapInFunction(Decl(Let("f", vec3<f32>(1.5_f, 2.5_f, 3.5_f))),  //
+                   Decl(Let("v", Call("modf", "f"))));
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
@@ -487,18 +489,19 @@
 }
 
 kernel void test_function() {
-  tint_modf(float3(0.0f));
+  float3 const f = float3(1.5f, 2.5f, 3.5f);
+  modf_result_vec3 const v = tint_modf(f);
   return;
 }
 
 )");
 }
 
-TEST_F(MslGeneratorImplTest, Modf_Vector_f16) {
+TEST_F(MslGeneratorImplTest, Runtime_Modf_Vector_f16) {
     Enable(ast::Extension::kF16);
 
-    auto* call = Call("modf", vec3<f16>());
-    WrapInFunction(CallStmt(call));
+    WrapInFunction(Decl(Let("f", vec3<f16>(1.5_h, 2.5_h, 3.5_h))),  //
+                   Decl(Let("v", Call("modf", "f"))));
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
@@ -518,7 +521,100 @@
 }
 
 kernel void test_function() {
-  tint_modf(half3(0.0h));
+  half3 const f = half3(1.5h, 2.5h, 3.5h);
+  modf_result_vec3_f16 const v = tint_modf(f);
+  return;
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Const_Modf_Scalar_f32) {
+    WrapInFunction(Decl(Let("v", Call("modf", 1.5_f))));
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+
+struct modf_result {
+  float fract;
+  float whole;
+};
+kernel void test_function() {
+  modf_result const v = modf_result{.fract=0.5f, .whole=1.0f};
+  return;
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Const_Modf_Scalar_f16) {
+    Enable(ast::Extension::kF16);
+
+    WrapInFunction(Decl(Let("v", Call("modf", 1.5_h))));
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+
+struct modf_result_f16 {
+  half fract;
+  half whole;
+};
+kernel void test_function() {
+  modf_result_f16 const v = modf_result_f16{.fract=0.5h, .whole=1.0h};
+  return;
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Const_Modf_Vector_f32) {
+    WrapInFunction(Decl(Let("v", Call("modf", vec3<f32>(1.5_f, 2.5_f, 3.5_f)))));
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+
+struct modf_result_vec3 {
+  float3 fract;
+  float3 whole;
+};
+kernel void test_function() {
+  modf_result_vec3 const v = modf_result_vec3{.fract=float3(0.5f), .whole=float3(1.0f, 2.0f, 3.0f)};
+  return;
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Const_Modf_Vector_f16) {
+    Enable(ast::Extension::kF16);
+
+    WrapInFunction(Decl(Let("v", Call("modf", vec3<f16>(1.5_h, 2.5_h, 3.5_h)))));
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+
+struct modf_result_vec3_f16 {
+  half3 fract;
+  half3 whole;
+};
+kernel void test_function() {
+  modf_result_vec3_f16 const v = modf_result_vec3_f16{.fract=half3(0.5h), .whole=half3(1.0h, 2.0h, 3.0h)};
   return;
 }
 
@@ -537,12 +633,12 @@
 using namespace metal;
 
 struct frexp_result {
-  float sig;
+  float fract;
   int exp;
 };
 frexp_result tint_frexp(float param_0) {
   frexp_result result;
-  result.sig = frexp(param_0, result.exp);
+  result.fract = frexp(param_0, result.exp);
   return result;
 }
 
@@ -568,12 +664,12 @@
 using namespace metal;
 
 struct frexp_result_f16 {
-  half sig;
+  half fract;
   int exp;
 };
 frexp_result_f16 tint_frexp(half param_0) {
   frexp_result_f16 result;
-  result.sig = frexp(param_0, result.exp);
+  result.fract = frexp(param_0, result.exp);
   return result;
 }
 
@@ -597,12 +693,12 @@
 using namespace metal;
 
 struct frexp_result_vec3 {
-  float3 sig;
+  float3 fract;
   int3 exp;
 };
 frexp_result_vec3 tint_frexp(float3 param_0) {
   frexp_result_vec3 result;
-  result.sig = frexp(param_0, result.exp);
+  result.fract = frexp(param_0, result.exp);
   return result;
 }
 
@@ -628,12 +724,12 @@
 using namespace metal;
 
 struct frexp_result_vec3_f16 {
-  half3 sig;
+  half3 fract;
   int3 exp;
 };
 frexp_result_vec3_f16 tint_frexp(half3 param_0) {
   frexp_result_vec3_f16 result;
-  result.sig = frexp(param_0, result.exp);
+  result.fract = frexp(param_0, result.exp);
   return result;
 }
 
@@ -645,6 +741,35 @@
 )");
 }
 
+// TODO(crbug.com/tint/1757): Remove once deprecation period for `frexp().sig` is over
+TEST_F(MslGeneratorImplTest, Frexp_Sig_Deprecation) {
+    WrapInFunction(MemberAccessor(Call("frexp", 1_f), "sig"));
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+
+struct frexp_result {
+  float fract;
+  int exp;
+};
+frexp_result tint_frexp(float param_0) {
+  frexp_result result;
+  result.fract = frexp(param_0, result.exp);
+  return result;
+}
+
+kernel void test_function() {
+  float const tint_symbol = tint_frexp(1.0f).fract;
+  return;
+}
+
+)");
+}
+
 TEST_F(MslGeneratorImplTest, Degrees_Scalar_f32) {
     auto* val = Var("val", ty.f32());
     auto* call = Call("degrees", val);
diff --git a/src/tint/writer/msl/generator_impl_case_test.cc b/src/tint/writer/msl/generator_impl_case_test.cc
index 8aae4fe..c218000 100644
--- a/src/tint/writer/msl/generator_impl_case_test.cc
+++ b/src/tint/writer/msl/generator_impl_case_test.cc
@@ -12,7 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "src/tint/ast/fallthrough_statement.h"
 #include "src/tint/writer/msl/test_helper.h"
 
 using namespace tint::number_suffixes;  // NOLINT
@@ -53,22 +52,6 @@
 )");
 }
 
-TEST_F(MslGeneratorImplTest, Emit_Case_WithFallthrough) {
-    auto* s = Switch(1_i, Case(CaseSelector(5_i), Block(create<ast::FallthroughStatement>())),
-                     DefaultCase());
-    WrapInFunction(s);
-
-    GeneratorImpl& gen = Build();
-
-    gen.increment_indent();
-
-    ASSERT_TRUE(gen.EmitCase(s->body[0])) << gen.error();
-    EXPECT_EQ(gen.result(), R"(  case 5: {
-    /* fallthrough */
-  }
-)");
-}
-
 TEST_F(MslGeneratorImplTest, Emit_Case_MultipleSelectors) {
     auto* s = Switch(1_i,
                      Case(
diff --git a/src/tint/writer/msl/generator_impl_loop_test.cc b/src/tint/writer/msl/generator_impl_loop_test.cc
index 1dd5430..89009e0 100644
--- a/src/tint/writer/msl/generator_impl_loop_test.cc
+++ b/src/tint/writer/msl/generator_impl_loop_test.cc
@@ -23,7 +23,7 @@
 using MslGeneratorImplTest = TestHelper;
 
 TEST_F(MslGeneratorImplTest, Emit_Loop) {
-    auto* body = Block(create<ast::DiscardStatement>());
+    auto* body = Block(Break());
     auto* continuing = Block();
     auto* l = Loop(body, continuing);
 
@@ -36,7 +36,7 @@
 
     ASSERT_TRUE(gen.EmitStatement(l)) << gen.error();
     EXPECT_EQ(gen.result(), R"(  while (true) {
-    discard_fragment();
+    break;
   }
 )");
 }
@@ -44,7 +44,7 @@
 TEST_F(MslGeneratorImplTest, Emit_LoopWithContinuing) {
     Func("a_statement", {}, ty.void_(), utils::Empty);
 
-    auto* body = Block(create<ast::DiscardStatement>());
+    auto* body = Block(Break());
     auto* continuing = Block(CallStmt(Call("a_statement")));
     auto* l = Loop(body, continuing);
 
@@ -57,7 +57,7 @@
 
     ASSERT_TRUE(gen.EmitStatement(l)) << gen.error();
     EXPECT_EQ(gen.result(), R"(  while (true) {
-    discard_fragment();
+    break;
     {
       a_statement();
     }
@@ -68,7 +68,7 @@
 TEST_F(MslGeneratorImplTest, Emit_LoopWithContinuing_BreakIf) {
     Func("a_statement", {}, ty.void_(), {});
 
-    auto* body = Block(create<ast::DiscardStatement>());
+    auto* body = Block(Break());
     auto* continuing = Block(CallStmt(Call("a_statement")), BreakIf(true));
     auto* l = Loop(body, continuing);
 
@@ -81,7 +81,7 @@
 
     ASSERT_TRUE(gen.EmitStatement(l)) << gen.error();
     EXPECT_EQ(gen.result(), R"(  while (true) {
-    discard_fragment();
+    break;
     {
       a_statement();
       if (true) { break; }
@@ -96,13 +96,13 @@
     GlobalVar("lhs", ty.f32(), ast::AddressSpace::kPrivate);
     GlobalVar("rhs", ty.f32(), ast::AddressSpace::kPrivate);
 
-    auto* body = Block(create<ast::DiscardStatement>());
+    auto* body = Block(Break());
     auto* continuing = Block(CallStmt(Call("a_statement")));
     auto* inner = Loop(body, continuing);
 
     body = Block(inner);
 
-    continuing = Block(Assign("lhs", "rhs"));
+    continuing = Block(Assign("lhs", "rhs"), BreakIf(true));
 
     auto* outer = Loop(body, continuing);
 
@@ -116,13 +116,14 @@
     ASSERT_TRUE(gen.EmitStatement(outer)) << gen.error();
     EXPECT_EQ(gen.result(), R"(  while (true) {
     while (true) {
-      discard_fragment();
+      break;
       {
         a_statement();
       }
     }
     {
       lhs = rhs;
+      if (true) { break; }
     }
   }
 )");
diff --git a/src/tint/writer/spirv/builder.cc b/src/tint/writer/spirv/builder.cc
index 6342f71..9b1e077 100644
--- a/src/tint/writer/spirv/builder.cc
+++ b/src/tint/writer/spirv/builder.cc
@@ -19,7 +19,6 @@
 
 #include "spirv/unified1/GLSL.std.450.h"
 #include "src/tint/ast/call_statement.h"
-#include "src/tint/ast/fallthrough_statement.h"
 #include "src/tint/ast/id_attribute.h"
 #include "src/tint/ast/internal_attribute.h"
 #include "src/tint/ast/traverse_expressions.h"
@@ -86,10 +85,6 @@
     return model;
 }
 
-bool LastIsFallthrough(const ast::BlockStatement* stmts) {
-    return !stmts->Empty() && stmts->Last()->Is<ast::FallthroughStatement>();
-}
-
 /// Returns the matrix type that is `type` or that is wrapped by
 /// one or more levels of an arrays inside of `type`.
 /// @param type the given type, which must not be null
@@ -3408,54 +3403,6 @@
 }
 
 bool Builder::GenerateIfStatement(const ast::IfStatement* stmt) {
-    if (!continuing_stack_.empty() &&
-        stmt == continuing_stack_.back().last_statement->As<ast::IfStatement>()) {
-        const ContinuingInfo& ci = continuing_stack_.back();
-        // Match one of two patterns: the break-if and break-unless patterns.
-        //
-        // The break-if pattern:
-        //  continuing { ...
-        //    if (cond) { break; }
-        //  }
-        //
-        // The break-unless pattern:
-        //  continuing { ...
-        //    if (cond) {} else {break;}
-        //  }
-        //
-        // TODO(crbug.com/tint/1451): Remove this when the if break construct is made an error.
-        auto is_just_a_break = [](const ast::BlockStatement* block) {
-            return block && (block->statements.Length() == 1) &&
-                   block->Last()->Is<ast::BreakStatement>();
-        };
-        if (is_just_a_break(stmt->body) && stmt->else_statement == nullptr) {
-            // It's a break-if.
-            TINT_ASSERT(Writer, !backedge_stack_.empty());
-            const auto cond_id = GenerateExpressionWithLoadIfNeeded(stmt->condition);
-            if (!cond_id) {
-                return false;
-            }
-            backedge_stack_.back() = Backedge(
-                spv::Op::OpBranchConditional,
-                {Operand(cond_id), Operand(ci.break_target_id), Operand(ci.loop_header_id)});
-            return true;
-        } else if (stmt->body->Empty()) {
-            auto* es_block = As<ast::BlockStatement>(stmt->else_statement);
-            if (es_block && is_just_a_break(es_block)) {
-                // It's a break-unless.
-                TINT_ASSERT(Writer, !backedge_stack_.empty());
-                const auto cond_id = GenerateExpressionWithLoadIfNeeded(stmt->condition);
-                if (!cond_id) {
-                    return false;
-                }
-                backedge_stack_.back() = Backedge(
-                    spv::Op::OpBranchConditional,
-                    {Operand(cond_id), Operand(ci.loop_header_id), Operand(ci.break_target_id)});
-                return true;
-            }
-        }
-    }
-
     if (!GenerateConditionalBlock(stmt->condition, stmt->body, stmt->else_statement)) {
         return false;
     }
@@ -3515,9 +3462,7 @@
     bool generated_default = false;
     auto& body = stmt->body;
     // We output the case statements in order they were entered in the original
-    // source. Each fallthrough goes to the next case entry, so is a forward
-    // branch, otherwise the branch is to the merge block which comes after
-    // the switch statement.
+    // source. The branch is to the merge block which comes after the switch statement.
     for (uint32_t i = 0; i < body.Length(); i++) {
         auto* item = body[i];
 
@@ -3531,17 +3476,7 @@
         if (!GenerateBlockStatement(item->body)) {
             return false;
         }
-
-        if (LastIsFallthrough(item->body)) {
-            if (i == (body.Length() - 1)) {
-                // This case is caught by Resolver validation
-                TINT_UNREACHABLE(Writer, builder_.Diagnostics());
-                return false;
-            }
-            if (!push_function_inst(spv::Op::OpBranch, {Operand(case_ids[i + 1])})) {
-                return false;
-            }
-        } else if (InsideBasicBlock()) {
+        if (InsideBasicBlock()) {
             if (!push_function_inst(spv::Op::OpBranch, {Operand(merge_block_id)})) {
                 return false;
             }
@@ -3671,10 +3606,6 @@
         [&](const ast::CallStatement* c) { return GenerateCallExpression(c->expr) != 0; },
         [&](const ast::ContinueStatement* c) { return GenerateContinueStatement(c); },
         [&](const ast::DiscardStatement* d) { return GenerateDiscardStatement(d); },
-        [&](const ast::FallthroughStatement*) {
-            // Do nothing here, the fallthrough gets handled by the switch code.
-            return true;
-        },
         [&](const ast::IfStatement* i) { return GenerateIfStatement(i); },
         [&](const ast::LoopStatement* l) { return GenerateLoopStatement(l); },
         [&](const ast::ReturnStatement* r) { return GenerateReturnStatement(r); },
diff --git a/src/tint/writer/spirv/builder_builtin_test.cc b/src/tint/writer/spirv/builder_builtin_test.cc
index efdc43b..c0995e8 100644
--- a/src/tint/writer/spirv/builder_builtin_test.cc
+++ b/src/tint/writer/spirv/builder_builtin_test.cc
@@ -1630,7 +1630,7 @@
     EXPECT_EQ(got, expect);
 }
 
-TEST_F(BuiltinBuilderTest, Call_Modf_f32) {
+TEST_F(BuiltinBuilderTest, Runtime_Call_Modf_f32) {
     auto* vec = Var("vec", vec2<f32>(1_f, 2_f));
     auto* expr = Call("modf", vec);
     Func("a_func", utils::Empty, ty.void_(),
@@ -1682,7 +1682,7 @@
     Validate(b);
 }
 
-TEST_F(BuiltinBuilderTest, Call_Modf_f16) {
+TEST_F(BuiltinBuilderTest, Runtime_Call_Modf_f16) {
     Enable(ast::Extension::kF16);
 
     auto* vec = Var("vec", vec2<f16>(1_h, 2_h));
@@ -1740,6 +1740,100 @@
     Validate(b);
 }
 
+TEST_F(BuiltinBuilderTest, Const_Call_Modf_f32) {
+    auto* expr = Call("modf", vec2<f32>(1_f, 2_f));
+    Func("a_func", utils::Empty, ty.void_(),
+         utils::Vector{
+             CallStmt(expr),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
+
+    spirv::Builder& b = Build();
+
+    ASSERT_TRUE(b.Build()) << b.error();
+    auto got = DumpBuilder(b);
+    auto* expect = R"(OpCapability Shader
+%9 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %3 "a_func"
+OpExecutionMode %3 OriginUpperLeft
+OpName %3 "a_func"
+OpName %6 "__modf_result_vec2"
+OpMemberName %6 0 "fract"
+OpMemberName %6 1 "whole"
+OpMemberDecorate %6 0 Offset 0
+OpMemberDecorate %6 1 Offset 8
+%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%8 = OpTypeFloat 32
+%7 = OpTypeVector %8 2
+%6 = OpTypeStruct %7 %7
+%10 = OpConstant %8 1
+%11 = OpConstant %8 2
+%12 = OpConstantComposite %7 %10 %11
+%3 = OpFunction %2 None %1
+%4 = OpLabel
+%5 = OpExtInst %6 %9 ModfStruct %12
+OpReturn
+OpFunctionEnd
+)";
+    EXPECT_EQ(expect, got);
+
+    Validate(b);
+}
+
+TEST_F(BuiltinBuilderTest, Const_Call_Modf_f16) {
+    Enable(ast::Extension::kF16);
+
+    auto* expr = Call("modf", vec2<f16>(1_h, 2_h));
+    Func("a_func", utils::Empty, ty.void_(),
+         utils::Vector{
+             CallStmt(expr),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
+
+    spirv::Builder& b = Build();
+
+    ASSERT_TRUE(b.Build()) << b.error();
+    auto got = DumpBuilder(b);
+    auto* expect = R"(OpCapability Shader
+OpCapability Float16
+OpCapability UniformAndStorageBuffer16BitAccess
+OpCapability StorageBuffer16BitAccess
+OpCapability StorageInputOutput16
+%9 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %3 "a_func"
+OpExecutionMode %3 OriginUpperLeft
+OpName %3 "a_func"
+OpName %6 "__modf_result_vec2_f16"
+OpMemberName %6 0 "fract"
+OpMemberName %6 1 "whole"
+OpMemberDecorate %6 0 Offset 0
+OpMemberDecorate %6 1 Offset 4
+%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%8 = OpTypeFloat 16
+%7 = OpTypeVector %8 2
+%6 = OpTypeStruct %7 %7
+%10 = OpConstant %8 0x1p+0
+%11 = OpConstant %8 0x1p+1
+%12 = OpConstantComposite %7 %10 %11
+%3 = OpFunction %2 None %1
+%4 = OpLabel
+%5 = OpExtInst %6 %9 ModfStruct %12
+OpReturn
+OpFunctionEnd
+)";
+    EXPECT_EQ(expect, got);
+
+    Validate(b);
+}
+
 TEST_F(BuiltinBuilderTest, Call_Frexp_f32) {
     auto* vec = Var("vec", vec2<f32>(1_f, 2_f));
     auto* expr = Call("frexp", vec);
@@ -1764,7 +1858,7 @@
 OpName %3 "a_func"
 OpName %10 "vec"
 OpName %14 "__frexp_result_vec2"
-OpMemberName %14 0 "sig"
+OpMemberName %14 0 "fract"
 OpMemberName %14 1 "exp"
 OpMemberDecorate %14 0 Offset 0
 OpMemberDecorate %14 1 Offset 8
@@ -1824,7 +1918,7 @@
 OpName %3 "a_func"
 OpName %10 "vec"
 OpName %14 "__frexp_result_vec2_f16"
-OpMemberName %14 0 "sig"
+OpMemberName %14 0 "fract"
 OpMemberName %14 1 "exp"
 OpMemberDecorate %14 0 Offset 0
 OpMemberDecorate %14 1 Offset 8
@@ -1854,6 +1948,78 @@
     Validate(b);
 }
 
+// TODO(crbug.com/tint/1757): Remove once deprecation period for `frexp().sig` is over
+TEST_F(BuiltinBuilderTest, Frexp_Sig_Deprecation) {
+    WrapInFunction(MemberAccessor(Call("frexp", 1_f), "sig"));
+
+    auto* vec = Var("vec", vec2<f32>(1_f, 2_f));
+    Func("a_func", utils::Empty, ty.void_(),
+         utils::Vector{
+             Decl(vec),
+             Decl(Let("s", MemberAccessor(Call("frexp", vec), "sig"))),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
+
+    spirv::Builder& b = Build();
+
+    ASSERT_TRUE(b.Build()) << b.error();
+    auto got = DumpBuilder(b);
+    auto* expect = R"(OpCapability Shader
+%9 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %3 "test_function"
+OpEntryPoint Fragment %12 "a_func"
+OpExecutionMode %3 LocalSize 1 1 1
+OpExecutionMode %12 OriginUpperLeft
+OpName %3 "test_function"
+OpName %6 "__frexp_result"
+OpMemberName %6 0 "fract"
+OpMemberName %6 1 "exp"
+OpName %12 "a_func"
+OpName %17 "vec"
+OpName %21 "__frexp_result_vec2"
+OpMemberName %21 0 "fract"
+OpMemberName %21 1 "exp"
+OpMemberDecorate %6 0 Offset 0
+OpMemberDecorate %6 1 Offset 4
+OpMemberDecorate %21 0 Offset 0
+OpMemberDecorate %21 1 Offset 8
+%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%7 = OpTypeFloat 32
+%8 = OpTypeInt 32 1
+%6 = OpTypeStruct %7 %8
+%10 = OpConstant %7 1
+%14 = OpTypeVector %7 2
+%15 = OpConstant %7 2
+%16 = OpConstantComposite %14 %10 %15
+%18 = OpTypePointer Function %14
+%19 = OpConstantNull %14
+%22 = OpTypeVector %8 2
+%21 = OpTypeStruct %14 %22
+%3 = OpFunction %2 None %1
+%4 = OpLabel
+%5 = OpExtInst %6 %9 FrexpStruct %10
+%11 = OpCompositeExtract %7 %5 0
+OpReturn
+OpFunctionEnd
+%12 = OpFunction %2 None %1
+%13 = OpLabel
+%17 = OpVariable %18 Function %19
+OpStore %17 %16
+%23 = OpLoad %14 %17
+%20 = OpExtInst %21 %9 FrexpStruct %23
+%24 = OpCompositeExtract %14 %20 0
+OpReturn
+OpFunctionEnd
+)";
+    EXPECT_EQ(expect, got);
+
+    Validate(b);
+}
+
 TEST_F(BuiltinBuilderTest, Call_QuantizeToF16_Scalar) {
     GlobalVar("v", Expr(2_f), ast::AddressSpace::kPrivate);
 
diff --git a/src/tint/writer/spirv/builder_loop_test.cc b/src/tint/writer/spirv/builder_loop_test.cc
index e27cac4..9d19bde 100644
--- a/src/tint/writer/spirv/builder_loop_test.cc
+++ b/src/tint/writer/spirv/builder_loop_test.cc
@@ -332,44 +332,6 @@
 )");
 }
 
-TEST_F(BuilderTest, Loop_WithContinuing_BreakUnless_ConditionIsVar) {
-    // loop {
-    //   continuing {
-    //     var cond = true;
-    //     if (cond) {} else { break; }
-    //   }
-    // }
-    auto* cond_var = Decl(Var("cond", Expr(true)));
-    auto* if_stmt = If(Expr("cond"), Block(), Else(Block(Break())));
-    auto* continuing = Block(cond_var, if_stmt);
-    auto* loop = Loop(Block(), continuing);
-    WrapInFunction(loop);
-
-    spirv::Builder& b = Build();
-
-    b.push_function(Function{});
-
-    EXPECT_TRUE(b.GenerateLoopStatement(loop)) << b.error();
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%5 = OpTypeBool
-%6 = OpConstantTrue %5
-%8 = OpTypePointer Function %5
-%9 = OpConstantNull %5
-)");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-              R"(OpBranch %1
-%1 = OpLabel
-OpLoopMerge %2 %3 None
-OpBranch %4
-%4 = OpLabel
-OpBranch %3
-%3 = OpLabel
-OpStore %7 %6
-%10 = OpLoad %5 %7
-OpBranchConditional %10 %1 %2
-%2 = OpLabel
-)");
-}
-
 TEST_F(BuilderTest, Loop_WithContinuing_BreakIf_Nested) {
     // Make sure the right backedge and break target are used.
     // loop {
@@ -421,58 +383,5 @@
 )");
 }
 
-TEST_F(BuilderTest, Loop_WithContinuing_BreakUnless_Nested) {
-    // Make sure the right backedge and break target are used.
-    // loop {
-    //   continuing {
-    //     loop {
-    //       continuing {
-    //         if (true) {} else { break; }
-    //       }
-    //     }
-    //     if (true) {} else { break; }
-    //   }
-    // }
-
-    auto* inner_if_stmt = If(Expr(true), Block(), Else(Block(Break())));
-    auto* inner_continuing = Block(inner_if_stmt);
-    auto* inner_loop = Loop(Block(), inner_continuing);
-
-    auto* outer_if_stmt = If(Expr(true), Block(), Else(Block(Break())));
-    auto* outer_continuing = Block(inner_loop, outer_if_stmt);
-    auto* outer_loop = Loop(Block(), outer_continuing);
-
-    WrapInFunction(outer_loop);
-
-    spirv::Builder& b = Build();
-
-    b.push_function(Function{});
-
-    EXPECT_TRUE(b.GenerateLoopStatement(outer_loop)) << b.error();
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%9 = OpTypeBool
-%10 = OpConstantTrue %9
-)");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-              R"(OpBranch %1
-%1 = OpLabel
-OpLoopMerge %2 %3 None
-OpBranch %4
-%4 = OpLabel
-OpBranch %3
-%3 = OpLabel
-OpBranch %5
-%5 = OpLabel
-OpLoopMerge %6 %7 None
-OpBranch %8
-%8 = OpLabel
-OpBranch %7
-%7 = OpLabel
-OpBranchConditional %10 %5 %6
-%6 = OpLabel
-OpBranchConditional %10 %1 %2
-%2 = OpLabel
-)");
-}
-
 }  // namespace
 }  // namespace tint::writer::spirv
diff --git a/src/tint/writer/spirv/builder_static_assert_test.cc b/src/tint/writer/spirv/builder_static_assert_test.cc
index c6d6a51..6e5092d 100644
--- a/src/tint/writer/spirv/builder_static_assert_test.cc
+++ b/src/tint/writer/spirv/builder_static_assert_test.cc
@@ -12,7 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "src/tint/ast/fallthrough_statement.h"
 #include "src/tint/writer/spirv/spv_dump.h"
 #include "src/tint/writer/spirv/test_helper.h"
 
diff --git a/src/tint/writer/spirv/builder_switch_test.cc b/src/tint/writer/spirv/builder_switch_test.cc
index c59c640..6cf8fad 100644
--- a/src/tint/writer/spirv/builder_switch_test.cc
+++ b/src/tint/writer/spirv/builder_switch_test.cc
@@ -12,7 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "src/tint/ast/fallthrough_statement.h"
 #include "src/tint/writer/spirv/spv_dump.h"
 #include "src/tint/writer/spirv/test_helper.h"
 
@@ -328,69 +327,6 @@
 )");
 }
 
-TEST_F(BuilderTest, Switch_CaseWithFallthrough) {
-    // switch(a) {
-    //   case 1i:
-    //      v = 1i;
-    //      fallthrough;
-    //   case 2i:
-    //      v = 2i;
-    //   default: {}
-    //      v = 3i;
-    //  }
-
-    auto* v = GlobalVar("v", ty.i32(), ast::AddressSpace::kPrivate);
-    auto* a = GlobalVar("a", ty.i32(), ast::AddressSpace::kPrivate);
-
-    auto* func = Func("a_func", utils::Empty, ty.void_(),
-                      utils::Vector{
-                          Switch(Expr("a"),                                     //
-                                 Case(CaseSelector(1_i),                        //
-                                      Block(Assign("v", 1_i), Fallthrough())),  //
-                                 Case(CaseSelector(2_i),                        //
-                                      Block(Assign("v", 2_i))),                 //
-                                 DefaultCase(Block(Assign("v", 3_i)))),
-                      });
-
-    spirv::Builder& b = Build();
-
-    ASSERT_TRUE(b.GenerateGlobalVariable(v)) << b.error();
-    ASSERT_TRUE(b.GenerateGlobalVariable(a)) << b.error();
-    ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
-
-    EXPECT_EQ(DumpBuilder(b), R"(OpName %1 "v"
-OpName %5 "a"
-OpName %8 "a_func"
-%3 = OpTypeInt 32 1
-%2 = OpTypePointer Private %3
-%4 = OpConstantNull %3
-%1 = OpVariable %2 Private %4
-%5 = OpVariable %2 Private %4
-%7 = OpTypeVoid
-%6 = OpTypeFunction %7
-%15 = OpConstant %3 1
-%16 = OpConstant %3 2
-%17 = OpConstant %3 3
-%8 = OpFunction %7 None %6
-%9 = OpLabel
-%11 = OpLoad %3 %5
-OpSelectionMerge %10 None
-OpSwitch %11 %12 1 %13 2 %14
-%13 = OpLabel
-OpStore %1 %15
-OpBranch %14
-%14 = OpLabel
-OpStore %1 %16
-OpBranch %10
-%12 = OpLabel
-OpStore %1 %17
-OpBranch %10
-%10 = OpLabel
-OpReturn
-OpFunctionEnd
-)");
-}
-
 TEST_F(BuilderTest, Switch_WithNestedBreak) {
     // switch (a) {
     //   case 1:
@@ -460,7 +396,7 @@
     //     return 1i;
     //   }
     //   case 2i: {
-    //     fallthrough;
+    //     return 1i;
     //   }
     //   default: {
     //     return 3i;
@@ -469,9 +405,9 @@
 
     auto* fn = Func("f", utils::Empty, ty.i32(),
                     utils::Vector{
-                        Switch(1_i,                                            //
-                               Case(CaseSelector(1_i), Block(Return(1_i))),    //
-                               Case(CaseSelector(2_i), Block(Fallthrough())),  //
+                        Switch(1_i,                                          //
+                               Case(CaseSelector(1_i), Block(Return(1_i))),  //
+                               Case(CaseSelector(2_i), Block(Return(1_i))),  //
                                DefaultCase(Block(Return(3_i)))),
                     });
 
@@ -491,7 +427,7 @@
 %8 = OpLabel
 OpReturnValue %6
 %9 = OpLabel
-OpBranch %7
+OpReturnValue %6
 %7 = OpLabel
 OpReturnValue %10
 %5 = OpLabel
diff --git a/src/tint/writer/spirv/generator_impl.cc b/src/tint/writer/spirv/generator_impl.cc
index aa5d449..18c7511 100644
--- a/src/tint/writer/spirv/generator_impl.cc
+++ b/src/tint/writer/spirv/generator_impl.cc
@@ -21,6 +21,7 @@
 #include "src/tint/transform/add_empty_entry_point.h"
 #include "src/tint/transform/builtin_polyfill.h"
 #include "src/tint/transform/canonicalize_entry_point_io.h"
+#include "src/tint/transform/demote_to_helper.h"
 #include "src/tint/transform/disable_uniformity_analysis.h"
 #include "src/tint/transform/expand_compound_assignment.h"
 #include "src/tint/transform/for_loop_to_loop.h"
@@ -32,7 +33,6 @@
 #include "src/tint/transform/simplify_pointers.h"
 #include "src/tint/transform/std140.h"
 #include "src/tint/transform/unshadow.h"
-#include "src/tint/transform/unwind_discard_functions.h"
 #include "src/tint/transform/var_for_dynamic_index.h"
 #include "src/tint/transform/vectorize_matrix_conversions.h"
 #include "src/tint/transform/vectorize_scalar_matrix_initializers.h"
@@ -48,6 +48,9 @@
 
     manager.Add<transform::DisableUniformityAnalysis>();
 
+    // ExpandCompoundAssignment must come before BuiltinPolyfill
+    manager.Add<transform::ExpandCompoundAssignment>();
+
     {  // Builtin polyfills
         transform::BuiltinPolyfill::Builtins polyfills;
         polyfills.acosh = transform::BuiltinPolyfill::Level::kRangeCheck;
@@ -60,6 +63,7 @@
         polyfills.first_leading_bit = true;
         polyfills.first_trailing_bit = true;
         polyfills.insert_bits = transform::BuiltinPolyfill::Level::kClampParameters;
+        polyfills.int_div_mod = true;
         polyfills.saturate = true;
         polyfills.texture_sample_base_clamp_to_edge_2d_f32 = true;
         polyfills.quantize_to_vec_f16 = true;  // crbug.com/tint/1741
@@ -80,9 +84,7 @@
         manager.Add<transform::ZeroInitWorkgroupMemory>();
     }
     manager.Add<transform::RemoveUnreachableStatements>();
-    manager.Add<transform::ExpandCompoundAssignment>();
     manager.Add<transform::PromoteSideEffectsToDecl>();
-    manager.Add<transform::UnwindDiscardFunctions>();
     manager.Add<transform::SimplifyPointers>();  // Required for arrayLength()
     manager.Add<transform::RemovePhonies>();
     manager.Add<transform::VectorizeScalarMatrixInitializers>();
@@ -93,6 +95,11 @@
     manager.Add<transform::AddEmptyEntryPoint>();
     manager.Add<transform::AddBlockAttribute>();
 
+    // DemoteToHelper must come after CanonicalizeEntryPointIO, PromoteSideEffectsToDecl, and
+    // ExpandCompoundAssignment.
+    // TODO(crbug.com/tint/1752): Use SPV_EXT_demote_to_helper_invocation if available.
+    manager.Add<transform::DemoteToHelper>();
+
     // Std140 must come after PromoteSideEffectsToDecl.
     // Std140 must come before VarForDynamicIndex and ForLoopToLoop.
     manager.Add<transform::Std140>();
diff --git a/src/tint/writer/wgsl/generator_impl.cc b/src/tint/writer/wgsl/generator_impl.cc
index bf7ebd7..6327c6c 100644
--- a/src/tint/writer/wgsl/generator_impl.cc
+++ b/src/tint/writer/wgsl/generator_impl.cc
@@ -970,7 +970,6 @@
         [&](const ast::CompoundAssignmentStatement* c) { return EmitCompoundAssign(c); },
         [&](const ast::ContinueStatement* c) { return EmitContinue(c); },
         [&](const ast::DiscardStatement* d) { return EmitDiscard(d); },
-        [&](const ast::FallthroughStatement* f) { return EmitFallthrough(f); },
         [&](const ast::IfStatement* i) { return EmitIf(i); },
         [&](const ast::IncrementDecrementStatement* l) { return EmitIncrementDecrement(l); },
         [&](const ast::LoopStatement* l) { return EmitLoop(l); },
@@ -1093,11 +1092,6 @@
     return true;
 }
 
-bool GeneratorImpl::EmitFallthrough(const ast::FallthroughStatement*) {
-    line() << "fallthrough;";
-    return true;
-}
-
 bool GeneratorImpl::EmitIf(const ast::IfStatement* stmt) {
     {
         auto out = line();
diff --git a/src/tint/writer/wgsl/generator_impl.h b/src/tint/writer/wgsl/generator_impl.h
index 2b7f1c9..7c76cbd 100644
--- a/src/tint/writer/wgsl/generator_impl.h
+++ b/src/tint/writer/wgsl/generator_impl.h
@@ -25,7 +25,6 @@
 #include "src/tint/ast/compound_assignment_statement.h"
 #include "src/tint/ast/continue_statement.h"
 #include "src/tint/ast/discard_statement.h"
-#include "src/tint/ast/fallthrough_statement.h"
 #include "src/tint/ast/for_loop_statement.h"
 #include "src/tint/ast/if_statement.h"
 #include "src/tint/ast/index_accessor_expression.h"
@@ -124,10 +123,6 @@
     /// @param expr the expression
     /// @returns true if the expression was emitted
     bool EmitExpression(std::ostream& out, const ast::Expression* expr);
-    /// Handles generating a fallthrough statement
-    /// @param stmt the fallthrough statement
-    /// @returns true if the statement was successfully emitted
-    bool EmitFallthrough(const ast::FallthroughStatement* stmt);
     /// Handles generating a function
     /// @param func the function to generate
     /// @returns true if the function was emitted
diff --git a/src/tint/writer/wgsl/generator_impl_fallthrough_test.cc b/src/tint/writer/wgsl/generator_impl_fallthrough_test.cc
deleted file mode 100644
index 093fba1..0000000
--- a/src/tint/writer/wgsl/generator_impl_fallthrough_test.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2020 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/writer/wgsl/test_helper.h"
-
-using namespace tint::number_suffixes;  // NOLINT
-
-namespace tint::writer::wgsl {
-namespace {
-
-using WgslGeneratorImplTest = TestHelper;
-
-TEST_F(WgslGeneratorImplTest, Emit_Fallthrough) {
-    auto* f = create<ast::FallthroughStatement>();
-    WrapInFunction(Switch(1_i,                                //
-                          Case(CaseSelector(1_i), Block(f)),  //
-                          DefaultCase()));
-
-    GeneratorImpl& gen = Build();
-
-    gen.increment_indent();
-
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
-    EXPECT_EQ(gen.result(), "  fallthrough;\n");
-}
-
-}  // namespace
-}  // namespace tint::writer::wgsl
diff --git a/src/tint/writer/wgsl/generator_impl_loop_test.cc b/src/tint/writer/wgsl/generator_impl_loop_test.cc
index 48510bb..ac3f808 100644
--- a/src/tint/writer/wgsl/generator_impl_loop_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_loop_test.cc
@@ -22,7 +22,7 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, Emit_Loop) {
-    auto* body = Block(create<ast::DiscardStatement>());
+    auto* body = Block(Break());
     auto* continuing = Block();
     auto* l = Loop(body, continuing);
 
@@ -35,7 +35,7 @@
 
     ASSERT_TRUE(gen.EmitStatement(l)) << gen.error();
     EXPECT_EQ(gen.result(), R"(  loop {
-    discard;
+    break;
   }
 )");
 }
@@ -43,7 +43,7 @@
 TEST_F(WgslGeneratorImplTest, Emit_LoopWithContinuing) {
     Func("a_statement", {}, ty.void_(), {});
 
-    auto* body = Block(create<ast::DiscardStatement>());
+    auto* body = Block(Break());
     auto* continuing = Block(CallStmt(Call("a_statement")));
     auto* l = Loop(body, continuing);
 
@@ -56,7 +56,7 @@
 
     ASSERT_TRUE(gen.EmitStatement(l)) << gen.error();
     EXPECT_EQ(gen.result(), R"(  loop {
-    discard;
+    break;
 
     continuing {
       a_statement();