Import Tint changes from Dawn

Changes:
  - d4908670e1773c358d178b116e308e06262d9683 tint: clean up const eval test framework by Antonio Maiorano <amaiorano@google.com>
  - 8392a82a40b73685a9832d42556efd7c43e4951e Const eval for `normalize` by dan sinclair <dsinclair@chromium.org>
  - 91ad2bc3846280f5c4679ee6b2168fa0ff0e6656 Remove support for SPIRV-Tools API transition by David Neto <dneto@google.com>
  - 724ad2a290af41acaa6a6d390412a6d460638f8c Const eval for `fma` by dan sinclair <dsinclair@chromium.org>
  - a2a88950200c553e6bcce254fb4515c08f876b69 Const eval for `distance` by dan sinclair <dsinclair@chromium.org>
  - 7736153c1505431d8d9b044af2decef04bac1fee Const eval for `inverseSqrt` by dan sinclair <dsinclair@chromium.org>
  - ab9b5f3aa5996054993f01914fe5a6833a2c8e38 Tint: Implement f16 in uniform and storage address space by Zhaoming Jiang <zhaoming.jiang@intel.com>
  - 9ba5f9e2c6406ec15e9a8d1390843aa4606a78ae tint: const eval of transpose builtin by Antonio Maiorano <amaiorano@google.com>
  - b785dc1e3961c664cbd8b0b6d2d181af5b15f7b3 Update parser to match * and & spec change. by dan sinclair <dsinclair@chromium.org>
  - 7c6e229a18cab18b1a069cb83f91809819e32828 tint/utils: Make Hashmap::Find() safer to use by Ben Clayton <bclayton@google.com>
  - 597ad53029a2d0834da2b799d586beb7d0379b68 tint/transform: Add HoistToDeclBefore::VariableKind by Ben Clayton <bclayton@google.com>
  - ed998e91ab43fa7a9e62f8e716af7b058305aae3 tint: Suffix builtin return types with '_f32' by Ben Clayton <bclayton@google.com>
  - 05c8daac420965194364a6647bfadf5322c284c2 tint: const eval of determinant builtin by Antonio Maiorano <amaiorano@google.com>
  - 5180be6b1d1248542573cad51b5283810b9c4fcf Remove reserved words. by dan sinclair <dsinclair@chromium.org>
  - 69c2c34326e09ced925cea219e3e5da22f42d1bb tint: Implement const-eval of frexp() by Ben Clayton <bclayton@google.com>
  - f2ad5fd260505911d3cd51bafc9142b7ee9baecb Add const-eval for `log` and `log2`. by dan sinclair <dsinclair@chromium.org>
  - ae739d6d1cf29d1ca45e4a2b5840fa7f3f2b003f Add const-eval for `exp` and `exp2`. by dan sinclair <dsinclair@chromium.org>
  - 47fb4f9c5738e8c6e94f042e35e8b064f7fe5c67 tint/ir: fix double-underscore in identifier by Ben Clayton <bclayton@google.com>
  - 31b7fca82c312035917f2168cb40d6d0966c1607 tint: Add abstract overload of modf by Ben Clayton <bclayton@google.com>
GitOrigin-RevId: d4908670e1773c358d178b116e308e06262d9683
Change-Id: I533b0650df13fee8e7175bfb407677d64bd30541
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/109580
Commit-Queue: Ben Clayton <bclayton@chromium.org>
Reviewed-by: Ben Clayton <bclayton@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index 491fd7d..3741764 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -1246,6 +1246,8 @@
       "transform/single_entry_point_test.cc",
       "transform/spirv_atomic_test.cc",
       "transform/std140_exhaustive_test.cc",
+      "transform/std140_f16_test.cc",
+      "transform/std140_f32_test.cc",
       "transform/std140_test.cc",
       "transform/substitute_override_test.cc",
       "transform/test_helper.h",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index dfe9433..a8644b7 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -1212,6 +1212,8 @@
       transform/single_entry_point_test.cc
       transform/spirv_atomic_test.cc
       transform/std140_exhaustive_test.cc
+      transform/std140_f16_test.cc
+      transform/std140_f32_test.cc
       transform/std140_test.cc
       transform/substitute_override_test.cc
       transform/test_helper.h
diff --git a/src/tint/clone_context.cc b/src/tint/clone_context.cc
index 457522b..fe94a14 100644
--- a/src/tint/clone_context.cc
+++ b/src/tint/clone_context.cc
@@ -73,7 +73,7 @@
     }
 
     // Was Replace() called for this object?
-    if (auto* fn = replacements_.Find(object)) {
+    if (auto fn = replacements_.Find(object)) {
         return (*fn)();
     }
 
diff --git a/src/tint/clone_context.h b/src/tint/clone_context.h
index 05f4868..f1db617 100644
--- a/src/tint/clone_context.h
+++ b/src/tint/clone_context.h
@@ -208,7 +208,7 @@
                 to.Push(CheckedCast<T>(builder()));
             }
             for (auto& el : from) {
-                if (auto* insert_before = transforms->insert_before_.Find(el)) {
+                if (auto insert_before = transforms->insert_before_.Find(el)) {
                     for (auto& builder : *insert_before) {
                         to.Push(CheckedCast<T>(builder()));
                     }
@@ -216,7 +216,7 @@
                 if (!transforms->remove_.Contains(el)) {
                     to.Push(Clone(el));
                 }
-                if (auto* insert_after = transforms->insert_after_.Find(el)) {
+                if (auto insert_after = transforms->insert_after_.Find(el)) {
                     for (auto& builder : *insert_after) {
                         to.Push(CheckedCast<T>(builder()));
                     }
@@ -232,7 +232,7 @@
                 // Clone(el) may have updated the transformation list, adding an `insert_after`
                 // transform for `from`.
                 if (transforms) {
-                    if (auto* insert_after = transforms->insert_after_.Find(el)) {
+                    if (auto insert_after = transforms->insert_after_.Find(el)) {
                         for (auto& builder : *insert_after) {
                             to.Push(CheckedCast<T>(builder()));
                         }
@@ -389,7 +389,7 @@
             return *this;
         }
 
-        list_transforms_.Edit(&vector).remove_.Add(object);
+        list_transforms_.GetOrZero(&vector)->remove_.Add(object);
         return *this;
     }
 
@@ -411,7 +411,7 @@
     /// @returns this CloneContext so calls can be chained
     template <typename T, size_t N, typename BUILDER>
     CloneContext& InsertFront(const utils::Vector<T, N>& vector, BUILDER&& builder) {
-        list_transforms_.Edit(&vector).insert_front_.Push(std::forward<BUILDER>(builder));
+        list_transforms_.GetOrZero(&vector)->insert_front_.Push(std::forward<BUILDER>(builder));
         return *this;
     }
 
@@ -434,7 +434,7 @@
     /// @returns this CloneContext so calls can be chained
     template <typename T, size_t N, typename BUILDER>
     CloneContext& InsertBack(const utils::Vector<T, N>& vector, BUILDER&& builder) {
-        list_transforms_.Edit(&vector).insert_back_.Push(std::forward<BUILDER>(builder));
+        list_transforms_.GetOrZero(&vector)->insert_back_.Push(std::forward<BUILDER>(builder));
         return *this;
     }
 
@@ -456,7 +456,7 @@
             return *this;
         }
 
-        list_transforms_.Edit(&vector).insert_before_.GetOrZero(before).Push(
+        list_transforms_.GetOrZero(&vector)->insert_before_.GetOrZero(before)->Push(
             [object] { return object; });
         return *this;
     }
@@ -475,7 +475,7 @@
     CloneContext& InsertBefore(const utils::Vector<T, N>& vector,
                                const BEFORE* before,
                                BUILDER&& builder) {
-        list_transforms_.Edit(&vector).insert_before_.GetOrZero(before).Push(
+        list_transforms_.GetOrZero(&vector)->insert_before_.GetOrZero(before)->Push(
             std::forward<BUILDER>(builder));
         return *this;
     }
@@ -498,7 +498,7 @@
             return *this;
         }
 
-        list_transforms_.Edit(&vector).insert_after_.GetOrZero(after).Push(
+        list_transforms_.GetOrZero(&vector)->insert_after_.GetOrZero(after)->Push(
             [object] { return object; });
         return *this;
     }
@@ -517,7 +517,7 @@
     CloneContext& InsertAfter(const utils::Vector<T, N>& vector,
                               const AFTER* after,
                               BUILDER&& builder) {
-        list_transforms_.Edit(&vector).insert_after_.GetOrZero(after).Push(
+        list_transforms_.GetOrZero(&vector)->insert_after_.GetOrZero(after)->Push(
             std::forward<BUILDER>(builder));
         return *this;
     }
@@ -601,61 +601,6 @@
     /// @returns the diagnostic list of #dst
     diag::List& Diagnostics() const;
 
-    /// VectorListTransforms is a map of utils::Vector pointer to transforms for that list
-    struct VectorListTransforms {
-        using Map = utils::Hashmap<const void*, ListTransforms, 4>;
-
-        /// An accessor to the VectorListTransforms map.
-        /// Index caches the last map lookup, and will only re-search the map if the transform map
-        /// was modified since the last lookup.
-        struct Index {
-            /// @returns true if the map now holds a value for the index
-            operator bool() {
-                Update();
-                return cached_;
-            }
-
-            /// @returns a pointer to the indexed map entry
-            const ListTransforms* operator->() {
-                Update();
-                return cached_;
-            }
-
-          private:
-            friend VectorListTransforms;
-
-            Index(const void* list, Map* map)
-                : list_(list),
-                  map_(map),
-                  generation_(map->Generation()),
-                  cached_(map_->Find(list)) {}
-
-            void Update() {
-                if (map_->Generation() != generation_) {
-                    cached_ = map_->Find(list_);
-                    generation_ = map_->Generation();
-                }
-            }
-
-            const void* list_;
-            Map* map_;
-            uint64_t generation_;
-            const ListTransforms* cached_;
-        };
-
-        /// Edit returns a reference to the ListTransforms for the given vector pointer and
-        /// increments #list_transform_generation_ signalling that the list transforms have been
-        /// modified.
-        inline ListTransforms& Edit(const void* list) { return map_.GetOrZero(list); }
-
-        /// @returns an Index to the transforms for the given list.
-        inline Index Find(const void* list) { return Index{list, &map_}; }
-
-      private:
-        /// The map of vector pointer to ListTransforms
-        Map map_;
-    };
-
     /// A map of object in #src to functions that create their replacement in #dst
     utils::Hashmap<const Cloneable*, std::function<const Cloneable*()>, 8> replacements_;
 
@@ -666,7 +611,7 @@
     utils::Vector<CloneableTransform, 8> transforms_;
 
     /// Transformations to apply to vectors
-    VectorListTransforms list_transforms_;
+    utils::Hashmap<const void*, ListTransforms, 4> list_transforms_;
 
     /// Symbol transform registered with ReplaceAll()
     SymbolTransform symbol_transform_;
diff --git a/src/tint/intrinsics.def b/src/tint/intrinsics.def
index da58aea..a148d4d 100644
--- a/src/tint/intrinsics.def
+++ b/src/tint/intrinsics.def
@@ -442,9 +442,9 @@
 @const fn cross<T: fa_f32_f16>(vec3<T>, vec3<T>) -> vec3<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
+@const fn determinant<N: num, T: fa_f32_f16>(mat<N, N, T>) -> T
+@const fn distance<T: fa_f32_f16>(T, T) -> T
+@const fn distance<N: num, T: fa_f32_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
@@ -460,10 +460,10 @@
 @stage("fragment") fn dpdyCoarse<N: num>(vec<N, f32>) -> vec<N, f32>
 @stage("fragment") fn dpdyFine(f32) -> f32
 @stage("fragment") fn dpdyFine<N: num>(vec<N, f32>) -> vec<N, f32>
-fn exp<T: f32_f16>(T) -> T
-fn exp<N: num, T: f32_f16>(vec<N, T>) -> vec<N, T>
-fn exp2<T: f32_f16>(T) -> T
-fn exp2<N: num, T: f32_f16>(vec<N, T>) -> vec<N, T>
+@const fn exp<T: fa_f32_f16>(T) -> T
+@const fn exp<N: num, T: fa_f32_f16>(vec<N, T>) -> vec<N, T>
+@const fn exp2<T: fa_f32_f16>(T) -> T
+@const fn exp2<N: num, T: fa_f32_f16>(vec<N, T>) -> vec<N, T>
 @const fn extractBits<T: iu32>(T, u32, u32) -> T
 @const fn extractBits<N: num, T: iu32>(vec<N, T>, u32, u32) -> vec<N, T>
 fn faceForward<N: num, T: f32_f16>(vec<N, T>, vec<N, T>, vec<N, T>) -> vec<N, T>
@@ -473,12 +473,12 @@
 @const fn firstTrailingBit<N: num, T: iu32>(vec<N, T>) -> vec<N, T>
 @const fn floor<T: fa_f32_f16>(@test_value(1.5) T) -> T
 @const fn floor<N: num, T: fa_f32_f16>(@test_value(1.5) vec<N, T>) -> vec<N, T>
-fn fma<T: f32_f16>(T, T, T) -> T
-fn fma<N: num, T: f32_f16>(vec<N, T>, vec<N, T>, vec<N, T>) -> vec<N, T>
+@const fn fma<T: fa_f32_f16>(T, T, T) -> T
+@const fn fma<N: num, T: fa_f32_f16>(vec<N, T>, vec<N, T>, vec<N, T>) -> vec<N, T>
 fn fract<T: f32_f16>(T) -> T
 fn fract<N: num, T: f32_f16>(vec<N, T>) -> vec<N, T>
-fn frexp<T: f32_f16>(T) -> __frexp_result<T>
-fn frexp<N: num, T: f32_f16>(vec<N, T>) -> __frexp_result_vec<N, T>
+@const fn frexp<T: fa_f32_f16>(T) -> __frexp_result<T>
+@const fn frexp<N: num, T: fa_f32_f16>(vec<N, T>) -> __frexp_result_vec<N, T>
 @stage("fragment") fn fwidth(f32) -> f32
 @stage("fragment") fn fwidth<N: num>(vec<N, f32>) -> vec<N, f32>
 @stage("fragment") fn fwidthCoarse(f32) -> f32
@@ -487,16 +487,16 @@
 @stage("fragment") fn fwidthFine<N: num>(vec<N, f32>) -> vec<N, f32>
 @const fn insertBits<T: iu32>(T, T, u32, u32) -> T
 @const fn insertBits<N: num, T: iu32>(vec<N, T>, vec<N, T>, u32, u32) -> vec<N, T>
-fn inverseSqrt<T: f32_f16>(T) -> T
-fn inverseSqrt<N: num, T: f32_f16>(vec<N, T>) -> vec<N, T>
+@const fn inverseSqrt<T: fa_f32_f16>(T) -> T
+@const fn inverseSqrt<N: num, T: fa_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>
 @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>
+@const fn log<T: fa_f32_f16>(T) -> T
+@const fn log<N: num, T: fa_f32_f16>(vec<N, T>) -> vec<N, T>
+@const fn log2<T: fa_f32_f16>(T) -> T
+@const fn log2<N: num, T: fa_f32_f16>(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
@@ -504,9 +504,9 @@
 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>
-@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>
+@const fn modf<T: fa_f32_f16>(@test_value(-1.5) T) -> __modf_result<T>
+@const fn modf<N: num, T: fa_f32_f16>(@test_value(-1.5) vec<N, T>) -> __modf_result_vec<N, T>
+@const fn normalize<N: num, T: fa_f32_f16>(vec<N, T>) -> vec<N, T>
 @const fn pack2x16float(vec2<f32>) -> u32
 @const fn pack2x16snorm(vec2<f32>) -> u32
 @const fn pack2x16unorm(vec2<f32>) -> u32
@@ -546,7 +546,7 @@
 @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>
+@const fn transpose<M: num, N: num, T: fa_f32_f16>(mat<M, N, T>) -> mat<N, M, T>
 @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>
diff --git a/src/tint/ir/disassembler.cc b/src/tint/ir/disassembler.cc
index 88e2e69..a648d68 100644
--- a/src/tint/ir/disassembler.cc
+++ b/src/tint/ir/disassembler.cc
@@ -26,15 +26,15 @@
 
 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(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_); }
+    ~ScopedStopNode() { stop_nodes_->erase(node_); }
 
   private:
-    std::unordered_set<const FlowNode*>* stop_nodes__;
+    std::unordered_set<const FlowNode*>* stop_nodes_;
     const FlowNode* node_;
 };
 
diff --git a/src/tint/reader/spirv/function.cc b/src/tint/reader/spirv/function.cc
index f97d169..d2edcaf 100644
--- a/src/tint/reader/spirv/function.cc
+++ b/src/tint/reader/spirv/function.cc
@@ -143,83 +143,19 @@
 
 namespace tint::reader::spirv {
 
-namespace three_sided_patch_function_cc {
-// This machinery is only used while SPIRV-Tools is in transition before it fully
-// uses the C++11 header spirv.hpp11
-
-/// Typedef for pointer to member function while the API call uses
-/// SpvStorageClass for its second argument.
-typedef uint32_t (
-    spvtools::opt::analysis::TypeManager::*PointerFinderSpvStorageClass)(uint32_t, SpvStorageClass);
-/// Typedef for pointer to member function while the API call uses
-/// spv::StorageClass for its second argument.
-typedef uint32_t (spvtools::opt::analysis::TypeManager::*PointerFinderSpvStorageClassCpp11)(
-    uint32_t,
-    spv::StorageClass);
-
-/// @param type_manager the SPIRV-Tools optimizer's type manager
-/// @param finder a pointer to member function in the type manager that does the
-/// actual lookup
-/// @param pointee_type_id the ID of the pointee type
-/// @param sc the storage class.  SC can be SpvStorageClass or spv::StorageClass
-/// @returns the ID for a SPIR-V pointer to pointee_type_id in storage class sc
-template <typename FinderType, typename SC>
-uint32_t FindPointerToType(spvtools::opt::analysis::TypeManager* type_manager,
-                           FinderType finder,
-                           uint32_t pointee_type_id,
-                           SC sc);
-
-template <>
-uint32_t FindPointerToType(spvtools::opt::analysis::TypeManager* type_mgr,
-                           PointerFinderSpvStorageClass finder,
-                           uint32_t pointee_type_id,
-                           SpvStorageClass sc) {
-    return (type_mgr->*finder)(pointee_type_id, sc);
-}
-
-template <>
-uint32_t FindPointerToType(spvtools::opt::analysis::TypeManager* type_mgr,
-                           PointerFinderSpvStorageClass finder,
-                           uint32_t pointee_type_id,
-                           spv::StorageClass sc) {
-    return (type_mgr->*finder)(pointee_type_id, static_cast<SpvStorageClass>(sc));
-}
-
-template <>
-uint32_t FindPointerToType(spvtools::opt::analysis::TypeManager* type_mgr,
-                           PointerFinderSpvStorageClassCpp11 finder,
-                           uint32_t pointee_type_id,
-                           SpvStorageClass sc) {
-    return (type_mgr->*finder)(pointee_type_id, static_cast<spv::StorageClass>(sc));
-}
-
-template <>
-uint32_t FindPointerToType(spvtools::opt::analysis::TypeManager* type_mgr,
-                           PointerFinderSpvStorageClassCpp11 finder,
-                           uint32_t pointee_type_id,
-                           spv::StorageClass sc) {
-    return (type_mgr->*finder)(pointee_type_id, sc);
-}
-}  // namespace three_sided_patch_function_cc
-
 namespace {
 
 constexpr uint32_t kMaxVectorLen = 4;
 
-template <typename FromOpcodeType>
-spv::Op ToOpcode(FromOpcodeType oc) {
-    return static_cast<spv::Op>(oc);
-}
-
 /// @param inst a SPIR-V instruction
 /// @returns Returns the opcode for an instruciton
 inline spv::Op opcode(const spvtools::opt::Instruction& inst) {
-    return ToOpcode(inst.opcode());
+    return inst.opcode();
 }
 /// @param inst a SPIR-V instruction pointer
 /// @returns Returns the opcode for an instruciton
 inline spv::Op opcode(const spvtools::opt::Instruction* inst) {
-    return ToOpcode(inst->opcode());
+    return inst->opcode();
 }
 
 // Gets the AST unary opcode for the given SPIR-V opcode, if any
@@ -727,7 +663,7 @@
         // Visit successors. We will naturally skip the continue target and merge
         // blocks.
         auto* terminator = bb->terminator();
-        auto opcode = ToOpcode(terminator->opcode());
+        const auto opcode = terminator->opcode();
         if (opcode == spv::Op::OpBranchConditional) {
             // Visit the false branch, then the true branch, to make them come
             // out in the natural order for an "if".
@@ -3522,7 +3458,7 @@
             const auto phi_id = assignment.phi_id;
             auto* const lhs_expr = builder_.Expr(namer_.Name(phi_id));
             // If RHS value is actually a phi we just cpatured, then use it.
-            auto* const copy_sym = copied_phis.Find(assignment.value_id);
+            auto copy_sym = copied_phis.Find(assignment.value_id);
             auto* const rhs_expr =
                 copy_sym ? builder_.Expr(*copy_sym) : MakeExpression(assignment.value_id).expr;
             AddStatement(builder_.Assign(lhs_expr, rhs_expr));
@@ -4611,9 +4547,7 @@
                        << ": " << pointee_type_inst->PrettyPrint();
                 return {};
         }
-        const auto pointer_type_id = three_sided_patch_function_cc::FindPointerToType(
-            type_mgr_, &spvtools::opt::analysis::TypeManager::FindPointerToType, pointee_type_id,
-            address_space);
+        const auto pointer_type_id = type_mgr_->FindPointerToType(pointee_type_id, address_space);
         auto* type = parser_impl_.ConvertType(pointer_type_id, PtrAs::Ref);
         TINT_ASSERT(Reader, type && type->Is<Reference>());
         current_expr = TypedExpression{type, next_expr};
diff --git a/src/tint/reader/spirv/parser_impl.cc b/src/tint/reader/spirv/parser_impl.cc
index 600e570..52bdb12 100644
--- a/src/tint/reader/spirv/parser_impl.cc
+++ b/src/tint/reader/spirv/parser_impl.cc
@@ -34,65 +34,6 @@
 
 namespace tint::reader::spirv {
 
-namespace three_sided_patch {
-// This machinery is only used while SPIRV-Tools is in transition before it fully
-// uses the C++11 header spirv.hpp11
-
-/// Typedef for pointer to member function while the API call uses
-/// SpvStorageClass for its second argument.
-typedef uint32_t (
-    spvtools::opt::analysis::TypeManager::*PointerFinderSpvStorageClass)(uint32_t, SpvStorageClass);
-/// Typedef for pointer to member function while the API call uses
-/// spv::StorageClass for its second argument.
-typedef uint32_t (spvtools::opt::analysis::TypeManager::*PointerFinderSpvStorageClassCpp11)(
-    uint32_t,
-    spv::StorageClass);
-
-/// @param type_manager the SPIRV-Tools optimizer's type manager
-/// @param finder a pointer to member function in the type manager that does the
-/// actual lookup
-/// @param pointee_type_id the ID of the pointee type
-/// @param sc the storage class.  SC can be SpvStorageClass or spv::StorageClass
-/// @returns the ID for a SPIR-V pointer to pointee_type_id in storage class sc
-template <typename FinderType, typename SC>
-uint32_t FindPointerToType(spvtools::opt::analysis::TypeManager* type_manager,
-                           FinderType finder,
-                           uint32_t pointee_type_id,
-                           SC sc);
-
-template <>
-uint32_t FindPointerToType(spvtools::opt::analysis::TypeManager* type_mgr,
-                           PointerFinderSpvStorageClass finder,
-                           uint32_t pointee_type_id,
-                           SpvStorageClass sc) {
-    return (type_mgr->*finder)(pointee_type_id, sc);
-}
-
-template <>
-uint32_t FindPointerToType(spvtools::opt::analysis::TypeManager* type_mgr,
-                           PointerFinderSpvStorageClass finder,
-                           uint32_t pointee_type_id,
-                           spv::StorageClass sc) {
-    return (type_mgr->*finder)(pointee_type_id, static_cast<SpvStorageClass>(sc));
-}
-
-template <>
-uint32_t FindPointerToType(spvtools::opt::analysis::TypeManager* type_mgr,
-                           PointerFinderSpvStorageClassCpp11 finder,
-                           uint32_t pointee_type_id,
-                           SpvStorageClass sc) {
-    return (type_mgr->*finder)(pointee_type_id, static_cast<spv::StorageClass>(sc));
-}
-
-template <>
-uint32_t FindPointerToType(spvtools::opt::analysis::TypeManager* type_mgr,
-                           PointerFinderSpvStorageClassCpp11 finder,
-                           uint32_t pointee_type_id,
-                           spv::StorageClass sc) {
-    return (type_mgr->*finder)(pointee_type_id, sc);
-}
-}  // namespace three_sided_patch
-
 namespace {
 
 // Input SPIR-V needs only to conform to Vulkan 1.1 requirements.
@@ -104,12 +45,12 @@
 /// @param inst a SPIR-V instruction
 /// @returns Returns the opcode for an instruciton
 inline spv::Op opcode(const spvtools::opt::Instruction& inst) {
-    return static_cast<spv::Op>(inst.opcode());
+    return inst.opcode();
 }
 /// @param inst a SPIR-V instruction pointer
 /// @returns Returns the opcode for an instruciton
 inline spv::Op opcode(const spvtools::opt::Instruction* inst) {
-    return static_cast<spv::Op>(inst->opcode());
+    return inst->opcode();
 }
 
 // A FunctionTraverser is used to compute an ordering of functions in the
@@ -1347,8 +1288,7 @@
     // Manufacture a type for the gl_Position variable if we have to.
     if ((builtin_position_.struct_type_id != 0) &&
         (builtin_position_.position_member_pointer_type_id == 0)) {
-        builtin_position_.position_member_pointer_type_id = three_sided_patch::FindPointerToType(
-            type_mgr_, &spvtools::opt::analysis::TypeManager::FindPointerToType,
+        builtin_position_.position_member_pointer_type_id = type_mgr_->FindPointerToType(
             builtin_position_.position_member_type_id, builtin_position_.storage_class);
         ConvertType(builtin_position_.position_member_pointer_type_id);
     }
diff --git a/src/tint/reader/spirv/parser_impl.h b/src/tint/reader/spirv/parser_impl.h
index 1780ca3..9ff9033 100644
--- a/src/tint/reader/spirv/parser_impl.h
+++ b/src/tint/reader/spirv/parser_impl.h
@@ -666,7 +666,7 @@
     /// @param id a SPIR-V ID
     /// @returns the AST variable or null.
     const ast::Var* GetModuleVariable(uint32_t id) {
-        auto* entry = module_variable_.Find(id);
+        auto entry = module_variable_.Find(id);
         return entry ? *entry : nullptr;
     }
 
diff --git a/src/tint/reader/wgsl/parser_impl.cc b/src/tint/reader/wgsl/parser_impl.cc
index 6314133..49e266f 100644
--- a/src/tint/reader/wgsl/parser_impl.cc
+++ b/src/tint/reader/wgsl/parser_impl.cc
@@ -87,24 +87,23 @@
            t == "goto" || t == "groupshared" || t == "handle" || t == "highp" || t == "impl" ||
            t == "implements" || t == "import" || t == "inline" || t == "inout" ||
            t == "instanceof" || t == "interface" || t == "invariant" || t == "layout" ||
-           t == "line" || t == "lineadj" || t == "lowp" || t == "macro" || t == "macro_rules" ||
-           t == "match" || t == "mediump" || t == "meta" || t == "mod" || t == "module" ||
-           t == "move" || t == "mut" || t == "mutable" || t == "namespace" || t == "new" ||
-           t == "nil" || t == "noexcept" || t == "noinline" || t == "nointerpolation" ||
-           t == "noperspective" || t == "null" || t == "nullptr" || t == "of" || t == "operator" ||
-           t == "package" || t == "packoffset" || t == "partition" || t == "pass" || t == "patch" ||
-           t == "pixelfragment" || t == "point" || t == "precise" || t == "precision" ||
-           t == "premerge" || t == "priv" || t == "protected" || t == "pub" || t == "public" ||
-           t == "readonly" || t == "ref" || t == "regardless" || t == "register" ||
-           t == "reinterpret_cast" || t == "requires" || t == "resource" || t == "restrict" ||
-           t == "self" || t == "set" || t == "shared" || t == "signed" || t == "sizeof" ||
-           t == "smooth" || t == "snorm" || t == "static" || t == "static_cast" || t == "std" ||
-           t == "subroutine" || t == "super" || t == "target" || t == "template" || t == "this" ||
-           t == "thread_local" || t == "throw" || t == "trait" || t == "try" || t == "typedef" ||
-           t == "typeid" || t == "typename" || t == "typeof" || t == "union" || t == "unless" ||
-           t == "unorm" || t == "unsafe" || t == "unsized" || t == "use" || t == "using" ||
-           t == "varying" || t == "virtual" || t == "volatile" || t == "wgsl" || t == "where" ||
-           t == "with" || t == "writeonly" || t == "yield";
+           t == "lowp" || t == "macro" || t == "macro_rules" || t == "match" || t == "mediump" ||
+           t == "meta" || t == "mod" || t == "module" || t == "move" || t == "mut" ||
+           t == "mutable" || t == "namespace" || t == "new" || t == "nil" || t == "noexcept" ||
+           t == "noinline" || t == "nointerpolation" || t == "noperspective" || t == "null" ||
+           t == "nullptr" || t == "of" || t == "operator" || t == "package" || t == "packoffset" ||
+           t == "partition" || t == "pass" || t == "patch" || t == "pixelfragment" ||
+           t == "precise" || t == "precision" || t == "premerge" || t == "priv" ||
+           t == "protected" || t == "pub" || t == "public" || t == "readonly" || t == "ref" ||
+           t == "regardless" || t == "register" || t == "reinterpret_cast" || t == "requires" ||
+           t == "resource" || t == "restrict" || t == "self" || t == "set" || t == "shared" ||
+           t == "signed" || t == "sizeof" || t == "smooth" || t == "snorm" || t == "static" ||
+           t == "static_cast" || t == "std" || t == "subroutine" || t == "super" || t == "target" ||
+           t == "template" || t == "this" || t == "thread_local" || t == "throw" || t == "trait" ||
+           t == "try" || t == "typedef" || t == "typeid" || t == "typename" || t == "typeof" ||
+           t == "union" || t == "unless" || t == "unorm" || t == "unsafe" || t == "unsized" ||
+           t == "use" || t == "using" || t == "varying" || t == "virtual" || t == "volatile" ||
+           t == "wgsl" || t == "where" || t == "with" || t == "writeonly" || t == "yield";
 }
 
 /// Enter-exit counters for block token types.
@@ -3228,47 +3227,46 @@
 }
 
 // lhs_expression
-//   : ( STAR | AND )* core_lhs_expression component_or_swizzle_specifier?
+//   : core_lhs_expression component_or_swizzle_specifier ?
+//   | AND lhs_expression
+//   | STAR lhs_expression
 Maybe<const ast::Expression*> ParserImpl::lhs_expression() {
-    std::vector<const Token*> prefixes;
-    while (peek_is(Token::Type::kStar) || peek_is(Token::Type::kAnd) ||
-           peek_is(Token::Type::kAndAnd)) {
-        auto& t = next();
-
-        // If an '&&' is provided split into '&' and '&'
-        if (t.Is(Token::Type::kAndAnd)) {
-            split_token(Token::Type::kAnd, Token::Type::kAnd);
-        }
-
-        prefixes.push_back(&t);
-    }
-
     auto core_expr = core_lhs_expression();
     if (core_expr.errored) {
         return Failure::kErrored;
-    } else if (!core_expr.matched) {
-        if (prefixes.empty()) {
-            return Failure::kNoMatch;
+    }
+    if (core_expr.matched) {
+        return component_or_swizzle_specifier(core_expr.value);
+    }
+
+    auto check_lhs = [&](ast::UnaryOp op) -> Maybe<const ast::Expression*> {
+        auto& t = peek();
+        auto expr = lhs_expression();
+        if (expr.errored) {
+            return Failure::kErrored;
         }
-
-        return add_error(peek(), "missing expression");
-    }
-
-    const auto* expr = core_expr.value;
-    for (auto it = prefixes.rbegin(); it != prefixes.rend(); ++it) {
-        auto& t = **it;
-        ast::UnaryOp op = ast::UnaryOp::kAddressOf;
-        if (t.Is(Token::Type::kStar)) {
-            op = ast::UnaryOp::kIndirection;
+        if (!expr.matched) {
+            return add_error(t, "missing expression");
         }
-        expr = create<ast::UnaryOpExpression>(t.source(), op, expr);
+        return create<ast::UnaryOpExpression>(t.source(), op, expr.value);
+    };
+
+    // If an `&&` is encountered, split it into two `&`'s
+    if (match(Token::Type::kAndAnd)) {
+        // The first `&` is consumed as part of the `&&`, so this needs to run the check itself.
+        split_token(Token::Type::kAnd, Token::Type::kAnd);
+        return check_lhs(ast::UnaryOp::kAddressOf);
     }
 
-    auto e = component_or_swizzle_specifier(expr);
-    if (e.errored) {
-        return Failure::kErrored;
+    if (match(Token::Type::kAnd)) {
+        return check_lhs(ast::UnaryOp::kAddressOf);
     }
-    return e.value;
+
+    if (match(Token::Type::kStar)) {
+        return check_lhs(ast::UnaryOp::kIndirection);
+    }
+
+    return Failure::kNoMatch;
 }
 
 // variable_updating_statement
diff --git a/src/tint/reader/wgsl/parser_impl_lhs_expression_test.cc b/src/tint/reader/wgsl/parser_impl_lhs_expression_test.cc
index 6611f3e..4e1e0db 100644
--- a/src/tint/reader/wgsl/parser_impl_lhs_expression_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_lhs_expression_test.cc
@@ -100,6 +100,31 @@
     EXPECT_TRUE(expr->Is<ast::IdentifierExpression>());
 }
 
+TEST_F(ParserImplTest, LHSExpression_PostfixExpression_Array) {
+    auto p = parser("*a[0]");
+    auto e = p->lhs_expression();
+    ASSERT_FALSE(p->has_error()) << p->error();
+    ASSERT_FALSE(e.errored);
+    EXPECT_TRUE(e.matched);
+    ASSERT_NE(e.value, nullptr);
+    ASSERT_TRUE(e->Is<ast::UnaryOpExpression>());
+
+    auto* u = e->As<ast::UnaryOpExpression>();
+    EXPECT_EQ(u->op, ast::UnaryOp::kIndirection);
+
+    ASSERT_TRUE(u->expr->Is<ast::IndexAccessorExpression>());
+
+    auto* access = u->expr->As<ast::IndexAccessorExpression>();
+    ASSERT_TRUE(access->object->Is<ast::IdentifierExpression>());
+
+    auto* obj = access->object->As<ast::IdentifierExpression>();
+    EXPECT_EQ(obj->symbol, p->builder().Symbols().Get("a"));
+
+    ASSERT_TRUE(access->index->Is<ast::IntLiteralExpression>());
+    auto* idx = access->index->As<ast::IntLiteralExpression>();
+    EXPECT_EQ(0, idx->value);
+}
+
 TEST_F(ParserImplTest, LHSExpression_PostfixExpression) {
     auto p = parser("*a.foo");
     auto e = p->lhs_expression();
@@ -107,16 +132,17 @@
     ASSERT_FALSE(e.errored);
     EXPECT_TRUE(e.matched);
     ASSERT_NE(e.value, nullptr);
-    ASSERT_TRUE(e->Is<ast::MemberAccessorExpression>());
+    ASSERT_TRUE(e->Is<ast::UnaryOpExpression>());
 
-    auto* access = e->As<ast::MemberAccessorExpression>();
-    ASSERT_TRUE(access->structure->Is<ast::UnaryOpExpression>());
-
-    auto* u = access->structure->As<ast::UnaryOpExpression>();
+    auto* u = e->As<ast::UnaryOpExpression>();
     EXPECT_EQ(u->op, ast::UnaryOp::kIndirection);
 
-    ASSERT_TRUE(u->expr->Is<ast::IdentifierExpression>());
-    auto* struct_ident = u->expr->As<ast::IdentifierExpression>();
+    ASSERT_TRUE(u->expr->Is<ast::MemberAccessorExpression>());
+
+    auto* access = u->expr->As<ast::MemberAccessorExpression>();
+    ASSERT_TRUE(access->structure->Is<ast::IdentifierExpression>());
+
+    auto* struct_ident = access->structure->As<ast::IdentifierExpression>();
     EXPECT_EQ(struct_ident->symbol, p->builder().Symbols().Get("a"));
 
     ASSERT_TRUE(access->member->Is<ast::IdentifierExpression>());
diff --git a/src/tint/reader/wgsl/parser_impl_reserved_keyword_test.cc b/src/tint/reader/wgsl/parser_impl_reserved_keyword_test.cc
index 8e178ab..f2adc06 100644
--- a/src/tint/reader/wgsl/parser_impl_reserved_keyword_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_reserved_keyword_test.cc
@@ -154,8 +154,6 @@
                                          "interface",
                                          "invariant",
                                          "layout",
-                                         "line",
-                                         "lineadj",
                                          "lowp",
                                          "macro",
                                          "macro_rules",
@@ -184,7 +182,6 @@
                                          "pass",
                                          "patch",
                                          "pixelfragment",
-                                         "point",
                                          "precise",
                                          "precision",
                                          "premerge",
diff --git a/src/tint/resolver/address_space_layout_validation_test.cc b/src/tint/resolver/address_space_layout_validation_test.cc
index 82da573..b34b889 100644
--- a/src/tint/resolver/address_space_layout_validation_test.cc
+++ b/src/tint/resolver/address_space_layout_validation_test.cc
@@ -363,6 +363,29 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
+// Make sure that this doesn't fail validation because vec3's align is 8, but
+// size is 6. 's' should be at offset 6, which is okay here.
+TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_Vec3F16MemberOffset_NoFail) {
+    // struct ScalarPackedAtEndOfVec3 {
+    //     v : vec3<f16>;
+    //     s : f16;
+    // };
+    // @group(0) @binding(0)
+    // var<uniform> a : ScalarPackedAtEndOfVec3;
+
+    Enable(ast::Extension::kF16);
+
+    Structure("ScalarPackedAtEndOfVec3", utils::Vector{
+                                             Member("v", ty.vec3(ty.f16())),
+                                             Member("s", ty.f16()),
+                                         });
+
+    GlobalVar(Source{{78, 90}}, "a", ty.type_name("ScalarPackedAtEndOfVec3"),
+              ast::AddressSpace::kUniform, Group(0_a), Binding(0_a));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
 // Detect array stride must be a multiple of 16 bytes for uniform buffers
 TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_InvalidArrayStride_Scalar) {
     // type Inner = array<f32, 10u>;
diff --git a/src/tint/resolver/address_space_validation_test.cc b/src/tint/resolver/address_space_validation_test.cc
index 3ef19a6..60e54df 100644
--- a/src/tint/resolver/address_space_validation_test.cc
+++ b/src/tint/resolver/address_space_validation_test.cc
@@ -113,98 +113,6 @@
 56:78 note: while instantiating 'var' g)");
 }
 
-// F16 types in storage and uniform buffer is not implemented yet.
-// TODO(tint:1473, tint:1502): make these testcases valid after f16 is supported.
-TEST_F(ResolverAddressSpaceValidationTest, StorageBufferF16_TemporallyBan) {
-    // var<storage> g : f16;
-    Enable(ast::Extension::kF16);
-
-    GlobalVar("g", ty.f16(Source{{56, 78}}), ast::AddressSpace::kStorage, Binding(0_a), Group(0_a));
-
-    ASSERT_FALSE(r()->Resolve());
-
-    EXPECT_EQ(r()->error(),
-              "56:78 error: using f16 types in 'storage' address space is not "
-              "implemented yet");
-}
-
-TEST_F(ResolverAddressSpaceValidationTest, StorageBufferF16Alias_TemporallyBan) {
-    // type a = f16;
-    // var<storage, read> g : a;
-    Enable(ast::Extension::kF16);
-
-    auto* a = Alias("a", ty.f16());
-    GlobalVar("g", ty.type_name(Source{{56, 78}}, a->name), ast::AddressSpace::kStorage,
-              Binding(0_a), Group(0_a));
-
-    ASSERT_FALSE(r()->Resolve());
-
-    EXPECT_EQ(r()->error(),
-              "56:78 error: using f16 types in 'storage' address space is not "
-              "implemented yet");
-}
-
-TEST_F(ResolverAddressSpaceValidationTest, StorageBufferVectorF16_TemporallyBan) {
-    // var<storage> g : vec4<f16>;
-    Enable(ast::Extension::kF16);
-    GlobalVar("g", ty.vec(Source{{56, 78}}, ty.Of<f16>(), 4u), ast::AddressSpace::kStorage,
-              Binding(0_a), Group(0_a));
-
-    ASSERT_FALSE(r()->Resolve());
-
-    EXPECT_EQ(r()->error(),
-              "56:78 error: using f16 types in 'storage' address space is not "
-              "implemented yet");
-}
-
-TEST_F(ResolverAddressSpaceValidationTest, StorageBufferArrayF16_TemporallyBan) {
-    // struct S { a : f16 };
-    // var<storage, read> g : array<S, 3u>;
-    Enable(ast::Extension::kF16);
-
-    auto* s = Structure("S", utils::Vector{Member("a", ty.f16(Source{{56, 78}}))});
-    auto* a = ty.array(ty.Of(s), 3_u);
-    GlobalVar("g", a, ast::AddressSpace::kStorage, ast::Access::kRead, Binding(0_a), Group(0_a));
-
-    ASSERT_FALSE(r()->Resolve());
-
-    EXPECT_THAT(r()->error(), HasSubstr("56:78 error: using f16 types in 'storage' address "
-                                        "space is not implemented yet"));
-}
-
-TEST_F(ResolverAddressSpaceValidationTest, StorageBufferStructF16_TemporallyBan) {
-    // struct S { x : f16 };
-    // var<storage, read> g : S;
-    Enable(ast::Extension::kF16);
-
-    auto* s = Structure("S", utils::Vector{Member("x", ty.f16(Source{{12, 34}}))});
-    GlobalVar("g", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(0_a),
-              Group(0_a));
-
-    ASSERT_FALSE(r()->Resolve());
-
-    EXPECT_THAT(r()->error(), HasSubstr("12:34 error: using f16 types in 'storage' address "
-                                        "space is not implemented yet"));
-}
-
-TEST_F(ResolverAddressSpaceValidationTest, StorageBufferNoErrorStructF16Aliases_TemporallyBan) {
-    // struct S { x : f16 };
-    // type a1 = S;
-    // var<storage, read> g : a1;
-    Enable(ast::Extension::kF16);
-
-    auto* s = Structure("S", utils::Vector{Member("x", ty.f16(Source{{12, 34}}))});
-    auto* a1 = Alias("a1", ty.Of(s));
-    auto* a2 = Alias("a2", ty.Of(a1));
-    GlobalVar("g", ty.Of(a2), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(0_a),
-              Group(0_a));
-
-    ASSERT_FALSE(r()->Resolve());
-
-    EXPECT_THAT(r()->error(), HasSubstr("12:34 error: using f16 types in 'storage' address "
-                                        "space is not implemented yet"));
-}
-
 TEST_F(ResolverAddressSpaceValidationTest, StorageBufferPointer) {
     // var<storage> g : ptr<private, f32>;
     GlobalVar(Source{{56, 78}}, "g", ty.pointer(ty.f32(), ast::AddressSpace::kPrivate),
@@ -226,6 +134,27 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
+TEST_F(ResolverAddressSpaceValidationTest, StorageBufferF16) {
+    // var<storage> g : f16;
+    Enable(ast::Extension::kF16);
+
+    GlobalVar("g", ty.f16(Source{{56, 78}}), ast::AddressSpace::kStorage, Binding(0_a), Group(0_a));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, StorageBufferF16Alias) {
+    // type a = f16;
+    // var<storage, read> g : a;
+    Enable(ast::Extension::kF16);
+
+    auto* a = Alias("a", ty.f16());
+    GlobalVar("g", ty.type_name(Source{{56, 78}}, a->name), ast::AddressSpace::kStorage,
+              Binding(0_a), Group(0_a));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
 TEST_F(ResolverAddressSpaceValidationTest, StorageBufferVectorF32) {
     // var<storage> g : vec4<f32>;
     GlobalVar(Source{{56, 78}}, "g", ty.vec4<f32>(), ast::AddressSpace::kStorage, Binding(0_a),
@@ -234,6 +163,15 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
+TEST_F(ResolverAddressSpaceValidationTest, StorageBufferVectorF16) {
+    // var<storage> g : vec4<f16>;
+    Enable(ast::Extension::kF16);
+    GlobalVar("g", ty.vec(Source{{56, 78}}, ty.Of<f16>(), 4u), ast::AddressSpace::kStorage,
+              Binding(0_a), Group(0_a));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
 TEST_F(ResolverAddressSpaceValidationTest, StorageBufferArrayF32) {
     // var<storage, read> g : array<S, 3u>;
     auto* s = Structure("S", utils::Vector{Member("a", ty.f32())});
@@ -244,6 +182,68 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
+TEST_F(ResolverAddressSpaceValidationTest, StorageBufferArrayF16) {
+    // var<storage, read> g : array<S, 3u>;
+    Enable(ast::Extension::kF16);
+
+    auto* s = Structure("S", utils::Vector{Member("a", ty.f16())});
+    auto* a = ty.array(ty.Of(s), 3_u);
+    GlobalVar(Source{{56, 78}}, "g", a, ast::AddressSpace::kStorage, ast::Access::kRead,
+              Binding(0_a), Group(0_a));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, StorageBufferStructI32) {
+    // struct S { x : i32 };
+    // var<storage, read> g : S;
+    auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "x", ty.i32())});
+    GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead,
+              Binding(0_a), Group(0_a));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, StorageBufferStructI32Aliases) {
+    // struct S { x : i32 };
+    // type a1 = S;
+    // var<storage, read> g : a1;
+    auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "x", ty.i32())});
+    auto* a1 = Alias("a1", ty.Of(s));
+    auto* a2 = Alias("a2", ty.Of(a1));
+    GlobalVar(Source{{56, 78}}, "g", ty.Of(a2), ast::AddressSpace::kStorage, ast::Access::kRead,
+              Binding(0_a), Group(0_a));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, StorageBufferStructF16) {
+    // struct S { x : f16 };
+    // var<storage, read> g : S;
+    Enable(ast::Extension::kF16);
+
+    auto* s = Structure("S", utils::Vector{Member("x", ty.f16(Source{{12, 34}}))});
+    GlobalVar("g", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(0_a),
+              Group(0_a));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, StorageBufferStructF16Aliases) {
+    // struct S { x : f16 };
+    // type a1 = S;
+    // var<storage, read> g : a1;
+    Enable(ast::Extension::kF16);
+
+    auto* s = Structure("S", utils::Vector{Member("x", ty.f16(Source{{12, 34}}))});
+    auto* a1 = Alias("a1", ty.Of(s));
+    auto* a2 = Alias("a2", ty.Of(a1));
+    GlobalVar("g", ty.Of(a2), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(0_a),
+              Group(0_a));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
 TEST_F(ResolverAddressSpaceValidationTest, NotStorage_AccessMode) {
     // var<private, read> g : a;
     GlobalVar(Source{{56, 78}}, "g", ty.i32(), ast::AddressSpace::kPrivate, ast::Access::kRead);
@@ -282,29 +282,6 @@
               R"(56:78 error: access mode 'write' is not valid for the 'storage' address space)");
 }
 
-TEST_F(ResolverAddressSpaceValidationTest, StorageBufferStructI32) {
-    // struct S { x : i32 };
-    // var<storage, read> g : S;
-    auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "x", ty.i32())});
-    GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead,
-              Binding(0_a), Group(0_a));
-
-    ASSERT_TRUE(r()->Resolve());
-}
-
-TEST_F(ResolverAddressSpaceValidationTest, StorageBufferNoErrorStructI32Aliases) {
-    // struct S { x : i32 };
-    // type a1 = S;
-    // var<storage, read> g : a1;
-    auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "x", ty.i32())});
-    auto* a1 = Alias("a1", ty.Of(s));
-    auto* a2 = Alias("a2", ty.Of(a1));
-    GlobalVar(Source{{56, 78}}, "g", ty.Of(a2), ast::AddressSpace::kStorage, ast::Access::kRead,
-              Binding(0_a), Group(0_a));
-
-    ASSERT_TRUE(r()->Resolve());
-}
-
 TEST_F(ResolverAddressSpaceValidationTest, UniformBuffer_Struct_Runtime) {
     // struct S { m:  array<f32>; };
     // @group(0) @binding(0) var<uniform, > svar : S;
@@ -349,97 +326,6 @@
 56:78 note: while instantiating 'var' g)");
 }
 
-// F16 types in storage and uniform buffer is not implemented yet.
-// TODO(tint:1473, tint:1502): make these testcases valid after f16 is supported.
-TEST_F(ResolverAddressSpaceValidationTest, UniformBufferF16_TemporallyBan) {
-    // var<uniform> g : f16;
-    Enable(ast::Extension::kF16);
-
-    GlobalVar("g", ty.f16(Source{{56, 78}}), ast::AddressSpace::kUniform, Binding(0_a), Group(0_a));
-
-    ASSERT_FALSE(r()->Resolve());
-
-    EXPECT_EQ(r()->error(),
-              "56:78 error: using f16 types in 'uniform' address space is not "
-              "implemented yet");
-}
-
-TEST_F(ResolverAddressSpaceValidationTest, UniformBufferF16Alias_TemporallyBan) {
-    // type a = f16;
-    // var<uniform> g : a;
-    Enable(ast::Extension::kF16);
-
-    auto* a = Alias("a", ty.f16());
-    GlobalVar("g", ty.type_name(Source{{56, 78}}, a->name), ast::AddressSpace::kUniform,
-              Binding(0_a), Group(0_a));
-
-    ASSERT_FALSE(r()->Resolve());
-
-    EXPECT_EQ(r()->error(),
-              "56:78 error: using f16 types in 'uniform' address space is not "
-              "implemented yet");
-}
-
-TEST_F(ResolverAddressSpaceValidationTest, UniformBufferVectorF16_TemporallyBan) {
-    // var<uniform> g : vec4<f16>;
-    Enable(ast::Extension::kF16);
-    GlobalVar("g", ty.vec(Source{{56, 78}}, ty.Of<f16>(), 4u), ast::AddressSpace::kUniform,
-              Binding(0_a), Group(0_a));
-
-    ASSERT_FALSE(r()->Resolve());
-
-    EXPECT_THAT(r()->error(), HasSubstr("56:78 error: using f16 types in 'uniform' address "
-                                        "space is not implemented yet"));
-}
-
-TEST_F(ResolverAddressSpaceValidationTest, UniformBufferArrayF16_TemporallyBan) {
-    // struct S {
-    //   @size(16) f : f16;
-    // }
-    // var<uniform> g : array<S, 3u>;
-    Enable(ast::Extension::kF16);
-
-    auto* s = Structure(
-        "S", utils::Vector{Member("a", ty.f16(Source{{56, 78}}), utils::Vector{MemberSize(16_a)})});
-    auto* a = ty.array(ty.Of(s), 3_u);
-    GlobalVar("g", a, ast::AddressSpace::kUniform, Binding(0_a), Group(0_a));
-
-    ASSERT_FALSE(r()->Resolve());
-
-    EXPECT_THAT(r()->error(), HasSubstr("56:78 error: using f16 types in 'uniform' address "
-                                        "space is not implemented yet"));
-}
-
-TEST_F(ResolverAddressSpaceValidationTest, UniformBufferStructF16_TemporallyBan) {
-    // struct S { x : f16 };
-    // var<uniform> g :  S;
-    Enable(ast::Extension::kF16);
-
-    auto* s = Structure("S", utils::Vector{Member("x", ty.f16(Source{{12, 34}}))});
-    GlobalVar("g", ty.Of(s), ast::AddressSpace::kUniform, Binding(0_a), Group(0_a));
-
-    ASSERT_FALSE(r()->Resolve());
-
-    EXPECT_THAT(r()->error(), HasSubstr("12:34 error: using f16 types in 'uniform' address "
-                                        "space is not implemented yet"));
-}
-
-TEST_F(ResolverAddressSpaceValidationTest, UniformBufferStructF16Aliases_TemporallyBan) {
-    // struct S { x : f16 };
-    // type a1 = S;
-    // var<uniform> g : a1;
-    Enable(ast::Extension::kF16);
-
-    auto* s = Structure("S", utils::Vector{Member("x", ty.f16(Source{{12, 34}}))});
-    auto* a1 = Alias("a1", ty.Of(s));
-    GlobalVar("g", ty.Of(a1), ast::AddressSpace::kUniform, Binding(0_a), Group(0_a));
-
-    ASSERT_FALSE(r()->Resolve());
-
-    EXPECT_THAT(r()->error(), HasSubstr("12:34 error: using f16 types in 'uniform' address "
-                                        "space is not implemented yet"));
-}
-
 TEST_F(ResolverAddressSpaceValidationTest, UniformBufferPointer) {
     // var<uniform> g : ptr<private, f32>;
     GlobalVar(Source{{56, 78}}, "g", ty.pointer(ty.f32(), ast::AddressSpace::kPrivate),
@@ -461,6 +347,16 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
+TEST_F(ResolverAddressSpaceValidationTest, UniformBufferF16) {
+    // var<uniform> g : f16;
+    Enable(ast::Extension::kF16);
+
+    GlobalVar(Source{{56, 78}}, "g", ty.f16(), ast::AddressSpace::kUniform, Binding(0_a),
+              Group(0_a));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
 TEST_F(ResolverAddressSpaceValidationTest, UniformBufferVectorF32) {
     // var<uniform> g : vec4<f32>;
     GlobalVar(Source{{56, 78}}, "g", ty.vec4<f32>(), ast::AddressSpace::kUniform, Binding(0_a),
@@ -469,6 +365,16 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
+TEST_F(ResolverAddressSpaceValidationTest, UniformBufferVectorF16) {
+    // var<uniform> g : vec4<f16>;
+    Enable(ast::Extension::kF16);
+
+    GlobalVar(Source{{56, 78}}, "g", ty.vec4<f16>(), ast::AddressSpace::kUniform, Binding(0_a),
+              Group(0_a));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
 TEST_F(ResolverAddressSpaceValidationTest, UniformBufferArrayF32) {
     // struct S {
     //   @size(16) f : f32;
@@ -481,6 +387,20 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
+TEST_F(ResolverAddressSpaceValidationTest, UniformBufferArrayF16) {
+    // struct S {
+    //   @size(16) f : f16;
+    // }
+    // var<uniform> g : array<S, 3u>;
+    Enable(ast::Extension::kF16);
+
+    auto* s = Structure("S", utils::Vector{Member("a", ty.f16(), utils::Vector{MemberSize(16_a)})});
+    auto* a = ty.array(ty.Of(s), 3_u);
+    GlobalVar(Source{{56, 78}}, "g", a, ast::AddressSpace::kUniform, Binding(0_a), Group(0_a));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
 TEST_F(ResolverAddressSpaceValidationTest, UniformBufferStructI32) {
     // struct S { x : i32 };
     // var<uniform> g :  S;
@@ -503,6 +423,32 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
+TEST_F(ResolverAddressSpaceValidationTest, UniformBufferStructF16) {
+    // struct S { x : f16 };
+    // var<uniform> g :  S;
+    Enable(ast::Extension::kF16);
+
+    auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "x", ty.f16())});
+    GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::AddressSpace::kUniform, Binding(0_a),
+              Group(0_a));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, UniformBufferStructF16Aliases) {
+    // struct S { x : f16 };
+    // type a1 = S;
+    // var<uniform> g : a1;
+    Enable(ast::Extension::kF16);
+
+    auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "x", ty.f16())});
+    auto* a1 = Alias("a1", ty.Of(s));
+    GlobalVar(Source{{56, 78}}, "g", ty.Of(a1), ast::AddressSpace::kUniform, Binding(0_a),
+              Group(0_a));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
 TEST_F(ResolverAddressSpaceValidationTest, PushConstantBool) {
     // enable chromium_experimental_push_constant;
     // var<push_constant> g : bool;
diff --git a/src/tint/resolver/builtin_test.cc b/src/tint/resolver/builtin_test.cc
index c6ca1f4..15bf5de 100644
--- a/src/tint/resolver/builtin_test.cc
+++ b/src/tint/resolver/builtin_test.cc
@@ -882,8 +882,8 @@
     EXPECT_EQ(r()->error(), R"(error: no matching call to distance(vec3<f32>, vec3<f32>, vec3<f32>)
 
 2 candidate functions:
-  distance(T, T) -> T  where: T is f32 or f16
-  distance(vecN<T>, vecN<T>) -> T  where: T is f32 or f16
+  distance(T, T) -> T  where: T is abstract-float, f32 or f16
+  distance(vecN<T>, vecN<T>) -> T  where: T is abstract-float, f32 or f16
 )");
 }
 
@@ -896,8 +896,8 @@
     EXPECT_EQ(r()->error(), R"(error: no matching call to distance(vec3<f32>)
 
 2 candidate functions:
-  distance(T, T) -> T  where: T is f32 or f16
-  distance(vecN<T>, vecN<T>) -> T  where: T is f32 or f16
+  distance(T, T) -> T  where: T is abstract-float, f32 or f16
+  distance(vecN<T>, vecN<T>) -> T  where: T is abstract-float, f32 or f16
 )");
 }
 
@@ -910,8 +910,8 @@
     EXPECT_EQ(r()->error(), R"(error: no matching call to distance()
 
 2 candidate functions:
-  distance(T, T) -> T  where: T is f32 or f16
-  distance(vecN<T>, vecN<T>) -> T  where: T is f32 or f16
+  distance(T, T) -> T  where: T is abstract-float, f32 or f16
+  distance(vecN<T>, vecN<T>) -> T  where: T is abstract-float, f32 or f16
 )");
 }
 
@@ -1079,54 +1079,8 @@
               R"(error: no matching call to frexp(i32, ptr<workgroup, i32, read_write>)
 
 2 candidate functions:
-  frexp(T) -> __frexp_result_T  where: T is f32 or f16
-  frexp(vecN<T>) -> __frexp_result_vecN_T  where: T is f32 or f16
-)");
-}
-
-TEST_F(ResolverBuiltinFloatTest, Frexp_Error_SecondParamFloatPtr) {
-    GlobalVar("v", ty.f32(), ast::AddressSpace::kWorkgroup);
-    auto* call = Call("frexp", 1_f, AddressOf("v"));
-    WrapInFunction(call);
-
-    EXPECT_FALSE(r()->Resolve());
-
-    EXPECT_EQ(r()->error(),
-              R"(error: no matching call to frexp(f32, ptr<workgroup, f32, read_write>)
-
-2 candidate functions:
-  frexp(T) -> __frexp_result_T  where: T is f32 or f16
-  frexp(vecN<T>) -> __frexp_result_vecN_T  where: T is f32 or f16
-)");
-}
-
-TEST_F(ResolverBuiltinFloatTest, Frexp_Error_SecondParamNotAPointer) {
-    auto* call = Call("frexp", 1_f, 1_i);
-    WrapInFunction(call);
-
-    EXPECT_FALSE(r()->Resolve());
-
-    EXPECT_EQ(r()->error(), R"(error: no matching call to frexp(f32, i32)
-
-2 candidate functions:
-  frexp(T) -> __frexp_result_T  where: T is f32 or f16
-  frexp(vecN<T>) -> __frexp_result_vecN_T  where: T is f32 or f16
-)");
-}
-
-TEST_F(ResolverBuiltinFloatTest, Frexp_Error_VectorSizesDontMatch) {
-    GlobalVar("v", ty.vec4<i32>(), ast::AddressSpace::kWorkgroup);
-    auto* call = Call("frexp", vec2<f32>(1_f, 2_f), AddressOf("v"));
-    WrapInFunction(call);
-
-    EXPECT_FALSE(r()->Resolve());
-
-    EXPECT_EQ(r()->error(),
-              R"(error: no matching call to frexp(vec2<f32>, ptr<workgroup, vec4<i32>, read_write>)
-
-2 candidate functions:
-  frexp(T) -> __frexp_result_T  where: T is f32 or f16
-  frexp(vecN<T>) -> __frexp_result_vecN_T  where: T is f32 or f16
+  frexp(T) -> __frexp_result_T  where: T is abstract-float, f32 or f16
+  frexp(vecN<T>) -> __frexp_result_vecN_T  where: T is abstract-float, f32 or f16
 )");
 }
 
@@ -1374,8 +1328,8 @@
               R"(error: no matching call to modf(i32, ptr<workgroup, f32, read_write>)
 
 2 candidate functions:
-  modf(T) -> __modf_result_T  where: T is f32 or f16
-  modf(vecN<T>) -> __modf_result_vecN_T  where: T is f32 or f16
+  modf(T) -> __modf_result_T  where: T is abstract-float, f32 or f16
+  modf(vecN<T>) -> __modf_result_vecN_T  where: T is abstract-float, f32 or f16
 )");
 }
 
@@ -1390,8 +1344,8 @@
               R"(error: no matching call to modf(f32, ptr<workgroup, i32, read_write>)
 
 2 candidate functions:
-  modf(T) -> __modf_result_T  where: T is f32 or f16
-  modf(vecN<T>) -> __modf_result_vecN_T  where: T is f32 or f16
+  modf(T) -> __modf_result_T  where: T is abstract-float, f32 or f16
+  modf(vecN<T>) -> __modf_result_vecN_T  where: T is abstract-float, f32 or f16
 )");
 }
 
@@ -1404,8 +1358,8 @@
     EXPECT_EQ(r()->error(), R"(error: no matching call to modf(f32, f32)
 
 2 candidate functions:
-  modf(T) -> __modf_result_T  where: T is f32 or f16
-  modf(vecN<T>) -> __modf_result_vecN_T  where: T is f32 or f16
+  modf(T) -> __modf_result_T  where: T is abstract-float, f32 or f16
+  modf(vecN<T>) -> __modf_result_vecN_T  where: T is abstract-float, f32 or f16
 )");
 }
 
@@ -1420,8 +1374,8 @@
               R"(error: no matching call to modf(vec2<f32>, ptr<workgroup, vec4<f32>, read_write>)
 
 2 candidate functions:
-  modf(T) -> __modf_result_T  where: T is f32 or f16
-  modf(vecN<T>) -> __modf_result_vecN_T  where: T is f32 or f16
+  modf(T) -> __modf_result_T  where: T is abstract-float, f32 or f16
+  modf(vecN<T>) -> __modf_result_vecN_T  where: T is abstract-float, f32 or f16
 )");
 }
 
@@ -1461,7 +1415,7 @@
     EXPECT_EQ(r()->error(), R"(error: no matching call to normalize()
 
 1 candidate function:
-  normalize(vecN<T>) -> vecN<T>  where: T is f32 or f16
+  normalize(vecN<T>) -> vecN<T>  where: T is abstract-float, f32 or f16
 )");
 }
 
@@ -1978,7 +1932,7 @@
     EXPECT_EQ(r()->error(), R"(error: no matching call to determinant(mat2x3<f32>)
 
 1 candidate function:
-  determinant(matNxN<T>) -> T  where: T is f32 or f16
+  determinant(matNxN<T>) -> T  where: T is abstract-float, f32 or f16
 )");
 }
 
@@ -1993,7 +1947,7 @@
     EXPECT_EQ(r()->error(), R"(error: no matching call to determinant(f32)
 
 1 candidate function:
-  determinant(matNxN<T>) -> T  where: T is f32 or f16
+  determinant(matNxN<T>) -> T  where: T is abstract-float, f32 or f16
 )");
 }
 
diff --git a/src/tint/resolver/builtins_validation_test.cc b/src/tint/resolver/builtins_validation_test.cc
index 675ff49..c0452ce 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(f32(i)));
+        params.Push(Expr(f32(i + 1)));
     }
     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>(f32(i), f32(i)));
+        params.Push(vec2<f32>(f32(i + 1), f32(i + 1)));
     }
     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>(f32(i), f32(i), f32(i)));
+        params.Push(vec3<f32>(f32(i + 1), f32(i + 1), f32(i + 1)));
     }
     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>(f32(i), f32(i), f32(i), f32(i)));
+        params.Push(vec4<f32>(f32(i + 1), f32(i + 1), f32(i + 1), f32(i + 1)));
     }
     auto* builtin = Call(name, params);
     Func("func", utils::Empty, ty.void_(),
diff --git a/src/tint/resolver/const_eval.cc b/src/tint/resolver/const_eval.cc
index c5b51c0..159aaf4 100644
--- a/src/tint/resolver/const_eval.cc
+++ b/src/tint/resolver/const_eval.cc
@@ -202,6 +202,15 @@
     return ss.str();
 }
 
+template <typename NumberT>
+std::string OverflowExpErrorMessage(std::string_view base, NumberT value) {
+    std::stringstream ss;
+    ss << std::setprecision(20);
+    ss << base << "^" << value << " cannot be represented as "
+       << "'" << FriendlyName<NumberT>() << "'";
+    return ss.str();
+}
+
 /// @returns the number of consecutive leading bits in `@p e` set to `@p bit_value_to_count`.
 template <typename T>
 std::make_unsigned_t<T> CountLeadingBits(T e, T bit_value_to_count) {
@@ -403,13 +412,26 @@
                        const sem::Type* target_ty,
                        const Source& source) const override {
         // Convert each of the composite element types.
-        auto* el_ty = sem::Type::ElementOf(target_ty);
         utils::Vector<const sem::Constant*, 4> conv_els;
         conv_els.Reserve(elements.Length());
+        std::function<const sem::Type*(size_t idx)> target_el_ty;
+        if (auto* str = target_ty->As<sem::Struct>()) {
+            if (str->Members().size() != elements.Length()) {
+                TINT_ICE(Resolver, builder.Diagnostics())
+                    << "const-eval conversion of structure has mismatched element counts";
+                return utils::Failure;
+            }
+            target_el_ty = [str](size_t idx) { return str->Members()[idx]->Type(); };
+        } else {
+            auto* el_ty = sem::Type::ElementOf(target_ty);
+            target_el_ty = [el_ty](size_t) { return el_ty; };
+        }
+
         for (auto* el : elements) {
-            // Note: This file is the only place where `sem::Constant`s are created, so this
+            // Note: This file is the only place where `sem::Constant`s are created, so the
             // static_cast is safe.
-            auto conv_el = static_cast<const ImplConstant*>(el)->Convert(builder, el_ty, source);
+            auto conv_el = static_cast<const ImplConstant*>(el)->Convert(
+                builder, target_el_ty(conv_els.Length()), source);
             if (!conv_el) {
                 return utils::Failure;
             }
@@ -439,6 +461,8 @@
 /// CreateElement constructs and returns an Element<T>.
 template <typename T>
 ImplResult CreateElement(ProgramBuilder& builder, const Source& source, const sem::Type* t, T v) {
+    TINT_ASSERT(Resolver, t->is_scalar());
+
     if constexpr (IsFloatingPoint<T>) {
         if (!std::isfinite(v.value)) {
             auto msg = OverflowErrorMessage(v, builder.FriendlyName(t));
@@ -630,8 +654,9 @@
                                    F&& f,
                                    const sem::Constant* c0,
                                    const sem::Constant* c1) {
-    uint32_t n0 = 0, n1 = 0;
+    uint32_t n0 = 0;
     sem::Type::ElementOf(c0->Type(), &n0);
+    uint32_t n1 = 0;
     sem::Type::ElementOf(c1->Type(), &n1);
     uint32_t max_n = std::max(n0, n1);
     // If arity of both constants is 1, invoke callback
@@ -642,7 +667,7 @@
     utils::Vector<const sem::Constant*, 8> els;
     els.Reserve(max_n);
     for (uint32_t i = 0; i < max_n; i++) {
-        auto nested_or_self = [&](auto& c, uint32_t num_elems) {
+        auto nested_or_self = [&](auto* c, uint32_t num_elems) {
             if (num_elems == 1) {
                 return c;
             }
@@ -871,15 +896,22 @@
 
 template <typename NumberT>
 utils::Result<NumberT> ConstEval::Det2(const Source& source,
-                                       NumberT a1,
-                                       NumberT a2,
-                                       NumberT b1,
-                                       NumberT b2) {
-    auto r1 = Mul(source, a1, b2);
+                                       NumberT a,
+                                       NumberT b,
+                                       NumberT c,
+                                       NumberT d) {
+    // | a c |
+    // | b d |
+    //
+    // =
+    //
+    // a * d - c * b
+
+    auto r1 = Mul(source, a, d);
     if (!r1) {
         return utils::Failure;
     }
-    auto r2 = Mul(source, b1, a2);
+    auto r2 = Mul(source, c, b);
     if (!r2) {
         return utils::Failure;
     }
@@ -891,6 +923,129 @@
 }
 
 template <typename NumberT>
+utils::Result<NumberT> ConstEval::Det3(const Source& source,
+                                       NumberT a,
+                                       NumberT b,
+                                       NumberT c,
+                                       NumberT d,
+                                       NumberT e,
+                                       NumberT f,
+                                       NumberT g,
+                                       NumberT h,
+                                       NumberT i) {
+    // | a d g |
+    // | b e h |
+    // | c f i |
+    //
+    // =
+    //
+    // a | e h | - d | b h | + g | b e |
+    //   | f i |     | c i |     | c f |
+
+    auto det1 = Det2(source, e, f, h, i);
+    if (!det1) {
+        return utils::Failure;
+    }
+    auto a_det1 = Mul(source, a, det1.Get());
+    if (!a_det1) {
+        return utils::Failure;
+    }
+    auto det2 = Det2(source, b, c, h, i);
+    if (!det2) {
+        return utils::Failure;
+    }
+    auto d_det2 = Mul(source, d, det2.Get());
+    if (!d_det2) {
+        return utils::Failure;
+    }
+    auto det3 = Det2(source, b, c, e, f);
+    if (!det3) {
+        return utils::Failure;
+    }
+    auto g_det3 = Mul(source, g, det3.Get());
+    if (!g_det3) {
+        return utils::Failure;
+    }
+    auto r = Sub(source, a_det1.Get(), d_det2.Get());
+    if (!r) {
+        return utils::Failure;
+    }
+    return Add(source, r.Get(), g_det3.Get());
+}
+
+template <typename NumberT>
+utils::Result<NumberT> ConstEval::Det4(const Source& source,
+                                       NumberT a,
+                                       NumberT b,
+                                       NumberT c,
+                                       NumberT d,
+                                       NumberT e,
+                                       NumberT f,
+                                       NumberT g,
+                                       NumberT h,
+                                       NumberT i,
+                                       NumberT j,
+                                       NumberT k,
+                                       NumberT l,
+                                       NumberT m,
+                                       NumberT n,
+                                       NumberT o,
+                                       NumberT p) {
+    // | a e i m |
+    // | b f j n |
+    // | c g k o |
+    // | d h l p |
+    //
+    // =
+    //
+    // a | f j n | - e | b j n | + i | b f n | - m | b f j |
+    //   | g k o |     | c k o |     | c g o |     | c g k |
+    //   | h l p |     | d l p |     | d h p |     | d h l |
+
+    auto det1 = Det3(source, f, g, h, j, k, l, n, o, p);
+    if (!det1) {
+        return utils::Failure;
+    }
+    auto a_det1 = Mul(source, a, det1.Get());
+    if (!a_det1) {
+        return utils::Failure;
+    }
+    auto det2 = Det3(source, b, c, d, j, k, l, n, o, p);
+    if (!det2) {
+        return utils::Failure;
+    }
+    auto e_det2 = Mul(source, e, det2.Get());
+    if (!e_det2) {
+        return utils::Failure;
+    }
+    auto det3 = Det3(source, b, c, d, f, g, h, n, o, p);
+    if (!det3) {
+        return utils::Failure;
+    }
+    auto i_det3 = Mul(source, i, det3.Get());
+    if (!i_det3) {
+        return utils::Failure;
+    }
+    auto det4 = Det3(source, b, c, d, f, g, h, j, k, l);
+    if (!det4) {
+        return utils::Failure;
+    }
+    auto m_det4 = Mul(source, m, det4.Get());
+    if (!m_det4) {
+        return utils::Failure;
+    }
+    auto r = Sub(source, a_det1.Get(), e_det2.Get());
+    if (!r) {
+        return utils::Failure;
+    }
+    r = Add(source, r.Get(), i_det3.Get());
+    if (!r) {
+        return utils::Failure;
+    }
+    return Sub(source, r.Get(), m_det4.Get());
+}
+
+template <typename NumberT>
 utils::Result<NumberT> ConstEval::Sqrt(const Source& source, NumberT v) {
     if (v < NumberT(0)) {
         AddError("sqrt must be called with a value >= 0", source);
@@ -1013,6 +1168,27 @@
     return utils::Failure;
 }
 
+ConstEval::Result ConstEval::Length(const Source& source,
+                                    const sem::Type* ty,
+                                    const sem::Constant* c0) {
+    auto* vec_ty = c0->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, c0);
+    }
+
+    // Evaluates to sqrt(e[0]^2 + e[1]^2 + ...) if T is a vector type.
+    auto d = Dot(source, c0, c0);
+    if (!d) {
+        return utils::Failure;
+    }
+    return Dispatch_fa_f32_f16(SqrtFunc(source, ty), d.Get());
+}
+
 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(source, a, b, c, d)) {
@@ -1022,6 +1198,26 @@
     };
 }
 
+auto ConstEval::Det3Func(const Source& source, const sem::Type* elem_ty) {
+    return
+        [=](auto a, auto b, auto c, auto d, auto e, auto f, auto g, auto h, auto i) -> ImplResult {
+            if (auto r = Det3(source, a, b, c, d, e, f, g, h, i)) {
+                return CreateElement(builder, source, elem_ty, r.Get());
+            }
+            return utils::Failure;
+        };
+}
+
+auto ConstEval::Det4Func(const Source& source, const sem::Type* elem_ty) {
+    return [=](auto a, auto b, auto c, auto d, auto e, auto f, auto g, auto h, auto i, auto j,
+               auto k, auto l, auto m, auto n, auto o, auto p) -> ImplResult {
+        if (auto r = Det4(source, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p)) {
+            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(
@@ -2014,6 +2210,62 @@
     return TransformElements(builder, ty, transform, args[0]);
 }
 
+ConstEval::Result ConstEval::determinant(const sem::Type* ty,
+                                         utils::VectorRef<const sem::Constant*> args,
+                                         const Source& source) {
+    auto calculate = [&]() -> ImplResult {
+        auto* m = args[0];
+        auto* mat_ty = m->Type()->As<sem::Matrix>();
+        auto me = [&](size_t r, size_t c) { return m->Index(c)->Index(r); };
+        switch (mat_ty->rows()) {
+            case 2:
+                return Dispatch_fa_f32_f16(Det2Func(source, ty),  //
+                                           me(0, 0), me(1, 0),    //
+                                           me(0, 1), me(1, 1));
+
+            case 3:
+                return Dispatch_fa_f32_f16(Det3Func(source, ty),          //
+                                           me(0, 0), me(1, 0), me(2, 0),  //
+                                           me(0, 1), me(1, 1), me(2, 1),  //
+                                           me(0, 2), me(1, 2), me(2, 2));
+
+            case 4:
+                return Dispatch_fa_f32_f16(Det4Func(source, ty),                    //
+                                           me(0, 0), me(1, 0), me(2, 0), me(3, 0),  //
+                                           me(0, 1), me(1, 1), me(2, 1), me(3, 1),  //
+                                           me(0, 2), me(1, 2), me(2, 2), me(3, 2),  //
+                                           me(0, 3), me(1, 3), me(2, 3), me(3, 3));
+        }
+        TINT_ICE(Resolver, builder.Diagnostics()) << "Unexpected number of matrix rows";
+        return utils::Failure;
+    };
+    auto r = calculate();
+    if (!r) {
+        AddNote("when calculating determinant", source);
+    }
+    return r;
+}
+
+ConstEval::Result ConstEval::distance(const sem::Type* ty,
+                                      utils::VectorRef<const sem::Constant*> args,
+                                      const Source& source) {
+    auto err = [&]() -> ImplResult {
+        AddNote("when calculating distance", source);
+        return utils::Failure;
+    };
+
+    auto minus = OpMinus(args[0]->Type(), args, source);
+    if (!minus) {
+        return err();
+    }
+
+    auto len = Length(source, ty, minus.Get());
+    if (!len) {
+        return err();
+    }
+    return len;
+}
+
 ConstEval::Result ConstEval::dot(const sem::Type*,
                                  utils::VectorRef<const sem::Constant*> args,
                                  const Source& source) {
@@ -2024,6 +2276,42 @@
     return r;
 }
 
+ConstEval::Result ConstEval::exp(const sem::Type* ty,
+                                 utils::VectorRef<const sem::Constant*> args,
+                                 const Source& source) {
+    auto transform = [&](const sem::Constant* c0) {
+        auto create = [&](auto e0) -> ImplResult {
+            using NumberT = decltype(e0);
+            auto val = NumberT(std::exp(e0));
+            if (!std::isfinite(val.value)) {
+                AddError(OverflowExpErrorMessage("e", e0), source);
+                return utils::Failure;
+            }
+            return CreateElement(builder, source, c0->Type(), val);
+        };
+        return Dispatch_fa_f32_f16(create, c0);
+    };
+    return TransformElements(builder, ty, transform, args[0]);
+}
+
+ConstEval::Result ConstEval::exp2(const sem::Type* ty,
+                                  utils::VectorRef<const sem::Constant*> args,
+                                  const Source& source) {
+    auto transform = [&](const sem::Constant* c0) {
+        auto create = [&](auto e0) -> ImplResult {
+            using NumberT = decltype(e0);
+            auto val = NumberT(std::exp2(e0));
+            if (!std::isfinite(val.value)) {
+                AddError(OverflowExpErrorMessage("2", e0), source);
+                return utils::Failure;
+            }
+            return CreateElement(builder, source, c0->Type(), val);
+        };
+        return Dispatch_fa_f32_f16(create, c0);
+    };
+    return TransformElements(builder, ty, transform, args[0]);
+}
+
 ConstEval::Result ConstEval::extractBits(const sem::Type* ty,
                                          utils::VectorRef<const sem::Constant*> args,
                                          const Source& source) {
@@ -2161,6 +2449,106 @@
     return TransformElements(builder, ty, transform, args[0]);
 }
 
+ConstEval::Result ConstEval::fma(const sem::Type* ty,
+                                 utils::VectorRef<const sem::Constant*> args,
+                                 const Source& source) {
+    auto transform = [&](const sem::Constant* c1, const sem::Constant* c2,
+                         const sem::Constant* c3) {
+        auto create = [&](auto e1, auto e2, auto e3) -> ImplResult {
+            auto err_msg = [&] {
+                AddNote("when calculating fma", source);
+                return utils::Failure;
+            };
+
+            auto mul = Mul(source, e1, e2);
+            if (!mul) {
+                return err_msg();
+            }
+
+            auto val = Add(source, mul.Get(), e3);
+            if (!val) {
+                return err_msg();
+            }
+            return CreateElement(builder, source, c1->Type(), val.Get());
+        };
+        return Dispatch_fa_f32_f16(create, c1, c2, c3);
+    };
+    return TransformElements(builder, ty, transform, args[0], args[1], args[2]);
+}
+
+ConstEval::Result ConstEval::frexp(const sem::Type* ty,
+                                   utils::VectorRef<const sem::Constant*> args,
+                                   const Source& source) {
+    auto* arg = args[0];
+
+    struct FractExp {
+        ImplResult fract;
+        ImplResult exp;
+    };
+
+    auto scalar = [&](const sem::Constant* s) {
+        int exp = 0;
+        double fract = std::frexp(s->As<AFloat>(), &exp);
+        return Switch(
+            s->Type(),
+            [&](const sem::F32*) {
+                return FractExp{
+                    CreateElement(builder, source, builder.create<sem::F32>(), f32(fract)),
+                    CreateElement(builder, source, builder.create<sem::I32>(), i32(exp)),
+                };
+            },
+            [&](const sem::F16*) {
+                return FractExp{
+                    CreateElement(builder, source, builder.create<sem::F16>(), f16(fract)),
+                    CreateElement(builder, source, builder.create<sem::I32>(), i32(exp)),
+                };
+            },
+            [&](const sem::AbstractFloat*) {
+                return FractExp{
+                    CreateElement(builder, source, builder.create<sem::AbstractFloat>(),
+                                  AFloat(fract)),
+                    CreateElement(builder, source, builder.create<sem::AbstractInt>(), AInt(exp)),
+                };
+            },
+            [&](Default) {
+                TINT_ICE(Resolver, builder.Diagnostics())
+                    << "unhandled element type for frexp() const-eval: "
+                    << builder.FriendlyName(s->Type());
+                return FractExp{utils::Failure, utils::Failure};
+            });
+    };
+
+    if (auto* vec = arg->Type()->As<sem::Vector>()) {
+        utils::Vector<const sem::Constant*, 4> fract_els;
+        utils::Vector<const sem::Constant*, 4> exp_els;
+        for (uint32_t i = 0; i < vec->Width(); i++) {
+            auto fe = scalar(arg->Index(i));
+            if (!fe.fract || !fe.exp) {
+                return utils::Failure;
+            }
+            fract_els.Push(fe.fract.Get());
+            exp_els.Push(fe.exp.Get());
+        }
+        auto fract_ty = builder.create<sem::Vector>(fract_els[0]->Type(), vec->Width());
+        auto exp_ty = builder.create<sem::Vector>(exp_els[0]->Type(), vec->Width());
+        return CreateComposite(builder, ty,
+                               utils::Vector<const sem::Constant*, 2>{
+                                   CreateComposite(builder, fract_ty, std::move(fract_els)),
+                                   CreateComposite(builder, exp_ty, std::move(exp_els)),
+                               });
+    } else {
+        auto fe = scalar(arg);
+        if (!fe.fract || !fe.exp) {
+            return utils::Failure;
+        }
+        return CreateComposite(builder, ty,
+                               utils::Vector<const sem::Constant*, 2>{
+                                   fe.fract.Get(),
+                                   fe.exp.Get(),
+                               });
+    }
+}
+
 ConstEval::Result ConstEval::insertBits(const sem::Type* ty,
                                         utils::VectorRef<const sem::Constant*> args,
                                         const Source& source) {
@@ -2213,35 +2601,84 @@
     return TransformElements(builder, ty, transform, args[0], args[1]);
 }
 
+ConstEval::Result ConstEval::inverseSqrt(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);
+
+            if (e <= NumberT(0)) {
+                AddError("inverseSqrt must be called with a value > 0", source);
+                return utils::Failure;
+            }
+
+            auto err = [&] {
+                AddNote("when calculating inverseSqrt", source);
+                return utils::Failure;
+            };
+
+            auto s = Sqrt(source, e);
+            if (!s) {
+                return err();
+            }
+            auto div = Div(source, NumberT(1), s.Get());
+            if (!div) {
+                return err();
+            }
+
+            return CreateElement(builder, source, c0->Type(), div.Get());
+        };
+        return Dispatch_fa_f32_f16(create, c0);
+    };
+
+    return TransformElements(builder, ty, transform, args[0]);
+}
+
 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();
+    auto r = Length(source, ty, args[0]);
     if (!r) {
         AddNote("when calculating length", source);
     }
     return r;
 }
 
+ConstEval::Result ConstEval::log(const sem::Type* ty,
+                                 utils::VectorRef<const sem::Constant*> args,
+                                 const Source& source) {
+    auto transform = [&](const sem::Constant* c0) {
+        auto create = [&](auto v) -> ImplResult {
+            using NumberT = decltype(v);
+            if (v <= NumberT(0)) {
+                AddError("log must be called with a value > 0", source);
+                return utils::Failure;
+            }
+            return CreateElement(builder, source, c0->Type(), NumberT(std::log(v)));
+        };
+        return Dispatch_fa_f32_f16(create, c0);
+    };
+    return TransformElements(builder, ty, transform, args[0]);
+}
+
+ConstEval::Result ConstEval::log2(const sem::Type* ty,
+                                  utils::VectorRef<const sem::Constant*> args,
+                                  const Source& source) {
+    auto transform = [&](const sem::Constant* c0) {
+        auto create = [&](auto v) -> ImplResult {
+            using NumberT = decltype(v);
+            if (v <= NumberT(0)) {
+                AddError("log2 must be called with a value > 0", source);
+                return utils::Failure;
+            }
+            return CreateElement(builder, source, c0->Type(), NumberT(std::log2(v)));
+        };
+        return Dispatch_fa_f32_f16(create, c0);
+    };
+    return TransformElements(builder, ty, transform, args[0]);
+}
+
 ConstEval::Result ConstEval::max(const sem::Type* ty,
                                  utils::VectorRef<const sem::Constant*> args,
                                  const Source& source) {
@@ -2300,6 +2737,23 @@
     return CreateComposite(builder, ty, std::move(fields));
 }
 
+ConstEval::Result ConstEval::normalize(const sem::Type* ty,
+                                       utils::VectorRef<const sem::Constant*> args,
+                                       const Source& source) {
+    auto* len_ty = sem::Type::DeepestElementOf(ty);
+    auto len = Length(source, len_ty, args[0]);
+    if (!len) {
+        AddNote("when calculating normalize", source);
+        return utils::Failure;
+    }
+    auto* v = len.Get();
+    if (v->AllZero()) {
+        AddError("zero length vector can not be normalized", source);
+        return utils::Failure;
+    }
+    return OpDivide(ty, utils::Vector{args[0], v}, source);
+}
+
 ConstEval::Result ConstEval::pack2x16float(const sem::Type* ty,
                                            utils::VectorRef<const sem::Constant*> args,
                                            const Source& source) {
@@ -2681,6 +3135,26 @@
     return TransformElements(builder, ty, transform, args[0]);
 }
 
+ConstEval::Result ConstEval::transpose(const sem::Type* ty,
+                                       utils::VectorRef<const sem::Constant*> args,
+                                       const Source&) {
+    auto* m = args[0];
+    auto* mat_ty = m->Type()->As<sem::Matrix>();
+    auto me = [&](size_t r, size_t c) { return m->Index(c)->Index(r); };
+    auto* result_mat_ty = ty->As<sem::Matrix>();
+
+    // Produce column vectors from each row
+    utils::Vector<const sem::Constant*, 4> result_mat;
+    for (size_t r = 0; r < mat_ty->rows(); ++r) {
+        utils::Vector<const sem::Constant*, 4> new_col_vec;
+        for (size_t c = 0; c < mat_ty->columns(); ++c) {
+            new_col_vec.Push(me(r, c));
+        }
+        result_mat.Push(CreateComposite(builder, result_mat_ty->ColumnType(), new_col_vec));
+    }
+    return CreateComposite(builder, ty, result_mat);
+}
+
 ConstEval::Result ConstEval::trunc(const sem::Type* ty,
                                    utils::VectorRef<const sem::Constant*> args,
                                    const Source& source) {
diff --git a/src/tint/resolver/const_eval.h b/src/tint/resolver/const_eval.h
index f290adb..0c8ec6b 100644
--- a/src/tint/resolver/const_eval.h
+++ b/src/tint/resolver/const_eval.h
@@ -539,6 +539,7 @@
                  utils::VectorRef<const sem::Constant*> args,
                  const Source& source);
 
+    /// degrees builtin
     /// @param ty the expression type
     /// @param args the input arguments
     /// @param source the source location of the conversion
@@ -547,6 +548,24 @@
                    utils::VectorRef<const sem::Constant*> args,
                    const Source& source);
 
+    /// determinant 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 determinant(const sem::Type* ty,
+                       utils::VectorRef<const sem::Constant*> args,
+                       const Source& source);
+
+    /// distance 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 distance(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
@@ -556,6 +575,24 @@
                utils::VectorRef<const sem::Constant*> args,
                const Source& source);
 
+    /// exp 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 exp(const sem::Type* ty,
+               utils::VectorRef<const sem::Constant*> args,
+               const Source& source);
+
+    /// exp2 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 exp2(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
@@ -592,6 +629,24 @@
                  utils::VectorRef<const sem::Constant*> args,
                  const Source& source);
 
+    /// fma 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 fma(const sem::Type* ty,
+               utils::VectorRef<const sem::Constant*> args,
+               const Source& source);
+
+    /// frexp 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 frexp(const sem::Type* ty,
+                 utils::VectorRef<const sem::Constant*> args,
+                 const Source& source);
+
     /// insertBits builtin
     /// @param ty the expression type
     /// @param args the input arguments
@@ -601,6 +656,15 @@
                       utils::VectorRef<const sem::Constant*> args,
                       const Source& source);
 
+    /// inverseSqrt 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 inverseSqrt(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
@@ -610,6 +674,24 @@
                   utils::VectorRef<const sem::Constant*> args,
                   const Source& source);
 
+    /// log 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 log(const sem::Type* ty,
+               utils::VectorRef<const sem::Constant*> args,
+               const Source& source);
+
+    /// log2 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 log2(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
@@ -637,6 +719,15 @@
                 utils::VectorRef<const sem::Constant*> args,
                 const Source& source);
 
+    /// normalize 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 normalize(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
@@ -808,6 +899,15 @@
                 utils::VectorRef<const sem::Constant*> args,
                 const Source& source);
 
+    /// transpose 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 transpose(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
@@ -967,18 +1067,87 @@
                                 NumberT b3,
                                 NumberT b4);
 
-    /// Returns the determinant of the 2x2 matrix [(a1, a2), (b1, b2)]
+    /// Returns the determinant of the 2x2 matrix:
+    /// | a c |
+    /// | b d |
     /// @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
+    /// @param a component 1 of the first column vector
+    /// @param b component 2 of the first column vector
+    /// @param c component 1 of the second column vector
+    /// @param d component 2 of the second column vector
     template <typename NumberT>
-    utils::Result<NumberT> Det2(const Source& source,
-                                NumberT a1,
-                                NumberT a2,
-                                NumberT b1,
-                                NumberT b2);
+    utils::Result<NumberT> Det2(const Source& source,  //
+                                NumberT a,
+                                NumberT b,
+                                NumberT c,
+                                NumberT d);
+
+    /// Returns the determinant of the 3x3 matrix:
+    /// | a d g |
+    /// | b e h |
+    /// | c f i |
+    /// @param source the source location
+    /// @param a component 1 of the first column vector
+    /// @param b component 2 of the first column vector
+    /// @param c component 3 of the first column vector
+    /// @param d component 1 of the second column vector
+    /// @param e component 2 of the second column vector
+    /// @param f component 3 of the second column vector
+    /// @param g component 1 of the third column vector
+    /// @param h component 2 of the third column vector
+    /// @param i component 3 of the third column vector
+    template <typename NumberT>
+    utils::Result<NumberT> Det3(const Source& source,
+                                NumberT a,
+                                NumberT b,
+                                NumberT c,
+                                NumberT d,
+                                NumberT e,
+                                NumberT f,
+                                NumberT g,
+                                NumberT h,
+                                NumberT i);
+
+    /// Returns the determinant of the 4x4 matrix:
+    /// | a e i m |
+    /// | b f j n |
+    /// | c g k o |
+    /// | d h l p |
+    /// @param source the source location
+    /// @param a component 1 of the first column vector
+    /// @param b component 2 of the first column vector
+    /// @param c component 3 of the first column vector
+    /// @param d component 4 of the first column vector
+    /// @param e component 1 of the second column vector
+    /// @param f component 2 of the second column vector
+    /// @param g component 3 of the second column vector
+    /// @param h component 4 of the second column vector
+    /// @param i component 1 of the third column vector
+    /// @param j component 2 of the third column vector
+    /// @param k component 3 of the third column vector
+    /// @param l component 4 of the third column vector
+    /// @param m component 1 of the fourth column vector
+    /// @param n component 2 of the fourth column vector
+    /// @param o component 3 of the fourth column vector
+    /// @param p component 4 of the fourth column vector
+    template <typename NumberT>
+    utils::Result<NumberT> Det4(const Source& source,
+                                NumberT a,
+                                NumberT b,
+                                NumberT c,
+                                NumberT d,
+                                NumberT e,
+                                NumberT f,
+                                NumberT g,
+                                NumberT h,
+                                NumberT i,
+                                NumberT j,
+                                NumberT k,
+                                NumberT l,
+                                NumberT m,
+                                NumberT n,
+                                NumberT o,
+                                NumberT p);
 
     template <typename NumberT>
     utils::Result<NumberT> Sqrt(const Source& source, NumberT v);
@@ -1048,6 +1217,20 @@
     /// @returns the callable function
     auto Det2Func(const Source& source, const sem::Type* elem_ty);
 
+    /// Returns a callable that calls Det3, 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 Det3Func(const Source& source, const sem::Type* elem_ty);
+
+    /// Returns a callable that calls Det4, 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 Det4Func(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
@@ -1069,6 +1252,13 @@
     /// @returns the dot product
     Result Dot(const Source& source, const sem::Constant* v1, const sem::Constant* v2);
 
+    /// Return sthe length of c0
+    /// @param source the source location
+    /// @param ty the return type
+    /// @param c0 the constant to calculate the length of
+    /// @returns the length of c0
+    Result Length(const Source& source, const sem::Type* ty, const sem::Constant* c0);
+
     ProgramBuilder& builder;
 };
 
diff --git a/src/tint/resolver/const_eval_binary_op_test.cc b/src/tint/resolver/const_eval_binary_op_test.cc
index 70176c7..a35214a 100644
--- a/src/tint/resolver/const_eval_binary_op_test.cc
+++ b/src/tint/resolver/const_eval_binary_op_test.cc
@@ -22,30 +22,26 @@
 namespace tint::resolver {
 namespace {
 
-// Bring in std::ostream& operator<<(std::ostream& o, const Types& types)
-using resolver::operator<<;
-
 struct Case {
     struct Success {
-        Types value;
+        Value value;
     };
     struct Failure {
         std::string error;
     };
 
-    Types lhs;
-    Types rhs;
+    Value lhs;
+    Value rhs;
     utils::Result<Success, Failure> expected;
 };
 
 struct ErrorCase {
-    Types lhs;
-    Types rhs;
+    Value lhs;
+    Value rhs;
 };
 
 /// 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) {
+Case C(Value lhs, Value rhs, Value expected) {
     return Case{std::move(lhs), std::move(rhs), Case::Success{std::move(expected)}};
 }
 
@@ -56,8 +52,7 @@
 }
 
 /// 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) {
+Case E(Value lhs, Value rhs, std::string error) {
     return Case{std::move(lhs), std::move(rhs), Case::Failure{std::move(error)}};
 }
 
@@ -71,7 +66,7 @@
 static std::ostream& operator<<(std::ostream& o, const Case& c) {
     o << "lhs: " << c.lhs << ", rhs: " << c.rhs << ", expected: ";
     if (c.expected) {
-        auto s = c.expected.Get();
+        auto& s = c.expected.Get();
         o << s.value;
     } else {
         o << "[ERROR: " << c.expected.Failure().error << "]";
@@ -91,15 +86,16 @@
     auto op = std::get<0>(GetParam());
     auto& c = std::get<1>(GetParam());
 
-    auto* lhs_expr = ToValueBase(c.lhs)->Expr(*this);
-    auto* rhs_expr = ToValueBase(c.rhs)->Expr(*this);
+    auto* lhs_expr = c.lhs.Expr(*this);
+    auto* rhs_expr = c.rhs.Expr(*this);
+
     auto* expr = create<ast::BinaryExpression>(Source{{12, 34}}, op, lhs_expr, rhs_expr);
     GlobalConst("C", expr);
 
     if (c.expected) {
         ASSERT_TRUE(r()->Resolve()) << r()->error();
         auto expected_case = c.expected.Get();
-        auto* expected = ToValueBase(expected_case.value);
+        auto& expected = expected_case.value;
 
         auto* sem = Sem().Get(expr);
         const sem::Constant* value = sem->ConstantValue();
@@ -707,7 +703,6 @@
                                                       OpOrIntCases<u32>()))));
 
 TEST_F(ResolverConstEvalTest, NotAndOrOfVecs) {
-    // const C = !((vec2(true, true) & vec2(true, false)) | vec2(false, true));
     auto v1 = Vec(true, true).Expr(*this);
     auto v2 = Vec(true, false).Expr(*this);
     auto v3 = Vec(false, true).Expr(*this);
@@ -978,8 +973,8 @@
 // i32/u32 left shift by >= 32 -> error
 using ResolverConstEvalShiftLeftConcreteGeqBitWidthError = ResolverTestWithParam<ErrorCase>;
 TEST_P(ResolverConstEvalShiftLeftConcreteGeqBitWidthError, Test) {
-    auto* lhs_expr = ToValueBase(GetParam().lhs)->Expr(*this);
-    auto* rhs_expr = ToValueBase(GetParam().rhs)->Expr(*this);
+    auto* lhs_expr = GetParam().lhs.Expr(*this);
+    auto* rhs_expr = GetParam().rhs.Expr(*this);
     GlobalConst("c", Shl(Source{{1, 1}}, lhs_expr, rhs_expr));
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(
@@ -1024,8 +1019,8 @@
 // AInt left shift results in sign change error
 using ResolverConstEvalShiftLeftSignChangeError = ResolverTestWithParam<ErrorCase>;
 TEST_P(ResolverConstEvalShiftLeftSignChangeError, Test) {
-    auto* lhs_expr = ToValueBase(GetParam().lhs)->Expr(*this);
-    auto* rhs_expr = ToValueBase(GetParam().rhs)->Expr(*this);
+    auto* lhs_expr = GetParam().lhs.Expr(*this);
+    auto* rhs_expr = GetParam().rhs.Expr(*this);
     GlobalConst("c", Shl(Source{{1, 1}}, lhs_expr, rhs_expr));
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "1:1 error: shift left operation results in sign change");
diff --git a/src/tint/resolver/const_eval_builtin_test.cc b/src/tint/resolver/const_eval_builtin_test.cc
index eccd711..dabe7d9 100644
--- a/src/tint/resolver/const_eval_builtin_test.cc
+++ b/src/tint/resolver/const_eval_builtin_test.cc
@@ -22,15 +22,12 @@
 namespace tint::resolver {
 namespace {
 
-// Bring in std::ostream& operator<<(std::ostream& o, const Types& types)
-using resolver::operator<<;
-
 struct Case {
-    Case(utils::VectorRef<Types> in_args, utils::VectorRef<Types> expected_values)
+    Case(utils::VectorRef<Value> in_args, utils::VectorRef<Value> expected_values)
         : args(std::move(in_args)),
           expected(Success{std::move(expected_values), CheckConstantFlags{}}) {}
 
-    Case(utils::VectorRef<Types> in_args, std::string expected_err)
+    Case(utils::VectorRef<Value> in_args, std::string expected_err)
         : args(std::move(in_args)), expected(Failure{std::move(expected_err)}) {}
 
     /// Expected value may be positive or negative
@@ -52,14 +49,14 @@
     }
 
     struct Success {
-        utils::Vector<Types, 2> values;
+        utils::Vector<Value, 2> values;
         CheckConstantFlags flags;
     };
     struct Failure {
         std::string error;
     };
 
-    utils::Vector<Types, 8> args;
+    utils::Vector<Value, 8> args;
     utils::Result<Success, Failure> expected;
 };
 
@@ -94,34 +91,34 @@
 using ScalarTypes = std::variant<AInt, AFloat, u32, i32, f32, f16>;
 
 /// Creates a Case with Values for args and result
-static Case C(std::initializer_list<Types> args, Types result) {
-    return Case{utils::Vector<Types, 8>{args}, utils::Vector<Types, 2>{std::move(result)}};
+static Case C(std::initializer_list<Value> args, Value result) {
+    return Case{utils::Vector<Value, 8>{args}, utils::Vector<Value, 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}};
+static Case C(std::initializer_list<Value> args, std::initializer_list<Value> results) {
+    return Case{utils::Vector<Value, 8>{args}, utils::Vector<Value, 2>{results}};
 }
 
 /// Convenience overload that creates a Case with just scalars
 static Case C(std::initializer_list<ScalarTypes> sargs, ScalarTypes sresult) {
-    utils::Vector<Types, 8> args;
+    utils::Vector<Value, 8> args;
     for (auto& sa : sargs) {
         std::visit([&](auto&& v) { return args.Push(Val(v)); }, sa);
     }
-    Types result = Val(0_a);
+    Value result = Val(0_a);
     std::visit([&](auto&& v) { result = Val(v); }, sresult);
-    return Case{std::move(args), utils::Vector<Types, 2>{std::move(result)}};
+    return Case{std::move(args), utils::Vector<Value, 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;
+    utils::Vector<Value, 8> args;
     for (auto& sa : sargs) {
         std::visit([&](auto&& v) { return args.Push(Val(v)); }, sa);
     }
-    utils::Vector<Types, 2> results;
+    utils::Vector<Value, 2> results;
     for (auto& sa : sresults) {
         std::visit([&](auto&& v) { return results.Push(Val(v)); }, sa);
     }
@@ -129,13 +126,13 @@
 }
 
 /// Creates a Case with Values for args and expected error
-static Case E(std::initializer_list<Types> args, std::string err) {
-    return Case{utils::Vector<Types, 8>{args}, std::move(err)};
+static Case E(std::initializer_list<Value> args, std::string err) {
+    return Case{utils::Vector<Value, 8>{args}, std::move(err)};
 }
 
 /// Convenience overload that creates an expected-error Case with just scalars
 static Case E(std::initializer_list<ScalarTypes> sargs, std::string err) {
-    utils::Vector<Types, 8> args;
+    utils::Vector<Value, 8> args;
     for (auto& sa : sargs) {
         std::visit([&](auto&& v) { return args.Push(Val(v)); }, sa);
     }
@@ -152,7 +149,7 @@
 
     utils::Vector<const ast::Expression*, 8> args;
     for (auto& a : c.args) {
-        std::visit([&](auto&& v) { args.Push(v.Expr(*this)); }, a);
+        args.Push(a.Expr(*this));
     }
 
     auto* expr = Call(Source{{12, 34}}, sem::str(builtin), std::move(args));
@@ -173,14 +170,13 @@
             // 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);
+                CheckConstant(value->Index(i), 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);
+            CheckConstant(value, expected_case.values[0], expected_case.flags);
         }
     } else {
         EXPECT_FALSE(r()->Resolve());
@@ -803,6 +799,33 @@
                                               CrossCases<f16>()))));
 
 template <typename T>
+std::vector<Case> DistanceCases() {
+    auto error_msg = [](auto a, const char* op, auto b) {
+        return "12:34 error: " + OverflowErrorMessage(a, op, b) + R"(
+12:34 note: when calculating distance)";
+    };
+
+    return std::vector<Case>{
+        C({T(0), T(0)}, T(0)),
+        // length(-5) -> 5
+        C({T(30), T(35)}, T(5)),
+
+        C({Vec(T(30), T(20)), Vec(T(25), T(15))}, Val(T(7.0710678119))).FloatComp(),
+
+        E({T::Lowest(), T::Highest()}, error_msg(T::Lowest(), "-", T::Highest())),
+        E({Vec(T::Highest(), T::Highest()), Vec(T(1), T(1))},
+          error_msg(T(T::Highest() - T(1)), "*", T(T::Highest() - T(1)))),
+    };
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    Distance,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kDistance),
+                     testing::ValuesIn(Concat(DistanceCases<AFloat>(),  //
+                                              DistanceCases<f32>(),     //
+                                              DistanceCases<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))),
@@ -854,6 +877,83 @@
                                               DotCases<f16>()))));
 
 template <typename T>
+std::vector<Case> DeterminantCases() {
+    auto error_msg = [](auto a, const char* op, auto b) {
+        return "12:34 error: " + OverflowErrorMessage(a, op, b) + R"(
+12:34 note: when calculating determinant)";
+    };
+
+    auto r = std::vector<Case>{
+        // All zero == 0
+        C({Mat({T(0), T(0)},    //
+               {T(0), T(0)})},  //
+          Val(T(0))),
+
+        C({Mat({T(0), T(0), T(0)},    //
+               {T(0), T(0), T(0)},    //
+               {T(0), T(0), T(0)})},  //
+          Val(T(0))),
+
+        C({Mat({T(0), T(0), T(0), T(0)},    //
+               {T(0), T(0), T(0), T(0)},    //
+               {T(0), T(0), T(0), T(0)},    //
+               {T(0), T(0), T(0), T(0)})},  //
+          Val(T(0))),
+
+        // All same == 0
+        C({Mat({T(42), T(42)},    //
+               {T(42), T(42)})},  //
+          Val(T(0))),
+
+        C({Mat({T(42), T(42), T(42)},    //
+               {T(42), T(42), T(42)},    //
+               {T(42), T(42), T(42)})},  //
+          Val(T(0))),
+
+        C({Mat({T(42), T(42), T(42), T(42)},    //
+               {T(42), T(42), T(42), T(42)},    //
+               {T(42), T(42), T(42), T(42)},    //
+               {T(42), T(42), T(42), T(42)})},  //
+          Val(T(0))),
+
+        // Various values
+        C({Mat({-T(2), T(17)},   //
+               {T(5), T(45)})},  //
+          Val(-T(175))),
+
+        C({Mat({T(4), T(6), -T(13)},    //
+               {T(12), T(5), T(8)},     //
+               {T(9), T(17), T(16)})},  //
+          Val(-T(3011))),
+
+        C({Mat({T(2), T(9), T(8), T(1)},       //
+               {-T(4), T(11), -T(3), T(7)},    //
+               {T(6), T(5), T(12), -T(6)},     //
+               {T(3), -T(10), T(4), -T(7)})},  //
+          Val(T(469))),
+
+        // Overflow during multiply
+        E({Mat({T::Highest(), T(0)},  //
+               {T(0), T(2)})},        //
+          error_msg(T::Highest(), "*", T(2))),
+
+        // Overflow during subtract
+        E({Mat({T::Highest(), T::Lowest()},  //
+               {T(1), T(1)})},               //
+          error_msg(T::Highest(), "-", T::Lowest())),
+    };
+
+    return r;
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    Determinant,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kDeterminant),
+                     testing::ValuesIn(Concat(DeterminantCases<AFloat>(),  //
+                                              DeterminantCases<f32>(),     //
+                                              DeterminantCases<f16>()))));
+
+template <typename T>
 std::vector<Case> FirstLeadingBitCases() {
     using B = BitValues<T>;
     auto r = std::vector<Case>{
@@ -970,6 +1070,87 @@
                                               FloorCases<f16>()))));
 
 template <typename T>
+std::vector<Case> FmaCases() {
+    auto error_msg = [](auto a, const char* op, auto b) {
+        return "12:34 error: " + OverflowErrorMessage(a, op, b) + R"(
+12:34 note: when calculating fma)";
+    };
+    return {
+        C({T(0), T(0), T(0)}, T(0)),
+        C({T(1), T(2), T(3)}, T(5)),
+        C({Vec(T(1), T(2.5), -T(1)), Vec(T(2), T(2.5), T(1)), Vec(T(4), T(3.75), -T(2))},
+          Vec(T(6), T(10), -T(3))),
+
+        E({T::Highest(), T::Highest(), T(0)}, error_msg(T::Highest(), "*", T::Highest())),
+        E({T::Highest(), T(1), T::Highest()}, error_msg(T::Highest(), "+", T::Highest())),
+    };
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    Fma,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kFma),
+                     testing::ValuesIn(Concat(FmaCases<AFloat>(),  //
+                                              FmaCases<f32>(),
+                                              FmaCases<f16>()))));
+
+template <typename T>
+std::vector<Case> FrexpCases() {
+    using F = T;                                                         // fract type
+    using E = std::conditional_t<std::is_same_v<T, AFloat>, AInt, i32>;  // exp type
+
+    auto cases = std::vector<Case>{
+        // Scalar tests
+        //  in         fract     exp
+        C({T(-3.5)}, {F(-0.875), E(2)}),  //
+        C({T(-3.0)}, {F(-0.750), E(2)}),  //
+        C({T(-2.5)}, {F(-0.625), E(2)}),  //
+        C({T(-2.0)}, {F(-0.500), E(2)}),  //
+        C({T(-1.5)}, {F(-0.750), E(1)}),  //
+        C({T(-1.0)}, {F(-0.500), E(1)}),  //
+        C({T(+0.0)}, {F(+0.000), E(0)}),  //
+        C({T(+1.0)}, {F(+0.500), E(1)}),  //
+        C({T(+1.5)}, {F(+0.750), E(1)}),  //
+        C({T(+2.0)}, {F(+0.500), E(2)}),  //
+        C({T(+2.5)}, {F(+0.625), E(2)}),  //
+        C({T(+3.0)}, {F(+0.750), E(2)}),  //
+        C({T(+3.5)}, {F(+0.875), E(2)}),  //
+
+        // Vector tests
+        //         in                 fract                    exp
+        C({Vec(T(-2.5), T(+1.0))}, {Vec(F(-0.625), F(+0.500)), Vec(E(2), E(1))}),
+        C({Vec(T(+3.5), T(-2.5))}, {Vec(F(+0.875), F(-0.625)), Vec(E(2), E(2))}),
+    };
+
+    ConcatIntoIf<std::is_same_v<T, f16>>(cases, std::vector<Case>{
+                                                    C({T::Highest()}, {F(0x0.ffep0), E(16)}),  //
+                                                    C({T::Lowest()}, {F(-0x0.ffep0), E(16)}),  //
+                                                    C({T::Smallest()}, {F(0.5), E(-13)}),      //
+                                                });
+
+    ConcatIntoIf<std::is_same_v<T, f32>>(cases,
+                                         std::vector<Case>{
+                                             C({T::Highest()}, {F(0x0.ffffffp0), E(128)}),  //
+                                             C({T::Lowest()}, {F(-0x0.ffffffp0), E(128)}),  //
+                                             C({T::Smallest()}, {F(0.5), E(-125)}),         //
+                                         });
+
+    ConcatIntoIf<std::is_same_v<T, AFloat>>(
+        cases, std::vector<Case>{
+                   C({T::Highest()}, {F(0x0.fffffffffffff8p0), E(1024)}),  //
+                   C({T::Lowest()}, {F(-0x0.fffffffffffff8p0), E(1024)}),  //
+                   C({T::Smallest()}, {F(0.5), E(-1021)}),                 //
+               });
+    return cases;
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    Frexp,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kFrexp),
+                     testing::ValuesIn(Concat(FrexpCases<AFloat>(),  //
+                                              FrexpCases<f32>(),     //
+                                              FrexpCases<f16>()))));
+
+template <typename T>
 std::vector<Case> InsertBitsCases() {
     using UT = Number<std::make_unsigned_t<UnwrapNumber<T>>>;
 
@@ -1051,6 +1232,28 @@
                                               InsertBitsCases<u32>()))));
 
 template <typename T>
+std::vector<Case> InverseSqrtCases() {
+    std::vector<Case> cases = {
+        C({T(25)}, T(.2)),
+
+        // Vector tests
+        C({Vec(T(25), T(100))}, Vec(T(.2), T(.1))),
+
+        E({T(0)}, "12:34 error: inverseSqrt must be called with a value > 0"),
+        E({-T(0)}, "12:34 error: inverseSqrt must be called with a value > 0"),
+        E({-T(25)}, "12:34 error: inverseSqrt must be called with a value > 0"),
+    };
+    return cases;
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    InverseSqrt,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kInverseSqrt),
+                     testing::ValuesIn(Concat(InverseSqrtCases<AFloat>(),  //
+                                              InverseSqrtCases<f32>(),
+                                              InverseSqrtCases<f16>()))));
+
+template <typename T>
 std::vector<Case> DegreesAFloatCases() {
     return std::vector<Case>{
         C({T(0)}, T(0)),                             //
@@ -1102,6 +1305,46 @@
                      testing::ValuesIn(DegreesF16Cases<f16>())));
 
 template <typename T>
+std::vector<Case> ExpCases() {
+    auto error_msg = [](auto a) { return "12:34 error: " + OverflowExpErrorMessage("e", a); };
+    return std::vector<Case>{C({T(0)}, T(1)),   //
+                             C({-T(0)}, T(1)),  //
+                             C({T(2)}, T(7.3890562)).FloatComp(),
+                             C({-T(2)}, T(0.13533528)).FloatComp(),  //
+                             C({T::Lowest()}, T(0)),
+
+                             E({T::Highest()}, error_msg(T::Highest()))};
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    Exp,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kExp),
+                     testing::ValuesIn(Concat(ExpCases<AFloat>(),  //
+                                              ExpCases<f32>(),
+                                              ExpCases<f16>()))));
+
+template <typename T>
+std::vector<Case> Exp2Cases() {
+    auto error_msg = [](auto a) { return "12:34 error: " + OverflowExpErrorMessage("2", a); };
+    return std::vector<Case>{
+        C({T(0)}, T(1)),   //
+        C({-T(0)}, T(1)),  //
+        C({T(2)}, T(4.0)),
+        C({-T(2)}, T(0.25)),  //
+        C({T::Lowest()}, T(0)),
+
+        E({T::Highest()}, error_msg(T::Highest())),
+    };
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    Exp2,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kExp2),
+                     testing::ValuesIn(Concat(Exp2Cases<AFloat>(),  //
+                                              Exp2Cases<f32>(),
+                                              Exp2Cases<f16>()))));
+
+template <typename T>
 std::vector<Case> ExtractBitsCases() {
     using UT = Number<std::make_unsigned_t<UnwrapNumber<T>>>;
 
@@ -1257,6 +1500,110 @@
                                               LengthCases<f16>()))));
 
 template <typename T>
+std::vector<Case> LogCases() {
+    auto error_msg = [] { return "12:34 error: log must be called with a value > 0"; };
+    return std::vector<Case>{C({T(1)}, T(0)),                              //
+                             C({T(54.598150033)}, T(4)).FloatComp(0.002),  //
+
+                             E({T::Lowest()}, error_msg()), E({T(0)}, error_msg()),
+                             E({-T(0)}, error_msg())};
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    Log,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kLog),
+                     testing::ValuesIn(Concat(LogCases<AFloat>(),  //
+                                              LogCases<f32>(),
+                                              LogCases<f16>()))));
+template <typename T>
+std::vector<Case> LogF16Cases() {
+    return std::vector<Case>{
+        C({T::Highest()}, T(11.085938)).FloatComp(),
+    };
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    LogF16,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kLog),
+                     testing::ValuesIn(LogF16Cases<f16>())));
+template <typename T>
+std::vector<Case> LogF32Cases() {
+    return std::vector<Case>{
+        C({T::Highest()}, T(88.722839)).FloatComp(),
+    };
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    LogF32,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kLog),
+                     testing::ValuesIn(LogF32Cases<f32>())));
+
+template <typename T>
+std::vector<Case> LogAbstractCases() {
+    return std::vector<Case>{
+        C({T::Highest()}, T(709.78271)).FloatComp(),
+    };
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    LogAbstract,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kLog),
+                     testing::ValuesIn(LogAbstractCases<AFloat>())));
+
+template <typename T>
+std::vector<Case> Log2Cases() {
+    auto error_msg = [] { return "12:34 error: log2 must be called with a value > 0"; };
+    return std::vector<Case>{
+        C({T(1)}, T(0)),  //
+        C({T(4)}, T(2)),  //
+
+        E({T::Lowest()}, error_msg()),
+        E({T(0)}, error_msg()),
+        E({-T(0)}, error_msg()),
+    };
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    Log2,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kLog2),
+                     testing::ValuesIn(Concat(Log2Cases<AFloat>(),  //
+                                              Log2Cases<f32>(),
+                                              Log2Cases<f16>()))));
+template <typename T>
+std::vector<Case> Log2F16Cases() {
+    return std::vector<Case>{
+        C({T::Highest()}, T(15.9922)).FloatComp(),
+    };
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    Log2F16,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kLog2),
+                     testing::ValuesIn(Log2F16Cases<f16>())));
+template <typename T>
+std::vector<Case> Log2F32Cases() {
+    return std::vector<Case>{
+        C({T::Highest()}, T(128)).FloatComp(),
+    };
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    Log2F32,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kLog2),
+                     testing::ValuesIn(Log2F32Cases<f32>())));
+template <typename T>
+std::vector<Case> Log2AbstractCases() {
+    return std::vector<Case>{
+        C({T::Highest()}, T(1024)).FloatComp(),
+    };
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    Log2Abstract,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kLog2),
+                     testing::ValuesIn(Log2AbstractCases<AFloat>())));
+
+template <typename T>
 std::vector<Case> MaxCases() {
     return {
         C({T(0), T(0)}, T(0)),
@@ -1339,9 +1686,40 @@
     Modf,
     ResolverConstEvalBuiltinTest,
     testing::Combine(testing::Values(sem::BuiltinType::kModf),
-                     testing::ValuesIn(Concat(ModfCases<f32>(),  //
+                     testing::ValuesIn(Concat(ModfCases<AFloat>(),  //
+                                              ModfCases<f32>(),     //
                                               ModfCases<f16>()))));
 
+template <typename T>
+std::vector<Case> NormalizeCases() {
+    auto error_msg = [&](auto a) {
+        return "12:34 error: " + OverflowErrorMessage(a, "*", a) + R"(
+12:34 note: when calculating normalize)";
+    };
+
+    return {
+        C({Vec(T(2), T(4), T(2))}, Vec(T(0.4082482905), T(0.8164965809), T(0.4082482905)))
+            .FloatComp(),
+
+        C({Vec(T(2), T(0), T(0))}, Vec(T(1), T(0), T(0))),
+        C({Vec(T(0), T(2), T(0))}, Vec(T(0), T(1), T(0))),
+        C({Vec(T(0), T(0), T(2))}, Vec(T(0), T(0), T(1))),
+        C({Vec(-T(2), T(0), T(0))}, Vec(-T(1), T(0), T(0))),
+        C({Vec(T(0), -T(2), T(0))}, Vec(T(0), -T(1), T(0))),
+        C({Vec(T(0), T(0), -T(2))}, Vec(T(0), T(0), -T(1))),
+
+        E({Vec(T(0), T(0), T(0))}, "12:34 error: zero length vector can not be normalized"),
+        E({Vec(T::Highest(), T::Highest(), T::Highest())}, error_msg(T::Highest())),
+    };
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    Normalize,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kNormalize),
+                     testing::ValuesIn(Concat(NormalizeCases<AFloat>(),  //
+                                              NormalizeCases<f32>(),     //
+                                              NormalizeCases<f16>()))));
+
 std::vector<Case> Pack4x8snormCases() {
     return {
         C({Vec(f32(0), f32(0), f32(0), f32(0))}, Val(u32(0x0000'0000))),
@@ -1815,6 +2193,72 @@
                                               TanhCases<f16>()))));
 
 template <typename T>
+std::vector<Case> TransposeCases() {
+    return {
+        // 2x2
+        C({Mat({T(1), T(2)},    //
+               {T(3), T(4)})},  //
+          Mat({T(1), T(3)},     //
+              {T(2), T(4)})),
+
+        // 3x3
+        C({Mat({T(1), T(2), T(3)},    //
+               {T(4), T(5), T(6)},    //
+               {T(7), T(8), T(9)})},  //
+          Mat({T(1), T(4), T(7)},     //
+              {T(2), T(5), T(8)},     //
+              {T(3), T(6), T(9)})),
+
+        // 4x4
+        C({Mat({T(1), T(2), T(3), T(4)},        //
+               {T(5), T(6), T(7), T(8)},        //
+               {T(9), T(10), T(11), T(12)},     //
+               {T(13), T(14), T(15), T(16)})},  //
+          Mat({T(1), T(5), T(9), T(13)},        //
+              {T(2), T(6), T(10), T(14)},       //
+              {T(3), T(7), T(11), T(15)},       //
+              {T(4), T(8), T(12), T(16)})),
+
+        // 4x2
+        C({Mat({T(1), T(2), T(3), T(4)},    //
+               {T(5), T(6), T(7), T(8)})},  //
+          Mat({T(1), T(5)},                 //
+              {T(2), T(6)},                 //
+              {T(3), T(7)},                 //
+              {T(4), T(8)})),
+
+        // 2x4
+        C({Mat({T(1), T(2)},             //
+               {T(3), T(4)},             //
+               {T(5), T(6)},             //
+               {T(7), T(8)})},           //
+          Mat({T(1), T(3), T(5), T(7)},  //
+              {T(2), T(4), T(6), T(8)})),
+
+        // 3x2
+        C({Mat({T(1), T(2), T(3)},    //
+               {T(4), T(5), T(6)})},  //
+          Mat({T(1), T(4)},           //
+              {T(2), T(5)},           //
+              {T(3), T(6)})),
+
+        // 2x3
+        C({Mat({T(1), T(2)},       //
+               {T(3), T(4)},       //
+               {T(5), T(6)})},     //
+          Mat({T(1), T(3), T(5)},  //
+              {T(2), T(4), T(6)})),
+    };
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    Transpose,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kTranspose),
+                     testing::ValuesIn(Concat(TransposeCases<AFloat>(),  //
+                                              TransposeCases<f32>(),
+                                              TransposeCases<f16>()))));
+
+template <typename T>
 std::vector<Case> TruncCases() {
     std::vector<Case> cases = {C({T(0)}, T(0)),    //
                                C({-T(0)}, -T(0)),  //
diff --git a/src/tint/resolver/const_eval_conversion_test.cc b/src/tint/resolver/const_eval_conversion_test.cc
index ed68725..da37f3b 100644
--- a/src/tint/resolver/const_eval_conversion_test.cc
+++ b/src/tint/resolver/const_eval_conversion_test.cc
@@ -19,19 +19,6 @@
 namespace tint::resolver {
 namespace {
 
-using Scalar = std::variant<  //
-    builder::Value<AInt>,
-    builder::Value<AFloat>,
-    builder::Value<u32>,
-    builder::Value<i32>,
-    builder::Value<f32>,
-    builder::Value<f16>,
-    builder::Value<bool>>;
-
-static std::ostream& operator<<(std::ostream& o, const Scalar& scalar) {
-    return ToValueBase(scalar)->Print(o);
-}
-
 enum class Kind {
     kScalar,
     kVector,
@@ -48,8 +35,8 @@
 }
 
 struct Case {
-    Scalar input;
-    Scalar expected;
+    Value input;
+    Value expected;
     builder::CreatePtrs type;
     bool unrepresentable = false;
 };
@@ -65,7 +52,7 @@
 
 template <typename TO, typename FROM>
 Case Success(FROM input, TO expected) {
-    return {builder::Val(input), builder::Val(expected), builder::CreatePtrsFor<TO>()};
+    return {Val(input), Val(expected), builder::CreatePtrsFor<TO>()};
 }
 
 template <typename TO, typename FROM>
@@ -83,7 +70,7 @@
     const auto& type = std::get<1>(GetParam()).type;
     const auto unrepresentable = std::get<1>(GetParam()).unrepresentable;
 
-    auto* input_val = ToValueBase(input)->Expr(*this);
+    auto* input_val = input.Expr(*this);
     auto* expr = Construct(type.ast(*this), input_val);
     if (kind == Kind::kVector) {
         expr = Construct(ty.vec(nullptr, 3), expr);
@@ -107,7 +94,7 @@
         ASSERT_NE(sem->ConstantValue(), nullptr);
         EXPECT_TYPE(sem->ConstantValue()->Type(), target_sem_ty);
 
-        auto expected_values = ToValueBase(expected)->Args();
+        auto expected_values = expected.Args();
         if (kind == Kind::kVector) {
             expected_values.values.Push(expected_values.values[0]);
             expected_values.values.Push(expected_values.values[0]);
diff --git a/src/tint/resolver/const_eval_test.h b/src/tint/resolver/const_eval_test.h
index bc85542..dcb91d0 100644
--- a/src/tint/resolver/const_eval_test.h
+++ b/src/tint/resolver/const_eval_test.h
@@ -88,10 +88,10 @@
 /// @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,
+                          const builder::Value& expected_value,
                           CheckConstantFlags flags = {}) {
     auto values_flat = ScalarArgsFrom(got_constant);
-    auto expected_values_flat = expected_value->Args();
+    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];
@@ -223,7 +223,7 @@
     return ss.str();
 }
 
-/// Returns the overflow error message for converions
+/// Returns the overflow error message for conversions
 template <typename VALUE_TY>
 std::string OverflowErrorMessage(VALUE_TY value, std::string_view target_ty) {
     std::stringstream ss;
@@ -233,77 +233,22 @@
     return ss.str();
 }
 
+/// Returns the overflow error message for exponentiation
+template <typename NumberT>
+std::string OverflowExpErrorMessage(std::string_view base, NumberT value) {
+    std::stringstream ss;
+    ss << std::setprecision(20);
+    ss << base << "^" << value << " cannot be represented as "
+       << "'" << FriendlyName<NumberT>() << "'";
+    return ss.str();
+}
+
 using builder::IsValue;
 using builder::Mat;
 using builder::Val;
 using builder::Value;
-using builder::ValueBase;
 using builder::Vec;
 
-using Types = std::variant<  //
-    Value<AInt>,
-    Value<AFloat>,
-    Value<u32>,
-    Value<i32>,
-    Value<f32>,
-    Value<f16>,
-    Value<bool>,
-
-    Value<builder::vec2<AInt>>,
-    Value<builder::vec2<AFloat>>,
-    Value<builder::vec2<u32>>,
-    Value<builder::vec2<i32>>,
-    Value<builder::vec2<f32>>,
-    Value<builder::vec2<f16>>,
-    Value<builder::vec2<bool>>,
-
-    Value<builder::vec3<AInt>>,
-    Value<builder::vec3<AFloat>>,
-    Value<builder::vec3<u32>>,
-    Value<builder::vec3<i32>>,
-    Value<builder::vec3<f32>>,
-    Value<builder::vec3<f16>>,
-    Value<builder::vec3<bool>>,
-
-    Value<builder::vec4<AInt>>,
-    Value<builder::vec4<AFloat>>,
-    Value<builder::vec4<u32>>,
-    Value<builder::vec4<i32>>,
-    Value<builder::vec4<f32>>,
-    Value<builder::vec4<f16>>,
-    Value<builder::vec4<bool>>,
-
-    Value<builder::mat2x2<AInt>>,
-    Value<builder::mat2x2<AFloat>>,
-    Value<builder::mat2x2<f32>>,
-    Value<builder::mat2x2<f16>>,
-
-    Value<builder::mat2x3<AInt>>,
-    Value<builder::mat2x3<AFloat>>,
-    Value<builder::mat2x3<f32>>,
-    Value<builder::mat2x3<f16>>,
-
-    Value<builder::mat3x2<AInt>>,
-    Value<builder::mat3x2<AFloat>>,
-    Value<builder::mat3x2<f32>>,
-    Value<builder::mat3x2<f16>>
-    //
-    >;
-
-/// Returns the current Value<T> in the `types` variant as a `ValueBase` pointer to use the
-/// polymorphic API. This trades longer compile times using std::variant for longer runtime via
-/// virtual function calls.
-template <typename ValueVariant>
-inline const ValueBase* ToValueBase(const ValueVariant& types) {
-    return std::visit(
-        [](auto&& t) -> const ValueBase* { return static_cast<const ValueBase*>(&t); }, types);
-}
-
-/// Prints Types to ostream
-inline std::ostream& operator<<(std::ostream& o, const Types& types) {
-    return ToValueBase(types)->Print(o);
-}
-
 // Calls `f` on deepest elements of both `a` and `b`. If function returns Action::kStop, it stops
 // traversing, and return Action::kStop; if the function returns Action::kContinue, it continues and
 // returns Action::kContinue when done.
diff --git a/src/tint/resolver/const_eval_unary_op_test.cc b/src/tint/resolver/const_eval_unary_op_test.cc
index fced490..d24c27b 100644
--- a/src/tint/resolver/const_eval_unary_op_test.cc
+++ b/src/tint/resolver/const_eval_unary_op_test.cc
@@ -19,22 +19,17 @@
 namespace tint::resolver {
 namespace {
 
-// Bring in std::ostream& operator<<(std::ostream& o, const Types& types)
-using resolver::operator<<;
-
 struct Case {
-    Types input;
-    Types expected;
+    Value input;
+    Value expected;
 };
 
 static std::ostream& operator<<(std::ostream& o, const Case& c) {
     o << "input: " << c.input << ", expected: " << c.expected;
     return o;
 }
-
-/// Creates a Case with Values of any type
-template <typename T, typename U>
-Case C(Value<T> input, Value<U> expected) {
+// Creates a Case with Values of any type
+Case C(Value input, Value expected) {
     return Case{std::move(input), std::move(expected)};
 }
 
@@ -52,10 +47,10 @@
     auto op = std::get<0>(GetParam());
     auto& c = std::get<1>(GetParam());
 
-    auto* expected = ToValueBase(c.expected);
-    auto* input = ToValueBase(c.input);
+    auto& expected = c.expected;
+    auto& input = c.input;
 
-    auto* input_expr = input->Expr(*this);
+    auto* input_expr = input.Expr(*this);
     auto* expr = create<ast::UnaryOpExpression>(op, input_expr);
 
     GlobalConst("C", expr);
@@ -67,13 +62,13 @@
     EXPECT_TYPE(value->Type(), sem->Type());
 
     auto values_flat = ScalarArgsFrom(value);
-    auto expected_values_flat = expected->Args();
+    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()) {
+        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));
diff --git a/src/tint/resolver/dependency_graph.cc b/src/tint/resolver/dependency_graph.cc
index 1fda54f..796412b 100644
--- a/src/tint/resolver/dependency_graph.cc
+++ b/src/tint/resolver/dependency_graph.cc
@@ -471,7 +471,7 @@
             }
         }
 
-        if (auto* global = globals_.Find(to); global && (*global)->node == resolved) {
+        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);
diff --git a/src/tint/resolver/dependency_graph_test.cc b/src/tint/resolver/dependency_graph_test.cc
index 2cc4a3a..81e79ff 100644
--- a/src/tint/resolver/dependency_graph_test.cc
+++ b/src/tint/resolver/dependency_graph_test.cc
@@ -1128,8 +1128,8 @@
 
     if (expect_pass) {
         // Check that the use resolves to the declaration
-        auto* resolved_symbol = graph.resolved_symbols.Find(use);
-        ASSERT_NE(resolved_symbol, nullptr);
+        auto resolved_symbol = graph.resolved_symbols.Find(use);
+        ASSERT_TRUE(resolved_symbol);
         EXPECT_EQ(*resolved_symbol, decl)
             << "resolved: " << (*resolved_symbol ? (*resolved_symbol)->TypeInfo().name : "<null>")
             << "\n"
@@ -1179,8 +1179,8 @@
     helper.Build();
 
     auto shadows = Build().shadows;
-    auto* shadow = shadows.Find(inner_var);
-    ASSERT_NE(shadow, nullptr);
+    auto shadow = shadows.Find(inner_var);
+    ASSERT_TRUE(shadow);
     EXPECT_EQ(*shadow, outer);
 }
 
@@ -1310,8 +1310,8 @@
 
     auto graph = Build();
     for (auto use : symbol_uses) {
-        auto* resolved_symbol = graph.resolved_symbols.Find(use.use);
-        ASSERT_NE(resolved_symbol, nullptr) << use.where;
+        auto resolved_symbol = graph.resolved_symbols.Find(use.use);
+        ASSERT_TRUE(resolved_symbol) << use.where;
         EXPECT_EQ(*resolved_symbol, use.decl) << use.where;
     }
 }
diff --git a/src/tint/resolver/intrinsic_table.cc b/src/tint/resolver/intrinsic_table.cc
index 9d11e08..a04eca4 100644
--- a/src/tint/resolver/intrinsic_table.cc
+++ b/src/tint/resolver/intrinsic_table.cc
@@ -796,20 +796,20 @@
     std::string name;
     const sem::Type* type;
 };
-const sem::Struct* build_struct(MatchState& state,
-                                std::string name,
-                                std::initializer_list<NameAndType> member_names_and_types) {
+sem::Struct* build_struct(ProgramBuilder& b,
+                          std::string name,
+                          std::initializer_list<NameAndType> member_names_and_types) {
     uint32_t offset = 0;
     uint32_t max_align = 0;
     sem::StructMemberList members;
     for (auto& m : member_names_and_types) {
-        uint32_t align = m.type->Align();
+        uint32_t align = std::max<uint32_t>(m.type->Align(), 1);
         uint32_t size = m.type->Size();
         offset = utils::RoundUp(align, offset);
         max_align = std::max(max_align, align);
-        members.emplace_back(state.builder.create<sem::StructMember>(
+        members.emplace_back(b.create<sem::StructMember>(
             /* declaration */ nullptr,
-            /* name */ state.builder.Sym(m.name),
+            /* name */ b.Sym(m.name),
             /* type */ m.type,
             /* index */ static_cast<uint32_t>(members.size()),
             /* offset */ offset,
@@ -820,9 +820,9 @@
     }
     uint32_t size_without_padding = offset;
     uint32_t size_with_padding = utils::RoundUp(max_align, offset);
-    return state.builder.create<sem::Struct>(
+    return b.create<sem::Struct>(
         /* declaration */ nullptr,
-        /* name */ state.builder.Sym(name),
+        /* name */ b.Sym(name),
         /* members */ members,
         /* align */ max_align,
         /* size */ size_with_padding,
@@ -830,48 +830,128 @@
 }
 
 const sem::Struct* build_modf_result(MatchState& state, const sem::Type* el) {
-    std::string display_name;
-    if (el->Is<sem::F16>()) {
-        display_name = "__modf_result_f16";
-    } else {
-        display_name = "__modf_result";
-    }
-    return build_struct(state, display_name, {{"fract", el}, {"whole", el}});
+    auto build_f32 = [&] {
+        auto* ty = state.builder.create<sem::F32>();
+        return build_struct(state.builder, "__modf_result_f32", {{"fract", ty}, {"whole", ty}});
+    };
+    auto build_f16 = [&] {
+        auto* ty = state.builder.create<sem::F16>();
+        return build_struct(state.builder, "__modf_result_f16", {{"fract", ty}, {"whole", ty}});
+    };
+
+    return Switch(
+        el,                                            //
+        [&](const sem::F32*) { return build_f32(); },  //
+        [&](const sem::F16*) { return build_f16(); },  //
+        [&](const sem::AbstractFloat*) {
+            auto* abstract = build_struct(state.builder, "__modf_result_abstract",
+                                          {{"fract", el}, {"whole", el}});
+            abstract->SetConcreteTypes(utils::Vector{build_f32(), build_f16()});
+            return abstract;
+        },
+        [&](Default) {
+            TINT_ICE(Resolver, state.builder.Diagnostics())
+                << "unhandled modf type: " << state.builder.FriendlyName(el);
+            return nullptr;
+        });
 }
+
 const sem::Struct* build_modf_result_vec(MatchState& state, Number& n, const sem::Type* el) {
-    std::string display_name;
-    if (el->Is<sem::F16>()) {
-        display_name = "__modf_result_vec" + std::to_string(n.Value()) + "_f16";
-    } else {
-        display_name = "__modf_result_vec" + std::to_string(n.Value());
-    }
-    auto* vec = state.builder.create<sem::Vector>(el, n.Value());
-    return build_struct(state, display_name, {{"fract", vec}, {"whole", vec}});
+    auto prefix = "__modf_result_vec" + std::to_string(n.Value());
+    auto build_f32 = [&] {
+        auto* vec = state.builder.create<sem::Vector>(state.builder.create<sem::F32>(), n.Value());
+        return build_struct(state.builder, prefix + "_f32", {{"fract", vec}, {"whole", vec}});
+    };
+    auto build_f16 = [&] {
+        auto* vec = state.builder.create<sem::Vector>(state.builder.create<sem::F16>(), n.Value());
+        return build_struct(state.builder, prefix + "_f16", {{"fract", vec}, {"whole", vec}});
+    };
+
+    return Switch(
+        el,                                            //
+        [&](const sem::F32*) { return build_f32(); },  //
+        [&](const sem::F16*) { return build_f16(); },  //
+        [&](const sem::AbstractFloat*) {
+            auto* vec = state.builder.create<sem::Vector>(el, n.Value());
+            auto* abstract =
+                build_struct(state.builder, prefix + "_abstract", {{"fract", vec}, {"whole", vec}});
+            abstract->SetConcreteTypes(utils::Vector{build_f32(), build_f16()});
+            return abstract;
+        },
+        [&](Default) {
+            TINT_ICE(Resolver, state.builder.Diagnostics())
+                << "unhandled modf type: " << state.builder.FriendlyName(el);
+            return nullptr;
+        });
 }
+
 const sem::Struct* build_frexp_result(MatchState& state, const sem::Type* el) {
-    std::string display_name;
-    if (el->Is<sem::F16>()) {
-        display_name = "__frexp_result_f16";
-    } else {
-        display_name = "__frexp_result";
-    }
-    auto* i32 = state.builder.create<sem::I32>();
-    return build_struct(state, display_name, {{"fract", el}, {"exp", i32}});
+    auto build_f32 = [&] {
+        auto* f = state.builder.create<sem::F32>();
+        auto* i = state.builder.create<sem::I32>();
+        return build_struct(state.builder, "__frexp_result_f32", {{"fract", f}, {"exp", i}});
+    };
+    auto build_f16 = [&] {
+        auto* f = state.builder.create<sem::F16>();
+        auto* i = state.builder.create<sem::I32>();
+        return build_struct(state.builder, "__frexp_result_f16", {{"fract", f}, {"exp", i}});
+    };
+
+    return Switch(
+        el,                                            //
+        [&](const sem::F32*) { return build_f32(); },  //
+        [&](const sem::F16*) { return build_f16(); },  //
+        [&](const sem::AbstractFloat*) {
+            auto* i = state.builder.create<sem::AbstractInt>();
+            auto* abstract =
+                build_struct(state.builder, "__frexp_result_abstract", {{"fract", el}, {"exp", i}});
+            abstract->SetConcreteTypes(utils::Vector{build_f32(), build_f16()});
+            return abstract;
+        },
+        [&](Default) {
+            TINT_ICE(Resolver, state.builder.Diagnostics())
+                << "unhandled frexp type: " << state.builder.FriendlyName(el);
+            return nullptr;
+        });
 }
+
 const sem::Struct* build_frexp_result_vec(MatchState& state, Number& n, const sem::Type* el) {
-    std::string display_name;
-    if (el->Is<sem::F16>()) {
-        display_name = "__frexp_result_vec" + std::to_string(n.Value()) + "_f16";
-    } else {
-        display_name = "__frexp_result_vec" + std::to_string(n.Value());
-    }
-    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, {{"fract", vec}, {"exp", vec_i32}});
+    auto prefix = "__frexp_result_vec" + std::to_string(n.Value());
+    auto build_f32 = [&] {
+        auto* f = state.builder.create<sem::Vector>(state.builder.create<sem::F32>(), n.Value());
+        auto* e = state.builder.create<sem::Vector>(state.builder.create<sem::I32>(), n.Value());
+        return build_struct(state.builder, prefix + "_f32", {{"fract", f}, {"exp", e}});
+    };
+    auto build_f16 = [&] {
+        auto* f = state.builder.create<sem::Vector>(state.builder.create<sem::F16>(), n.Value());
+        auto* e = state.builder.create<sem::Vector>(state.builder.create<sem::I32>(), n.Value());
+        return build_struct(state.builder, prefix + "_f16", {{"fract", f}, {"exp", e}});
+    };
+
+    return Switch(
+        el,                                            //
+        [&](const sem::F32*) { return build_f32(); },  //
+        [&](const sem::F16*) { return build_f16(); },  //
+        [&](const sem::AbstractFloat*) {
+            auto* f = state.builder.create<sem::Vector>(el, n.Value());
+            auto* e = state.builder.create<sem::Vector>(state.builder.create<sem::AbstractInt>(),
+                                                        n.Value());
+            auto* abstract =
+                build_struct(state.builder, prefix + "_abstract", {{"fract", f}, {"exp", e}});
+            abstract->SetConcreteTypes(utils::Vector{build_f32(), build_f16()});
+            return abstract;
+        },
+        [&](Default) {
+            TINT_ICE(Resolver, state.builder.Diagnostics())
+                << "unhandled frexp type: " << state.builder.FriendlyName(el);
+            return nullptr;
+        });
 }
+
 const sem::Struct* build_atomic_compare_exchange_result(MatchState& state, const sem::Type* ty) {
     return build_struct(
-        state, "__atomic_compare_exchange_result" + ty->FriendlyName(state.builder.Symbols()),
+        state.builder,
+        "__atomic_compare_exchange_result" + ty->FriendlyName(state.builder.Symbols()),
         {{"old_value", const_cast<sem::Type*>(ty)},
          {"exchanged", state.builder.create<sem::Bool>()}});
 }
diff --git a/src/tint/resolver/intrinsic_table.h b/src/tint/resolver/intrinsic_table.h
index a873df9..05ca8d2 100644
--- a/src/tint/resolver/intrinsic_table.h
+++ b/src/tint/resolver/intrinsic_table.h
@@ -148,7 +148,6 @@
     ///        `sem::EvaluationStage::kRuntime`, then only overloads with concrete argument types
     ///        will be considered, as all abstract-numerics will have been materialized
     ///        after shader creation time (sem::EvaluationStage::kConstant).
-
     /// @param source the source of the call
     /// @return a sem::TypeInitializer, sem::TypeConversion or nullptr if nothing matched
     virtual InitOrConv Lookup(InitConvIntrinsic type,
diff --git a/src/tint/resolver/intrinsic_table.inl b/src/tint/resolver/intrinsic_table.inl
index be0e4bb..9d77df3 100644
--- a/src/tint/resolver/intrinsic_table.inl
+++ b/src/tint/resolver/intrinsic_table.inl
@@ -12198,24 +12198,24 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[600],
     /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::distance,
   },
   {
     /* [324] */
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[602],
     /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::distance,
   },
   {
     /* [325] */
@@ -12366,48 +12366,48 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[23],
     /* 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,
+    /* const eval */ &ConstEval::exp,
   },
   {
     /* [338] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[851],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::exp,
   },
   {
     /* [339] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[852],
     /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::exp2,
   },
   {
     /* [340] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[23],
     /* 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,
+    /* const eval */ &ConstEval::exp2,
   },
   {
     /* [341] */
@@ -12510,24 +12510,24 @@
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[23],
     /* 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,
+    /* const eval */ &ConstEval::fma,
   },
   {
     /* [350] */
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[23],
     /* 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,
+    /* const eval */ &ConstEval::fma,
   },
   {
     /* [351] */
@@ -12558,24 +12558,24 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[23],
     /* 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,
+    /* const eval */ &ConstEval::frexp,
   },
   {
     /* [354] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[23],
     /* 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,
+    /* const eval */ &ConstEval::frexp,
   },
   {
     /* [355] */
@@ -12678,24 +12678,24 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[23],
     /* 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,
+    /* const eval */ &ConstEval::inverseSqrt,
   },
   {
     /* [364] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[23],
     /* 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,
+    /* const eval */ &ConstEval::inverseSqrt,
   },
   {
     /* [365] */
@@ -12750,48 +12750,48 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[23],
     /* 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,
+    /* const eval */ &ConstEval::log,
   },
   {
     /* [370] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[23],
     /* 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,
+    /* const eval */ &ConstEval::log,
   },
   {
     /* [371] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[23],
     /* 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,
+    /* const eval */ &ConstEval::log2,
   },
   {
     /* [372] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[23],
     /* 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,
+    /* const eval */ &ConstEval::log2,
   },
   {
     /* [373] */
@@ -12846,7 +12846,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[878],
     /* return matcher indices */ &kMatcherIndices[106],
@@ -12858,7 +12858,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[879],
     /* return matcher indices */ &kMatcherIndices[45],
@@ -13566,12 +13566,12 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[23],
     /* 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,
+    /* const eval */ &ConstEval::determinant,
   },
   {
     /* [438] */
@@ -13626,12 +13626,12 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[23],
     /* 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,
+    /* const eval */ &ConstEval::normalize,
   },
   {
     /* [443] */
@@ -13734,12 +13734,12 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[23],
     /* 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,
+    /* const eval */ &ConstEval::transpose,
   },
   {
     /* [452] */
@@ -14124,14 +14124,14 @@
   },
   {
     /* [20] */
-    /* fn determinant<N : num, T : f32_f16>(mat<N, N, T>) -> T */
+    /* fn determinant<N : num, T : fa_f32_f16>(mat<N, N, T>) -> T */
     /* num overloads */ 1,
     /* 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 */
+    /* fn distance<T : fa_f32_f16>(T, T) -> T */
+    /* fn distance<N : num, T : fa_f32_f16>(vec<N, T>, vec<N, T>) -> T */
     /* num overloads */ 2,
     /* overloads */ &kOverloads[323],
   },
@@ -14197,15 +14197,15 @@
   },
   {
     /* [31] */
-    /* fn exp<T : f32_f16>(T) -> T */
-    /* fn exp<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
+    /* fn exp<T : fa_f32_f16>(T) -> T */
+    /* fn exp<N : num, T : fa_f32_f16>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
     /* overloads */ &kOverloads[337],
   },
   {
     /* [32] */
-    /* fn exp2<T : f32_f16>(T) -> T */
-    /* fn exp2<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
+    /* fn exp2<T : fa_f32_f16>(T) -> T */
+    /* fn exp2<N : num, T : fa_f32_f16>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
     /* overloads */ &kOverloads[339],
   },
@@ -14245,8 +14245,8 @@
   },
   {
     /* [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> */
+    /* fn fma<T : fa_f32_f16>(T, T, T) -> T */
+    /* fn fma<N : num, T : fa_f32_f16>(vec<N, T>, vec<N, T>, vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
     /* overloads */ &kOverloads[349],
   },
@@ -14259,8 +14259,8 @@
   },
   {
     /* [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> */
+    /* fn frexp<T : fa_f32_f16>(T) -> __frexp_result<T> */
+    /* fn frexp<N : num, T : fa_f32_f16>(vec<N, T>) -> __frexp_result_vec<N, T> */
     /* num overloads */ 2,
     /* overloads */ &kOverloads[353],
   },
@@ -14294,8 +14294,8 @@
   },
   {
     /* [45] */
-    /* fn inverseSqrt<T : f32_f16>(T) -> T */
-    /* fn inverseSqrt<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
+    /* fn inverseSqrt<T : fa_f32_f16>(T) -> T */
+    /* fn inverseSqrt<N : num, T : fa_f32_f16>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
     /* overloads */ &kOverloads[363],
   },
@@ -14315,15 +14315,15 @@
   },
   {
     /* [48] */
-    /* fn log<T : f32_f16>(T) -> T */
-    /* fn log<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
+    /* fn log<T : fa_f32_f16>(T) -> T */
+    /* fn log<N : num, T : fa_f32_f16>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
     /* overloads */ &kOverloads[369],
   },
   {
     /* [49] */
-    /* fn log2<T : f32_f16>(T) -> T */
-    /* fn log2<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
+    /* fn log2<T : fa_f32_f16>(T) -> T */
+    /* fn log2<N : num, T : fa_f32_f16>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
     /* overloads */ &kOverloads[371],
   },
@@ -14351,14 +14351,14 @@
   },
   {
     /* [53] */
-    /* 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> */
+    /* fn modf<T : fa_f32_f16>(@test_value(-1.5) T) -> __modf_result<T> */
+    /* fn modf<N : num, T : fa_f32_f16>(@test_value(-1.5) vec<N, T>) -> __modf_result_vec<N, T> */
     /* num overloads */ 2,
     /* overloads */ &kOverloads[377],
   },
   {
     /* [54] */
-    /* fn normalize<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
+    /* fn normalize<N : num, T : fa_f32_f16>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 1,
     /* overloads */ &kOverloads[442],
   },
@@ -14518,7 +14518,7 @@
   },
   {
     /* [78] */
-    /* fn transpose<M : num, N : num, T : f32_f16>(mat<M, N, T>) -> mat<N, M, T> */
+    /* fn transpose<M : num, N : num, T : fa_f32_f16>(mat<M, N, T>) -> mat<N, M, T> */
     /* num overloads */ 1,
     /* overloads */ &kOverloads[451],
   },
diff --git a/src/tint/resolver/materialize_test.cc b/src/tint/resolver/materialize_test.cc
index a2084a4..be8d400 100644
--- a/src/tint/resolver/materialize_test.cc
+++ b/src/tint/resolver/materialize_test.cc
@@ -1236,5 +1236,179 @@
 
 }  // namespace materialize_abstract_numeric_to_unrelated_type
 
+////////////////////////////////////////////////////////////////////////////////
+// Materialization tests for builtin-returned abstract structures
+// These are too bespoke to slot into the more general materialization tests above
+////////////////////////////////////////////////////////////////////////////////
+namespace materialize_abstract_structure {
+
+using MaterializeAbstractStructure = resolver::ResolverTest;
+
+TEST_F(MaterializeAbstractStructure, Modf_Scalar_DefaultType) {
+    // var v = modf(1);
+    auto* call = Call("modf", 1_a);
+    WrapInFunction(Decl(Var("v", call)));
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+    auto* sem = Sem().Get(call);
+    ASSERT_TRUE(sem->Is<sem::Materialize>());
+    auto* materialize = sem->As<sem::Materialize>();
+    ASSERT_TRUE(materialize->Type()->Is<sem::Struct>());
+    auto* concrete_str = materialize->Type()->As<sem::Struct>();
+    ASSERT_TRUE(concrete_str->Members()[0]->Type()->Is<sem::F32>());
+    ASSERT_TRUE(materialize->Expr()->Type()->Is<sem::Struct>());
+    auto* abstract_str = materialize->Expr()->Type()->As<sem::Struct>();
+    ASSERT_TRUE(abstract_str->Members()[0]->Type()->Is<sem::AbstractFloat>());
+}
+
+TEST_F(MaterializeAbstractStructure, Modf_Vector_DefaultType) {
+    // var v = modf(vec2(1));
+    auto* call = Call("modf", Construct(ty.vec2(nullptr), 1_a));
+    WrapInFunction(Decl(Var("v", call)));
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+    auto* sem = Sem().Get(call);
+    ASSERT_TRUE(sem->Is<sem::Materialize>());
+    auto* materialize = sem->As<sem::Materialize>();
+    ASSERT_TRUE(materialize->Type()->Is<sem::Struct>());
+    auto* concrete_str = materialize->Type()->As<sem::Struct>();
+    ASSERT_TRUE(concrete_str->Members()[0]->Type()->Is<sem::Vector>());
+    ASSERT_TRUE(concrete_str->Members()[0]->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
+    ASSERT_TRUE(materialize->Expr()->Type()->Is<sem::Struct>());
+    auto* abstract_str = materialize->Expr()->Type()->As<sem::Struct>();
+    ASSERT_TRUE(abstract_str->Members()[0]->Type()->Is<sem::Vector>());
+    ASSERT_TRUE(
+        abstract_str->Members()[0]->Type()->As<sem::Vector>()->type()->Is<sem::AbstractFloat>());
+}
+
+TEST_F(MaterializeAbstractStructure, Modf_Scalar_ExplicitType) {
+    // var v = modf(1_h); // v is __modf_result_f16
+    // v = modf(1);       // __modf_result_f16 <- __modf_result_abstract
+    Enable(ast::Extension::kF16);
+    auto* call = Call("modf", 1_a);
+    WrapInFunction(Decl(Var("v", Call("modf", 1_h))),  //
+                   Assign("v", call));
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+    auto* sem = Sem().Get(call);
+    ASSERT_TRUE(sem->Is<sem::Materialize>());
+    auto* materialize = sem->As<sem::Materialize>();
+    ASSERT_TRUE(materialize->Type()->Is<sem::Struct>());
+    auto* concrete_str = materialize->Type()->As<sem::Struct>();
+    ASSERT_TRUE(concrete_str->Members()[0]->Type()->Is<sem::F16>());
+    ASSERT_TRUE(materialize->Expr()->Type()->Is<sem::Struct>());
+    auto* abstract_str = materialize->Expr()->Type()->As<sem::Struct>();
+    ASSERT_TRUE(abstract_str->Members()[0]->Type()->Is<sem::AbstractFloat>());
+}
+
+TEST_F(MaterializeAbstractStructure, Modf_Vector_ExplicitType) {
+    // var v = modf(vec2(1_h)); // v is __modf_result_vec2_f16
+    // v = modf(vec2(1));       // __modf_result_vec2_f16 <- __modf_result_vec2_abstract
+    Enable(ast::Extension::kF16);
+    auto* call = Call("modf", Construct(ty.vec2(nullptr), 1_a));
+    WrapInFunction(Decl(Var("v", Call("modf", Construct(ty.vec2(nullptr), 1_h)))),
+                   Assign("v", call));
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+    auto* sem = Sem().Get(call);
+    ASSERT_TRUE(sem->Is<sem::Materialize>());
+    auto* materialize = sem->As<sem::Materialize>();
+    ASSERT_TRUE(materialize->Type()->Is<sem::Struct>());
+    auto* concrete_str = materialize->Type()->As<sem::Struct>();
+    ASSERT_TRUE(concrete_str->Members()[0]->Type()->Is<sem::Vector>());
+    ASSERT_TRUE(concrete_str->Members()[0]->Type()->As<sem::Vector>()->type()->Is<sem::F16>());
+    ASSERT_TRUE(materialize->Expr()->Type()->Is<sem::Struct>());
+    auto* abstract_str = materialize->Expr()->Type()->As<sem::Struct>();
+    ASSERT_TRUE(abstract_str->Members()[0]->Type()->Is<sem::Vector>());
+    ASSERT_TRUE(
+        abstract_str->Members()[0]->Type()->As<sem::Vector>()->type()->Is<sem::AbstractFloat>());
+}
+
+TEST_F(MaterializeAbstractStructure, Frexp_Scalar_DefaultType) {
+    // var v = frexp(1);
+    auto* call = Call("frexp", 1_a);
+    WrapInFunction(Decl(Var("v", call)));
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+    auto* sem = Sem().Get(call);
+    ASSERT_TRUE(sem->Is<sem::Materialize>());
+    auto* materialize = sem->As<sem::Materialize>();
+    ASSERT_TRUE(materialize->Type()->Is<sem::Struct>());
+    auto* concrete_str = materialize->Type()->As<sem::Struct>();
+    ASSERT_TRUE(concrete_str->Members()[0]->Type()->Is<sem::F32>());
+    ASSERT_TRUE(concrete_str->Members()[1]->Type()->Is<sem::I32>());
+    ASSERT_TRUE(materialize->Expr()->Type()->Is<sem::Struct>());
+    auto* abstract_str = materialize->Expr()->Type()->As<sem::Struct>();
+    ASSERT_TRUE(abstract_str->Members()[0]->Type()->Is<sem::AbstractFloat>());
+    ASSERT_TRUE(abstract_str->Members()[1]->Type()->Is<sem::AbstractInt>());
+}
+
+TEST_F(MaterializeAbstractStructure, Frexp_Vector_DefaultType) {
+    // var v = frexp(vec2(1));
+    auto* call = Call("frexp", Construct(ty.vec2(nullptr), 1_a));
+    WrapInFunction(Decl(Var("v", call)));
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+    auto* sem = Sem().Get(call);
+    ASSERT_TRUE(sem->Is<sem::Materialize>());
+    auto* materialize = sem->As<sem::Materialize>();
+    ASSERT_TRUE(materialize->Type()->Is<sem::Struct>());
+    auto* concrete_str = materialize->Type()->As<sem::Struct>();
+    ASSERT_TRUE(concrete_str->Members()[0]->Type()->Is<sem::Vector>());
+    ASSERT_TRUE(concrete_str->Members()[1]->Type()->Is<sem::Vector>());
+    ASSERT_TRUE(concrete_str->Members()[0]->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
+    ASSERT_TRUE(concrete_str->Members()[1]->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
+    ASSERT_TRUE(materialize->Expr()->Type()->Is<sem::Struct>());
+    auto* abstract_str = materialize->Expr()->Type()->As<sem::Struct>();
+    ASSERT_TRUE(abstract_str->Members()[0]->Type()->Is<sem::Vector>());
+    ASSERT_TRUE(
+        abstract_str->Members()[0]->Type()->As<sem::Vector>()->type()->Is<sem::AbstractFloat>());
+    ASSERT_TRUE(
+        abstract_str->Members()[1]->Type()->As<sem::Vector>()->type()->Is<sem::AbstractInt>());
+}
+
+TEST_F(MaterializeAbstractStructure, Frexp_Scalar_ExplicitType) {
+    // var v = frexp(1_h); // v is __frexp_result_f16
+    // v = frexp(1);       // __frexp_result_f16 <- __frexp_result_abstract
+    Enable(ast::Extension::kF16);
+    auto* call = Call("frexp", 1_a);
+    WrapInFunction(Decl(Var("v", Call("frexp", 1_h))),  //
+                   Assign("v", call));
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+    auto* sem = Sem().Get(call);
+    ASSERT_TRUE(sem->Is<sem::Materialize>());
+    auto* materialize = sem->As<sem::Materialize>();
+    ASSERT_TRUE(materialize->Type()->Is<sem::Struct>());
+    auto* concrete_str = materialize->Type()->As<sem::Struct>();
+    ASSERT_TRUE(concrete_str->Members()[0]->Type()->Is<sem::F16>());
+    ASSERT_TRUE(concrete_str->Members()[1]->Type()->Is<sem::I32>());
+    ASSERT_TRUE(materialize->Expr()->Type()->Is<sem::Struct>());
+    auto* abstract_str = materialize->Expr()->Type()->As<sem::Struct>();
+    ASSERT_TRUE(abstract_str->Members()[0]->Type()->Is<sem::AbstractFloat>());
+    ASSERT_TRUE(abstract_str->Members()[1]->Type()->Is<sem::AbstractInt>());
+}
+
+TEST_F(MaterializeAbstractStructure, Frexp_Vector_ExplicitType) {
+    // var v = frexp(vec2(1_h)); // v is __frexp_result_vec2_f16
+    // v = frexp(vec2(1));       // __frexp_result_vec2_f16 <- __frexp_result_vec2_abstract
+    Enable(ast::Extension::kF16);
+    auto* call = Call("frexp", Construct(ty.vec2(nullptr), 1_a));
+    WrapInFunction(Decl(Var("v", Call("frexp", Construct(ty.vec2(nullptr), 1_h)))),
+                   Assign("v", call));
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+    auto* sem = Sem().Get(call);
+    ASSERT_TRUE(sem->Is<sem::Materialize>());
+    auto* materialize = sem->As<sem::Materialize>();
+    ASSERT_TRUE(materialize->Type()->Is<sem::Struct>());
+    auto* concrete_str = materialize->Type()->As<sem::Struct>();
+    ASSERT_TRUE(concrete_str->Members()[0]->Type()->Is<sem::Vector>());
+    ASSERT_TRUE(concrete_str->Members()[1]->Type()->Is<sem::Vector>());
+    ASSERT_TRUE(concrete_str->Members()[0]->Type()->As<sem::Vector>()->type()->Is<sem::F16>());
+    ASSERT_TRUE(concrete_str->Members()[1]->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
+    ASSERT_TRUE(materialize->Expr()->Type()->Is<sem::Struct>());
+    auto* abstract_str = materialize->Expr()->Type()->As<sem::Struct>();
+    ASSERT_TRUE(abstract_str->Members()[0]->Type()->Is<sem::Vector>());
+    ASSERT_TRUE(
+        abstract_str->Members()[0]->Type()->As<sem::Vector>()->type()->Is<sem::AbstractFloat>());
+    ASSERT_TRUE(
+        abstract_str->Members()[1]->Type()->As<sem::Vector>()->type()->Is<sem::AbstractInt>());
+}
+
+}  // namespace materialize_abstract_structure
+
 }  // namespace
 }  // namespace tint::resolver
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index 143334e..0fdd78c 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -1726,6 +1726,12 @@
                 return Array(source, source, el_ty, a->Count(), /* explicit_stride */ 0);
             }
             return nullptr;
+        },
+        [&](const sem::Struct* s) -> const sem::Type* {
+            if (auto& tys = s->ConcreteTypes(); !tys.IsEmpty()) {
+                return target_ty ? target_ty : tys[0];
+            }
+            return nullptr;
         });
 }
 
@@ -1938,8 +1944,8 @@
     bool has_side_effects =
         std::any_of(args.begin(), args.end(), [](auto* e) { return e->HasSideEffects(); });
 
-    // ct_init_or_conv is a helper for building either a sem::TypeInitializer or sem::TypeConversion
-    // call for a InitConvIntrinsic with an optional template argument type.
+    // ct_init_or_conv is a helper for building either a sem::TypeInitializer or
+    // sem::TypeConversion call for a InitConvIntrinsic with an optional template argument type.
     auto ct_init_or_conv = [&](InitConvIntrinsic ty, const sem::Type* template_arg) -> sem::Call* {
         auto arg_tys = utils::Transform(args, [](auto* arg) { return arg->Type(); });
         auto ctor_or_conv =
@@ -1987,8 +1993,8 @@
             if (!value) {
                 // Constant evaluation failed.
                 // Can happen for expressions that will fail validation (later).
-                // Use the kRuntime EvaluationStage, as kConstant will trigger an assertion in the
-                // sem::Expression initializer, which checks that kConstant is paired with a
+                // Use the kRuntime EvaluationStage, as kConstant will trigger an assertion in
+                // the sem::Expression initializer, which checks that kConstant is paired with a
                 // constant value.
                 stage = sem::EvaluationStage::kRuntime;
             }
@@ -1998,8 +2004,8 @@
                                            current_statement_, value, has_side_effects);
     };
 
-    // ty_init_or_conv is a helper for building either a sem::TypeInitializer or sem::TypeConversion
-    // call for the given semantic type.
+    // ty_init_or_conv is a helper for building either a sem::TypeInitializer or
+    // sem::TypeConversion call for the given semantic type.
     auto ty_init_or_conv = [&](const sem::Type* ty) {
         return Switch(
             ty,  //
@@ -2076,7 +2082,8 @@
             });
     };
 
-    // ast::CallExpression has a target which is either an ast::Type or an ast::IdentifierExpression
+    // ast::CallExpression has a target which is either an ast::Type or an
+    // ast::IdentifierExpression
     sem::Call* call = nullptr;
     if (expr->target.type) {
         // ast::CallExpression has an ast::Type as the target.
@@ -2188,7 +2195,8 @@
             });
     } else {
         // ast::CallExpression has an ast::IdentifierExpression as the target.
-        // This call is either a function call, builtin call, type initializer or type conversion.
+        // This call is either a function call, builtin call, type initializer or type
+        // conversion.
         auto* ident = expr->target.name;
         Mark(ident);
         auto* resolved = sem_.ResolvedSymbol(ident);
@@ -2197,8 +2205,8 @@
             [&](sem::Type* ty) {
                 // A type initializer or conversions.
                 // Note: Unlike the code path where we're resolving the call target from an
-                // ast::Type, all types must already have the element type explicitly specified, so
-                // there's no need to infer element types.
+                // ast::Type, all types must already have the element type explicitly specified,
+                // so there's no need to infer element types.
                 return ty_init_or_conv(ty);
             },
             [&](sem::Function* func) { return FunctionCall(expr, func, args, arg_behaviors); },
@@ -2264,7 +2272,8 @@
         AddWarning("use of deprecated builtin", expr->source);
     }
 
-    // If the builtin is @const, and all arguments have constant values, evaluate the builtin now.
+    // If the builtin is @const, and all arguments have constant values, evaluate the builtin
+    // now.
     auto stage = sem::EarliestStage(arg_stage, builtin.sem->Stage());
     const sem::Constant* value = nullptr;
     if (stage == sem::EvaluationStage::kConstant) {
@@ -2472,7 +2481,7 @@
                 if (loop_block->FirstContinue()) {
                     // If our identifier is in loop_block->decls, make sure its index is
                     // less than first_continue
-                    if (auto* decl = loop_block->Decls().Find(symbol)) {
+                    if (auto decl = loop_block->Decls().Find(symbol)) {
                         if (decl->order >= loop_block->NumDeclsAtFirstContinue()) {
                             AddError("continue statement bypasses declaration of '" +
                                          builder_->Symbols().NameFor(symbol) + "'",
@@ -2894,7 +2903,8 @@
         }
     }
 
-    // Track the pipeline-overridable constants that are transitively referenced by this array type.
+    // Track the pipeline-overridable constants that are transitively referenced by this array
+    // type.
     for (auto* var : transitively_referenced_overrides) {
         out->AddTransitivelyReferencedOverride(var);
     }
@@ -2955,8 +2965,9 @@
         Mark(attr);
         if (auto* sd = attr->As<ast::StrideAttribute>()) {
             // 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.
+            // 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())) {
diff --git a/src/tint/resolver/resolver_test_helper.h b/src/tint/resolver/resolver_test_helper.h
index 57fe14a..edbc456 100644
--- a/src/tint/resolver/resolver_test_helper.h
+++ b/src/tint/resolver/resolver_test_helper.h
@@ -151,6 +151,12 @@
 using mat3x2 = mat<3, 2, T>;
 
 template <typename T>
+using mat2x4 = mat<2, 4, T>;
+
+template <typename T>
+using mat4x2 = mat<4, 2, T>;
+
+template <typename T>
 using mat3x3 = mat<3, 3, T>;
 
 template <typename T>
@@ -235,12 +241,21 @@
 using sem_type_func_ptr = const sem::Type* (*)(ProgramBuilder& b);
 using type_name_func_ptr = std::string (*)();
 
+struct UnspecializedElementType {};
+
+/// Base template for DataType, specialized below.
 template <typename T>
-struct DataType {};
+struct DataType {
+    /// The element type
+    using ElementType = UnspecializedElementType;
+};
 
 /// Helper that represents no-type. Returns nullptr for all static methods.
 template <>
 struct DataType<void> {
+    /// The element type
+    using ElementType = void;
+
     /// @return nullptr
     static inline const ast::Type* AST(ProgramBuilder&) { return nullptr; }
     /// @return nullptr
@@ -756,7 +771,13 @@
             DataType<T>::Name};
 }
 
-/// Base class for Value<T>
+/// True if DataType<T> is specialized for T, false otherwise.
+template <typename T>
+const bool IsDataTypeSpecializedFor =
+    !std::is_same_v<typename DataType<T>::ElementType, UnspecializedElementType>;
+
+namespace detail {
+/// ValueBase is a base class of ConcreteValue<T>
 struct ValueBase {
     /// Constructor
     ValueBase() = default;
@@ -787,13 +808,12 @@
     virtual std::ostream& Print(std::ostream& o) const = 0;
 };
 
-/// Value<T> is an instance of a value of type DataType<T>. Useful for storing values to create
-/// expressions with.
+/// ConcreteValue<T> is used to create Values of type DataType<T> with a ScalarArgs initializer.
 template <typename T>
-struct Value : ValueBase {
+struct ConcreteValue : ValueBase {
     /// Constructor
-    /// @param a the scalar args
-    explicit Value(ScalarArgs a) : args(std::move(a)) {}
+    /// @param args the scalar args
+    explicit ConcreteValue(ScalarArgs args) : args_(std::move(args)) {}
 
     /// Alias to T
     using Type = T;
@@ -802,21 +822,16 @@
     /// Alias to DataType::ElementType
     using ElementType = typename DataType::ElementType;
 
-    /// Creates a Value<T> with `args`
-    /// @param args the args that will be passed to the expression
-    /// @returns a Value<T>
-    static Value Create(ScalarArgs args) { return Value{std::move(args)}; }
-
     /// Creates an `ast::Expression` for the type T passing in previously stored args
     /// @param b the ProgramBuilder
     /// @returns an expression node
     const ast::Expression* Expr(ProgramBuilder& b) const override {
         auto create = CreatePtrsFor<T>();
-        return (*create.expr)(b, args);
+        return (*create.expr)(b, args_);
     }
 
     /// @returns args used to create expression via `Expr`
-    const ScalarArgs& Args() const override { return args; }
+    const ScalarArgs& Args() const override { return args_; }
 
     /// @returns true if element type is abstract
     bool IsAbstract() const override { return tint::IsAbstract<ElementType>; }
@@ -832,9 +847,9 @@
     /// @returns input argument `o`
     std::ostream& Print(std::ostream& o) const override {
         o << TypeName() << "(";
-        for (auto& a : args.values) {
+        for (auto& a : args_.values) {
             o << std::get<ElementType>(a);
-            if (&a != &args.values.Back()) {
+            if (&a != &args_.values.Back()) {
                 o << ", ";
             }
         }
@@ -842,60 +857,95 @@
         return o;
     }
 
+  private:
     /// args to create expression with
-    ScalarArgs args;
+    ScalarArgs args_;
 };
-
-namespace detail {
-/// Base template for IsValue
-template <typename T>
-struct IsValue : std::false_type {};
-/// Specialization for IsValue
-template <typename T>
-struct IsValue<Value<T>> : std::true_type {};
 }  // namespace detail
 
-/// True if T is of type Value
-template <typename T>
-constexpr bool IsValue = detail::IsValue<T>::value;
+/// A Value represents a value of type DataType<T> created with ScalarArgs. Useful for storing
+/// values for unit tests.
+class Value {
+  public:
+    /// Creates a Value for type T initialized with `args`
+    /// @param args the scalar args
+    /// @returns Value
+    template <typename T>
+    static Value Create(ScalarArgs args) {
+        static_assert(IsDataTypeSpecializedFor<T>, "No DataType<T> specialization exists");
+        return Value{std::make_shared<detail::ConcreteValue<T>>(std::move(args))};
+    }
 
-/// Returns the friendly name of ValueT
-template <typename ValueT, typename = traits::EnableIf<IsValue<ValueT>>>
-const char* FriendlyName() {
-    return tint::FriendlyName<typename ValueT::ElementType>();
+    /// Creates an `ast::Expression` for the type T passing in previously stored args
+    /// @param b the ProgramBuilder
+    /// @returns an expression node
+    const ast::Expression* Expr(ProgramBuilder& b) const { return value_->Expr(b); }
+
+    /// @returns args used to create expression via `Expr`
+    const ScalarArgs& Args() const { return value_->Args(); }
+
+    /// @returns true if element type is abstract
+    bool IsAbstract() const { return value_->IsAbstract(); }
+
+    /// @returns true if element type is an integral
+    bool IsIntegral() const { return value_->IsIntegral(); }
+
+    /// @returns element type name
+    std::string TypeName() const { return value_->TypeName(); }
+
+    /// Prints this value to the output stream
+    /// @param o the output stream
+    /// @returns input argument `o`
+    std::ostream& Print(std::ostream& o) const { return value_->Print(o); }
+
+  private:
+    /// Private constructor
+    explicit Value(std::shared_ptr<const detail::ValueBase> value) : value_(std::move(value)) {}
+
+    /// Shared pointer to an immutable value. This type-erasure pattern allows Value to wrap a
+    /// polymorphic type, while being used like a value-type (i.e. copyable).
+    std::shared_ptr<const detail::ValueBase> value_;
+};
+
+/// Prints Value to ostream
+inline std::ostream& operator<<(std::ostream& o, const Value& value) {
+    return value.Print(o);
 }
 
-/// Creates a `Value<T>` from a scalar `v`
+/// True if T is Value, false otherwise
 template <typename T>
-auto Val(T v) {
-    return Value<T>::Create(ScalarArgs{v});
+constexpr bool IsValue = std::is_same_v<T, Value>;
+
+/// Creates a Value of DataType<T> from a scalar `v`
+template <typename T>
+Value Val(T v) {
+    return Value::Create<T>(ScalarArgs{v});
 }
 
-/// Creates a `Value<vec<N, T>>` from N scalar `args`
+/// Creates a Value of DataType<vec<N, T>> from N scalar `args`
 template <typename... T>
-auto Vec(T... args) {
-    constexpr size_t N = sizeof...(args);
+Value Vec(T... args) {
     using FirstT = std::tuple_element_t<0, std::tuple<T...>>;
+    constexpr size_t N = sizeof...(args);
     utils::Vector v{args...};
-    using VT = vec<N, FirstT>;
-    return Value<VT>::Create(utils::VectorRef<FirstT>{v});
+    return Value::Create<vec<N, FirstT>>(utils::VectorRef<FirstT>{v});
 }
 
-/// Creates a `Value<mat<C,R,T>` from C*R scalar `args`
+/// Creates a Value of DataType<mat<C,R,T> from C*R scalar `args`
 template <size_t C, size_t R, typename T>
-auto Mat(const T (&m_in)[C][R]) {
+Value Mat(const T (&m_in)[C][R]) {
     utils::Vector<T, C * R> m;
     for (uint32_t i = 0; i < C; ++i) {
         for (size_t j = 0; j < R; ++j) {
             m.Push(m_in[i][j]);
         }
     }
-    return Value<mat<C, R, T>>::Create(utils::VectorRef<T>{m});
+    return Value::Create<mat<C, R, T>>(utils::VectorRef<T>{m});
 }
 
-/// Creates a `Value<mat<2,R,T>` from column vectors `c0` and `c1`
+/// Creates a Value of DataType<mat<2,R,T> from column vectors `c0` and `c1`
 template <typename T, size_t R>
-auto Mat(const T (&c0)[R], const T (&c1)[R]) {
+Value Mat(const T (&c0)[R], const T (&c1)[R]) {
     constexpr size_t C = 2;
     utils::Vector<T, C * R> m;
     for (auto v : c0) {
@@ -904,12 +954,12 @@
     for (auto v : c1) {
         m.Push(v);
     }
-    return Value<mat<C, R, T>>::Create(utils::VectorRef<T>{m});
+    return Value::Create<mat<C, R, T>>(utils::VectorRef<T>{m});
 }
 
-/// Creates a `Value<mat<3,R,T>` from column vectors `c0`, `c1`, and `c2`
+/// Creates a Value of DataType<mat<3,R,T> from column vectors `c0`, `c1`, and `c2`
 template <typename T, size_t R>
-auto Mat(const T (&c0)[R], const T (&c1)[R], const T (&c2)[R]) {
+Value Mat(const T (&c0)[R], const T (&c1)[R], const T (&c2)[R]) {
     constexpr size_t C = 3;
     utils::Vector<T, C * R> m;
     for (auto v : c0) {
@@ -921,12 +971,12 @@
     for (auto v : c2) {
         m.Push(v);
     }
-    return Value<mat<C, R, T>>::Create(utils::VectorRef<T>{m});
+    return Value::Create<mat<C, R, T>>(utils::VectorRef<T>{m});
 }
 
-/// Creates a `Value<mat<4,R,T>` from column vectors `c0`, `c1`, `c2`, and `c3`
+/// Creates a Value of DataType<mat<4,R,T> from column vectors `c0`, `c1`, `c2`, and `c3`
 template <typename T, size_t R>
-auto Mat(const T (&c0)[R], const T (&c1)[R], const T (&c2)[R], const T (&c3)[R]) {
+Value Mat(const T (&c0)[R], const T (&c1)[R], const T (&c2)[R], const T (&c3)[R]) {
     constexpr size_t C = 4;
     utils::Vector<T, C * R> m;
     for (auto v : c0) {
@@ -941,7 +991,7 @@
     for (auto v : c3) {
         m.Push(v);
     }
-    return Value<mat<C, R, T>>::Create(utils::VectorRef<T>{m});
+    return Value::Create<mat<C, R, T>>(utils::VectorRef<T>{m});
 }
 
 }  // namespace builder
diff --git a/src/tint/resolver/sem_helper.h b/src/tint/resolver/sem_helper.h
index 12ef4a2..2e557d9 100644
--- a/src/tint/resolver/sem_helper.h
+++ b/src/tint/resolver/sem_helper.h
@@ -54,7 +54,7 @@
     /// @param node the node to retrieve
     template <typename SEM = sem::Node>
     SEM* ResolvedSymbol(const ast::Node* node) const {
-        auto* resolved = dependencies_.resolved_symbols.Find(node);
+        auto resolved = dependencies_.resolved_symbols.Find(node);
         return resolved ? const_cast<SEM*>(builder_->Sem().Get<SEM>(*resolved)) : nullptr;
     }
 
diff --git a/src/tint/resolver/uniformity.cc b/src/tint/resolver/uniformity.cc
index afdbdfb..ffedaf6 100644
--- a/src/tint/resolver/uniformity.cc
+++ b/src/tint/resolver/uniformity.cc
@@ -600,7 +600,7 @@
                     }
 
                     // Add an edge from the variable's loop input node to its value at this point.
-                    auto** in_node = info.var_in_nodes.Find(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) {
@@ -1334,7 +1334,7 @@
             [&](const sem::Function* func) {
                 // We must have already analyzed the user-defined function since we process
                 // functions in dependency order.
-                auto* info = functions_.Find(func->Declaration());
+                auto info = functions_.Find(func->Declaration());
                 TINT_ASSERT(Resolver, info != nullptr);
                 callsite_tag = info->callsite_tag;
                 function_tag = info->function_tag;
@@ -1466,7 +1466,7 @@
         } 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_.Find(user->Declaration());
+            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>();
@@ -1636,7 +1636,7 @@
             // 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_.Find(user->Declaration());
+                auto next_function = functions_.Find(user->Declaration());
                 Node* next_cause = next_function->parameters[cause->arg_index].init_value;
                 MakeError(*next_function, next_cause, true);
             }
diff --git a/src/tint/resolver/validation_test.cc b/src/tint/resolver/validation_test.cc
index 2e41bf1..946bcb9 100644
--- a/src/tint/resolver/validation_test.cc
+++ b/src/tint/resolver/validation_test.cc
@@ -1298,6 +1298,23 @@
     EXPECT_EQ(r()->error(), "12:24 error: value 4294967296 cannot be represented as 'u32'");
 }
 
+//    var a: array<i32,2>;
+//    *&a[0] = 1;
+TEST_F(ResolverTest, PointerIndexing_Fail) {
+    // var a: array<i32,2>;
+    // let p = &a;
+    // *p[0] = 0;
+
+    auto* a = Var("a", ty.array<i32, 2>());
+    auto* p = AddressOf("a");
+    auto* idx = Assign(Deref(IndexAccessor(p, 0_u)), 0_u);
+
+    WrapInFunction(a, idx);
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(), "error: cannot index type 'ptr<function, array<i32, 2>, read_write>'");
+}
+
 }  // namespace
 }  // namespace tint::resolver
 
diff --git a/src/tint/resolver/validator.cc b/src/tint/resolver/validator.cc
index db93bbe..70d9f8e 100644
--- a/src/tint/resolver/validator.cc
+++ b/src/tint/resolver/validator.cc
@@ -395,13 +395,11 @@
         return true;
     }
 
-    // Temporally forbid using f16 types in "uniform" and "storage" address space.
-    // TODO(tint:1473, tint:1502): Remove this error after f16 is supported in "uniform" and
-    // "storage" address space but keep for "push_constant" address space.
-    if (Is<sem::F16>(sem::Type::DeepestElementOf(store_ty))) {
-        AddError("using f16 types in '" + utils::ToString(address_space) +
-                     "' address space is not implemented yet",
-                 source);
+    // Among three host-shareable address spaces, f16 is supported in "uniform" and
+    // "storage" address space, but not "push_constant" address space yet.
+    if (Is<sem::F16>(sem::Type::DeepestElementOf(store_ty)) &&
+        address_space == ast::AddressSpace::kPushConstant) {
+        AddError("using f16 types in 'push_constant' address space is not implemented yet", source);
         return false;
     }
 
@@ -719,7 +717,7 @@
             return false;
         }
     } else if (type->IsAnyOf<sem::Struct, sem::Array>()) {
-        if (auto* found = atomic_composite_info.Find(type)) {
+        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",
@@ -798,7 +796,7 @@
     for (auto* attr : decl->attributes) {
         if (attr->Is<ast::IdAttribute>()) {
             auto id = v->OverrideId();
-            if (auto* var = override_ids.Find(id); var && *var != 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) +
diff --git a/src/tint/scope_stack.h b/src/tint/scope_stack.h
index a2da4dd..75c50b4 100644
--- a/src/tint/scope_stack.h
+++ b/src/tint/scope_stack.h
@@ -44,7 +44,7 @@
     /// stack, otherwise the zero initializer for type T.
     V Set(const K& key, V val) {
         auto& back = stack_.Back();
-        if (auto* el = back.Find(key)) {
+        if (auto el = back.Find(key)) {
             std::swap(val, *el);
             return val;
         }
@@ -57,7 +57,7 @@
     /// @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) {
-            if (auto* val = iter->Find(key)) {
+            if (auto val = iter->Find(key)) {
                 return *val;
             }
         }
diff --git a/src/tint/sem/struct.h b/src/tint/sem/struct.h
index f35d5f4..0ddfa23 100644
--- a/src/tint/sem/struct.h
+++ b/src/tint/sem/struct.h
@@ -152,6 +152,14 @@
     /// including size and alignment information.
     std::string Layout(const tint::SymbolTable& symbols) const;
 
+    /// @param concrete the conversion-rank ordered concrete versions of this abstract structure.
+    void SetConcreteTypes(utils::VectorRef<const Struct*> concrete) { concrete_types_ = concrete; }
+
+    /// @returns the conversion-rank ordered concrete versions of this abstract structure, or an
+    /// empty vector if this structure is not abstract.
+    /// @note only structures returned by builtins may be abstract (e.g. modf, frexp)
+    const utils::Vector<const Struct*, 2>& ConcreteTypes() const { return concrete_types_; }
+
   private:
     ast::Struct const* const declaration_;
     const Symbol name_;
@@ -161,6 +169,7 @@
     const uint32_t size_no_padding_;
     std::unordered_set<ast::AddressSpace> address_space_usage_;
     std::unordered_set<PipelineStageUsage> pipeline_stage_uses_;
+    utils::Vector<const Struct*, 2> concrete_types_;
 };
 
 /// StructMember holds the semantic information for structure members.
diff --git a/src/tint/sem/type.cc b/src/tint/sem/type.cc
index 0596a37..f9db53b 100644
--- a/src/tint/sem/type.cc
+++ b/src/tint/sem/type.cc
@@ -238,6 +238,15 @@
             }
             return kNoConversion;
         },
+        [&](const Struct* from_str) {
+            auto& concrete_tys = from_str->ConcreteTypes();
+            for (size_t i = 0; i < concrete_tys.Length(); i++) {
+                if (concrete_tys[i] == to) {
+                    return static_cast<uint32_t>(i + 1);
+                }
+            }
+            return kNoConversion;
+        },
         [&](Default) { return kNoConversion; });
 }
 
diff --git a/src/tint/sem/type_test.cc b/src/tint/sem/type_test.cc
index ee79102..aec9bde 100644
--- a/src/tint/sem/type_test.cc
+++ b/src/tint/sem/type_test.cc
@@ -44,6 +44,22 @@
     const sem::Matrix* mat4x3_af = create<Matrix>(vec3_af, 4u);
     const sem::Reference* ref_u32 =
         create<Reference>(u32, ast::AddressSpace::kPrivate, ast::Access::kReadWrite);
+    const sem::Struct* str_f32 = create<Struct>(nullptr,
+                                                Sym("str_f32"),
+                                                StructMemberList{
+                                                    create<StructMember>(
+                                                        /* declaration */ nullptr,
+                                                        /* name */ Sym("x"),
+                                                        /* type */ f32,
+                                                        /* index */ 0u,
+                                                        /* offset */ 0u,
+                                                        /* align */ 4u,
+                                                        /* size */ 4u,
+                                                        /* location */ std::nullopt),
+                                                },
+                                                /* align*/ 4u,
+                                                /* size*/ 4u,
+                                                /* size_no_padding*/ 4u);
     const sem::Struct* str_f16 = create<Struct>(nullptr,
                                                 Sym("str_f16"),
                                                 StructMemberList{
@@ -60,22 +76,22 @@
                                                 /* align*/ 4u,
                                                 /* size*/ 4u,
                                                 /* size_no_padding*/ 4u);
-    const sem::Struct* str_af = create<Struct>(nullptr,
-                                               Sym("str_af"),
-                                               StructMemberList{
-                                                   create<StructMember>(
-                                                       /* declaration */ nullptr,
-                                                       /* name */ Sym("x"),
-                                                       /* type */ af,
-                                                       /* index */ 0u,
-                                                       /* offset */ 0u,
-                                                       /* align */ 4u,
-                                                       /* size */ 4u,
-                                                       /* location */ std::nullopt),
-                                               },
-                                               /* align*/ 4u,
-                                               /* size*/ 4u,
-                                               /* size_no_padding*/ 4u);
+    sem::Struct* str_af = create<Struct>(nullptr,
+                                         Sym("str_af"),
+                                         StructMemberList{
+                                             create<StructMember>(
+                                                 /* declaration */ nullptr,
+                                                 /* name */ Sym("x"),
+                                                 /* type */ af,
+                                                 /* index */ 0u,
+                                                 /* offset */ 0u,
+                                                 /* align */ 4u,
+                                                 /* size */ 4u,
+                                                 /* location */ std::nullopt),
+                                         },
+                                         /* align*/ 4u,
+                                         /* size*/ 4u,
+                                         /* size_no_padding*/ 4u);
     const sem::Array* arr_i32 = create<Array>(
         /* element */ i32,
         /* count */ ConstantArrayCount{5u},
@@ -139,6 +155,8 @@
         /* size */ 5u * 4u,
         /* stride */ 5u * 4u,
         /* implicit_stride */ 5u * 4u);
+
+    TypeTest() { str_af->SetConcreteTypes(utils::Vector{str_f32, str_f16}); }
 };
 
 TEST_F(TypeTest, ConversionRank) {
@@ -178,6 +196,8 @@
     EXPECT_EQ(Type::ConversionRank(ai, af), 5u);
     EXPECT_EQ(Type::ConversionRank(ai, f32), 6u);
     EXPECT_EQ(Type::ConversionRank(ai, f16), 7u);
+    EXPECT_EQ(Type::ConversionRank(str_af, str_f32), 1u);
+    EXPECT_EQ(Type::ConversionRank(str_af, str_f16), 2u);
 
     EXPECT_EQ(Type::ConversionRank(i32, f32), Type::kNoConversion);
     EXPECT_EQ(Type::ConversionRank(f32, u32), Type::kNoConversion);
@@ -199,6 +219,10 @@
     EXPECT_EQ(Type::ConversionRank(af, ai), Type::kNoConversion);
     EXPECT_EQ(Type::ConversionRank(f32, ai), Type::kNoConversion);
     EXPECT_EQ(Type::ConversionRank(f16, ai), Type::kNoConversion);
+    EXPECT_EQ(Type::ConversionRank(str_f32, str_f16), Type::kNoConversion);
+    EXPECT_EQ(Type::ConversionRank(str_f16, str_f32), Type::kNoConversion);
+    EXPECT_EQ(Type::ConversionRank(str_f32, str_af), Type::kNoConversion);
+    EXPECT_EQ(Type::ConversionRank(str_f16, str_af), Type::kNoConversion);
 }
 
 TEST_F(TypeTest, ElementOf) {
diff --git a/src/tint/transform/decompose_memory_access.cc b/src/tint/transform/decompose_memory_access.cc
index 046583e..3be550c 100644
--- a/src/tint/transform/decompose_memory_access.cc
+++ b/src/tint/transform/decompose_memory_access.cc
@@ -153,6 +153,10 @@
         out = DecomposeMemoryAccess::Intrinsic::DataType::kF32;
         return true;
     }
+    if (ty->Is<sem::F16>()) {
+        out = DecomposeMemoryAccess::Intrinsic::DataType::kF16;
+        return true;
+    }
     if (auto* vec = ty->As<sem::Vector>()) {
         switch (vec->Width()) {
             case 2:
@@ -168,6 +172,10 @@
                     out = DecomposeMemoryAccess::Intrinsic::DataType::kVec2F32;
                     return true;
                 }
+                if (vec->type()->Is<sem::F16>()) {
+                    out = DecomposeMemoryAccess::Intrinsic::DataType::kVec2F16;
+                    return true;
+                }
                 break;
             case 3:
                 if (vec->type()->Is<sem::I32>()) {
@@ -182,6 +190,10 @@
                     out = DecomposeMemoryAccess::Intrinsic::DataType::kVec3F32;
                     return true;
                 }
+                if (vec->type()->Is<sem::F16>()) {
+                    out = DecomposeMemoryAccess::Intrinsic::DataType::kVec3F16;
+                    return true;
+                }
                 break;
             case 4:
                 if (vec->type()->Is<sem::I32>()) {
@@ -196,6 +208,10 @@
                     out = DecomposeMemoryAccess::Intrinsic::DataType::kVec4F32;
                     return true;
                 }
+                if (vec->type()->Is<sem::F16>()) {
+                    out = DecomposeMemoryAccess::Intrinsic::DataType::kVec4F16;
+                    return true;
+                }
                 break;
         }
         return false;
@@ -776,6 +792,9 @@
         case DataType::kI32:
             ss << "i32";
             break;
+        case DataType::kF16:
+            ss << "f16";
+            break;
         case DataType::kVec2U32:
             ss << "vec2_u32";
             break;
@@ -785,6 +804,9 @@
         case DataType::kVec2I32:
             ss << "vec2_i32";
             break;
+        case DataType::kVec2F16:
+            ss << "vec2_f16";
+            break;
         case DataType::kVec3U32:
             ss << "vec3_u32";
             break;
@@ -794,6 +816,9 @@
         case DataType::kVec3I32:
             ss << "vec3_i32";
             break;
+        case DataType::kVec3F16:
+            ss << "vec3_f16";
+            break;
         case DataType::kVec4U32:
             ss << "vec4_u32";
             break;
@@ -803,6 +828,9 @@
         case DataType::kVec4I32:
             ss << "vec4_i32";
             break;
+        case DataType::kVec4F16:
+            ss << "vec4_f16";
+            break;
     }
     return ss.str();
 }
diff --git a/src/tint/transform/decompose_memory_access.h b/src/tint/transform/decompose_memory_access.h
index 21c196b..3c620e0 100644
--- a/src/tint/transform/decompose_memory_access.h
+++ b/src/tint/transform/decompose_memory_access.h
@@ -60,15 +60,19 @@
             kU32,
             kF32,
             kI32,
+            kF16,
             kVec2U32,
             kVec2F32,
             kVec2I32,
+            kVec2F16,
             kVec3U32,
             kVec3F32,
             kVec3I32,
+            kVec3F16,
             kVec4U32,
             kVec4F32,
             kVec4I32,
+            kVec4F16,
         };
 
         /// Constructor
diff --git a/src/tint/transform/decompose_memory_access_test.cc b/src/tint/transform/decompose_memory_access_test.cc
index 581731e..ac798e0 100644
--- a/src/tint/transform/decompose_memory_access_test.cc
+++ b/src/tint/transform/decompose_memory_access_test.cc
@@ -51,192 +51,308 @@
 
 TEST_F(DecomposeMemoryAccessTest, SB_BasicLoad) {
     auto* src = R"(
+enable f16;
+
 struct SB {
-  a : i32,
-  b : u32,
-  c : f32,
-  d : vec2<i32>,
-  e : vec2<u32>,
-  f : vec2<f32>,
-  g : vec3<i32>,
-  h : vec3<u32>,
-  i : vec3<f32>,
-  j : vec4<i32>,
-  k : vec4<u32>,
-  l : vec4<f32>,
-  m : mat2x2<f32>,
-  n : mat2x3<f32>,
-  o : mat2x4<f32>,
-  p : mat3x2<f32>,
-  q : mat3x3<f32>,
-  r : mat3x4<f32>,
-  s : mat4x2<f32>,
-  t : mat4x3<f32>,
-  u : mat4x4<f32>,
-  v : array<vec3<f32>, 2>,
+  scalar_f32 : f32,
+  scalar_i32 : i32,
+  scalar_u32 : u32,
+  scalar_f16 : f16,
+  vec2_f32 : vec2<f32>,
+  vec2_i32 : vec2<i32>,
+  vec2_u32 : vec2<u32>,
+  vec2_f16 : vec2<f16>,
+  vec3_f32 : vec3<f32>,
+  vec3_i32 : vec3<i32>,
+  vec3_u32 : vec3<u32>,
+  vec3_f16 : vec3<f16>,
+  vec4_f32 : vec4<f32>,
+  vec4_i32 : vec4<i32>,
+  vec4_u32 : vec4<u32>,
+  vec4_f16 : vec4<f16>,
+  mat2x2_f32 : mat2x2<f32>,
+  mat2x3_f32 : mat2x3<f32>,
+  mat2x4_f32 : mat2x4<f32>,
+  mat3x2_f32 : mat3x2<f32>,
+  mat3x3_f32 : mat3x3<f32>,
+  mat3x4_f32 : mat3x4<f32>,
+  mat4x2_f32 : mat4x2<f32>,
+  mat4x3_f32 : mat4x3<f32>,
+  mat4x4_f32 : mat4x4<f32>,
+  mat2x2_f16 : mat2x2<f16>,
+  mat2x3_f16 : mat2x3<f16>,
+  mat2x4_f16 : mat2x4<f16>,
+  mat3x2_f16 : mat3x2<f16>,
+  mat3x3_f16 : mat3x3<f16>,
+  mat3x4_f16 : mat3x4<f16>,
+  mat4x2_f16 : mat4x2<f16>,
+  mat4x3_f16 : mat4x3<f16>,
+  mat4x4_f16 : mat4x4<f16>,
+  arr2_vec3_f32 : array<vec3<f32>, 2>,
+  arr2_vec3_f16 : array<vec3<f16>, 2>,
 };
 
 @group(0) @binding(0) var<storage, read_write> sb : SB;
 
 @compute @workgroup_size(1)
 fn main() {
-  var a : i32 = sb.a;
-  var b : u32 = sb.b;
-  var c : f32 = sb.c;
-  var d : vec2<i32> = sb.d;
-  var e : vec2<u32> = sb.e;
-  var f : vec2<f32> = sb.f;
-  var g : vec3<i32> = sb.g;
-  var h : vec3<u32> = sb.h;
-  var i : vec3<f32> = sb.i;
-  var j : vec4<i32> = sb.j;
-  var k : vec4<u32> = sb.k;
-  var l : vec4<f32> = sb.l;
-  var m : mat2x2<f32> = sb.m;
-  var n : mat2x3<f32> = sb.n;
-  var o : mat2x4<f32> = sb.o;
-  var p : mat3x2<f32> = sb.p;
-  var q : mat3x3<f32> = sb.q;
-  var r : mat3x4<f32> = sb.r;
-  var s : mat4x2<f32> = sb.s;
-  var t : mat4x3<f32> = sb.t;
-  var u : mat4x4<f32> = sb.u;
-  var v : array<vec3<f32>, 2> = sb.v;
+  var scalar_f32 : f32 = sb.scalar_f32;
+  var scalar_i32 : i32 = sb.scalar_i32;
+  var scalar_u32 : u32 = sb.scalar_u32;
+  var scalar_f16 : f16 = sb.scalar_f16;
+  var vec2_f32 : vec2<f32> = sb.vec2_f32;
+  var vec2_i32 : vec2<i32> = sb.vec2_i32;
+  var vec2_u32 : vec2<u32> = sb.vec2_u32;
+  var vec2_f16 : vec2<f16> = sb.vec2_f16;
+  var vec3_f32 : vec3<f32> = sb.vec3_f32;
+  var vec3_i32 : vec3<i32> = sb.vec3_i32;
+  var vec3_u32 : vec3<u32> = sb.vec3_u32;
+  var vec3_f16 : vec3<f16> = sb.vec3_f16;
+  var vec4_f32 : vec4<f32> = sb.vec4_f32;
+  var vec4_i32 : vec4<i32> = sb.vec4_i32;
+  var vec4_u32 : vec4<u32> = sb.vec4_u32;
+  var vec4_f16 : vec4<f16> = sb.vec4_f16;
+  var mat2x2_f32 : mat2x2<f32> = sb.mat2x2_f32;
+  var mat2x3_f32 : mat2x3<f32> = sb.mat2x3_f32;
+  var mat2x4_f32 : mat2x4<f32> = sb.mat2x4_f32;
+  var mat3x2_f32 : mat3x2<f32> = sb.mat3x2_f32;
+  var mat3x3_f32 : mat3x3<f32> = sb.mat3x3_f32;
+  var mat3x4_f32 : mat3x4<f32> = sb.mat3x4_f32;
+  var mat4x2_f32 : mat4x2<f32> = sb.mat4x2_f32;
+  var mat4x3_f32 : mat4x3<f32> = sb.mat4x3_f32;
+  var mat4x4_f32 : mat4x4<f32> = sb.mat4x4_f32;
+  var mat2x2_f16 : mat2x2<f16> = sb.mat2x2_f16;
+  var mat2x3_f16 : mat2x3<f16> = sb.mat2x3_f16;
+  var mat2x4_f16 : mat2x4<f16> = sb.mat2x4_f16;
+  var mat3x2_f16 : mat3x2<f16> = sb.mat3x2_f16;
+  var mat3x3_f16 : mat3x3<f16> = sb.mat3x3_f16;
+  var mat3x4_f16 : mat3x4<f16> = sb.mat3x4_f16;
+  var mat4x2_f16 : mat4x2<f16> = sb.mat4x2_f16;
+  var mat4x3_f16 : mat4x3<f16> = sb.mat4x3_f16;
+  var mat4x4_f16 : mat4x4<f16> = sb.mat4x4_f16;
+  var arr2_vec3_f32 : array<vec3<f32>, 2> = sb.arr2_vec3_f32;
+  var arr2_vec3_f16 : array<vec3<f16>, 2> = sb.arr2_vec3_f16;
 }
 )";
 
     auto* expect = R"(
+enable f16;
+
 struct SB {
-  a : i32,
-  b : u32,
-  c : f32,
-  d : vec2<i32>,
-  e : vec2<u32>,
-  f : vec2<f32>,
-  g : vec3<i32>,
-  h : vec3<u32>,
-  i : vec3<f32>,
-  j : vec4<i32>,
-  k : vec4<u32>,
-  l : vec4<f32>,
-  m : mat2x2<f32>,
-  n : mat2x3<f32>,
-  o : mat2x4<f32>,
-  p : mat3x2<f32>,
-  q : mat3x3<f32>,
-  r : mat3x4<f32>,
-  s : mat4x2<f32>,
-  t : mat4x3<f32>,
-  u : mat4x4<f32>,
-  v : array<vec3<f32>, 2>,
+  scalar_f32 : f32,
+  scalar_i32 : i32,
+  scalar_u32 : u32,
+  scalar_f16 : f16,
+  vec2_f32 : vec2<f32>,
+  vec2_i32 : vec2<i32>,
+  vec2_u32 : vec2<u32>,
+  vec2_f16 : vec2<f16>,
+  vec3_f32 : vec3<f32>,
+  vec3_i32 : vec3<i32>,
+  vec3_u32 : vec3<u32>,
+  vec3_f16 : vec3<f16>,
+  vec4_f32 : vec4<f32>,
+  vec4_i32 : vec4<i32>,
+  vec4_u32 : vec4<u32>,
+  vec4_f16 : vec4<f16>,
+  mat2x2_f32 : mat2x2<f32>,
+  mat2x3_f32 : mat2x3<f32>,
+  mat2x4_f32 : mat2x4<f32>,
+  mat3x2_f32 : mat3x2<f32>,
+  mat3x3_f32 : mat3x3<f32>,
+  mat3x4_f32 : mat3x4<f32>,
+  mat4x2_f32 : mat4x2<f32>,
+  mat4x3_f32 : mat4x3<f32>,
+  mat4x4_f32 : mat4x4<f32>,
+  mat2x2_f16 : mat2x2<f16>,
+  mat2x3_f16 : mat2x3<f16>,
+  mat2x4_f16 : mat2x4<f16>,
+  mat3x2_f16 : mat3x2<f16>,
+  mat3x3_f16 : mat3x3<f16>,
+  mat3x4_f16 : mat3x4<f16>,
+  mat4x2_f16 : mat4x2<f16>,
+  mat4x3_f16 : mat4x3<f16>,
+  mat4x4_f16 : mat4x4<f16>,
+  arr2_vec3_f32 : array<vec3<f32>, 2>,
+  arr2_vec3_f16 : array<vec3<f16>, 2>,
 }
 
 @group(0) @binding(0) var<storage, read_write> sb : SB;
 
+@internal(intrinsic_load_storage_f32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> f32
+
 @internal(intrinsic_load_storage_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> i32
+fn tint_symbol_1(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> i32
 
 @internal(intrinsic_load_storage_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_1(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> u32
+fn tint_symbol_2(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> u32
 
-@internal(intrinsic_load_storage_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_2(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> f32
-
-@internal(intrinsic_load_storage_vec2_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_3(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec2<i32>
-
-@internal(intrinsic_load_storage_vec2_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_4(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec2<u32>
+@internal(intrinsic_load_storage_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_3(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> f16
 
 @internal(intrinsic_load_storage_vec2_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_5(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec2<f32>
+fn tint_symbol_4(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec2<f32>
 
-@internal(intrinsic_load_storage_vec3_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_6(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec3<i32>
+@internal(intrinsic_load_storage_vec2_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_5(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec2<i32>
 
-@internal(intrinsic_load_storage_vec3_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_7(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec3<u32>
+@internal(intrinsic_load_storage_vec2_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_6(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec2<u32>
+
+@internal(intrinsic_load_storage_vec2_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_7(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec2<f16>
 
 @internal(intrinsic_load_storage_vec3_f32) @internal(disable_validation__function_has_no_body)
 fn tint_symbol_8(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec3<f32>
 
-@internal(intrinsic_load_storage_vec4_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_9(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec4<i32>
+@internal(intrinsic_load_storage_vec3_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_9(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec3<i32>
 
-@internal(intrinsic_load_storage_vec4_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_10(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec4<u32>
+@internal(intrinsic_load_storage_vec3_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_10(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec3<u32>
+
+@internal(intrinsic_load_storage_vec3_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_11(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec3<f16>
 
 @internal(intrinsic_load_storage_vec4_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_11(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec4<f32>
+fn tint_symbol_12(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec4<f32>
 
-fn tint_symbol_12(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat2x2<f32> {
-  return mat2x2<f32>(tint_symbol_5(buffer, (offset + 0u)), tint_symbol_5(buffer, (offset + 8u)));
+@internal(intrinsic_load_storage_vec4_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_13(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec4<i32>
+
+@internal(intrinsic_load_storage_vec4_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_14(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec4<u32>
+
+@internal(intrinsic_load_storage_vec4_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_15(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec4<f16>
+
+fn tint_symbol_16(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat2x2<f32> {
+  return mat2x2<f32>(tint_symbol_4(buffer, (offset + 0u)), tint_symbol_4(buffer, (offset + 8u)));
 }
 
-fn tint_symbol_13(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat2x3<f32> {
+fn tint_symbol_17(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat2x3<f32> {
   return mat2x3<f32>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 16u)));
 }
 
-fn tint_symbol_14(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat2x4<f32> {
-  return mat2x4<f32>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 16u)));
+fn tint_symbol_18(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat2x4<f32> {
+  return mat2x4<f32>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 16u)));
 }
 
-fn tint_symbol_15(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat3x2<f32> {
-  return mat3x2<f32>(tint_symbol_5(buffer, (offset + 0u)), tint_symbol_5(buffer, (offset + 8u)), tint_symbol_5(buffer, (offset + 16u)));
+fn tint_symbol_19(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat3x2<f32> {
+  return mat3x2<f32>(tint_symbol_4(buffer, (offset + 0u)), tint_symbol_4(buffer, (offset + 8u)), tint_symbol_4(buffer, (offset + 16u)));
 }
 
-fn tint_symbol_16(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat3x3<f32> {
+fn tint_symbol_20(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat3x3<f32> {
   return mat3x3<f32>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 16u)), tint_symbol_8(buffer, (offset + 32u)));
 }
 
-fn tint_symbol_17(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat3x4<f32> {
-  return mat3x4<f32>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 16u)), tint_symbol_11(buffer, (offset + 32u)));
+fn tint_symbol_21(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat3x4<f32> {
+  return mat3x4<f32>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 16u)), tint_symbol_12(buffer, (offset + 32u)));
 }
 
-fn tint_symbol_18(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat4x2<f32> {
-  return mat4x2<f32>(tint_symbol_5(buffer, (offset + 0u)), tint_symbol_5(buffer, (offset + 8u)), tint_symbol_5(buffer, (offset + 16u)), tint_symbol_5(buffer, (offset + 24u)));
+fn tint_symbol_22(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat4x2<f32> {
+  return mat4x2<f32>(tint_symbol_4(buffer, (offset + 0u)), tint_symbol_4(buffer, (offset + 8u)), tint_symbol_4(buffer, (offset + 16u)), tint_symbol_4(buffer, (offset + 24u)));
 }
 
-fn tint_symbol_19(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat4x3<f32> {
+fn tint_symbol_23(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat4x3<f32> {
   return mat4x3<f32>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 16u)), tint_symbol_8(buffer, (offset + 32u)), tint_symbol_8(buffer, (offset + 48u)));
 }
 
-fn tint_symbol_20(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat4x4<f32> {
-  return mat4x4<f32>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 16u)), tint_symbol_11(buffer, (offset + 32u)), tint_symbol_11(buffer, (offset + 48u)));
+fn tint_symbol_24(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat4x4<f32> {
+  return mat4x4<f32>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 16u)), tint_symbol_12(buffer, (offset + 32u)), tint_symbol_12(buffer, (offset + 48u)));
 }
 
-fn tint_symbol_21(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> array<vec3<f32>, 2u> {
+fn tint_symbol_25(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat2x2<f16> {
+  return mat2x2<f16>(tint_symbol_7(buffer, (offset + 0u)), tint_symbol_7(buffer, (offset + 4u)));
+}
+
+fn tint_symbol_26(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat2x3<f16> {
+  return mat2x3<f16>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 8u)));
+}
+
+fn tint_symbol_27(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat2x4<f16> {
+  return mat2x4<f16>(tint_symbol_15(buffer, (offset + 0u)), tint_symbol_15(buffer, (offset + 8u)));
+}
+
+fn tint_symbol_28(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat3x2<f16> {
+  return mat3x2<f16>(tint_symbol_7(buffer, (offset + 0u)), tint_symbol_7(buffer, (offset + 4u)), tint_symbol_7(buffer, (offset + 8u)));
+}
+
+fn tint_symbol_29(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat3x3<f16> {
+  return mat3x3<f16>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 8u)), tint_symbol_11(buffer, (offset + 16u)));
+}
+
+fn tint_symbol_30(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat3x4<f16> {
+  return mat3x4<f16>(tint_symbol_15(buffer, (offset + 0u)), tint_symbol_15(buffer, (offset + 8u)), tint_symbol_15(buffer, (offset + 16u)));
+}
+
+fn tint_symbol_31(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat4x2<f16> {
+  return mat4x2<f16>(tint_symbol_7(buffer, (offset + 0u)), tint_symbol_7(buffer, (offset + 4u)), tint_symbol_7(buffer, (offset + 8u)), tint_symbol_7(buffer, (offset + 12u)));
+}
+
+fn tint_symbol_32(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat4x3<f16> {
+  return mat4x3<f16>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 8u)), tint_symbol_11(buffer, (offset + 16u)), tint_symbol_11(buffer, (offset + 24u)));
+}
+
+fn tint_symbol_33(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat4x4<f16> {
+  return mat4x4<f16>(tint_symbol_15(buffer, (offset + 0u)), tint_symbol_15(buffer, (offset + 8u)), tint_symbol_15(buffer, (offset + 16u)), tint_symbol_15(buffer, (offset + 24u)));
+}
+
+fn tint_symbol_34(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> array<vec3<f32>, 2u> {
   var arr : array<vec3<f32>, 2u>;
-  for(var i_1 = 0u; (i_1 < 2u); i_1 = (i_1 + 1u)) {
-    arr[i_1] = tint_symbol_8(buffer, (offset + (i_1 * 16u)));
+  for(var i = 0u; (i < 2u); i = (i + 1u)) {
+    arr[i] = tint_symbol_8(buffer, (offset + (i * 16u)));
   }
   return arr;
 }
 
+fn tint_symbol_35(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> array<vec3<f16>, 2u> {
+  var arr_1 : array<vec3<f16>, 2u>;
+  for(var i_1 = 0u; (i_1 < 2u); i_1 = (i_1 + 1u)) {
+    arr_1[i_1] = tint_symbol_11(buffer, (offset + (i_1 * 8u)));
+  }
+  return arr_1;
+}
+
 @compute @workgroup_size(1)
 fn main() {
-  var a : i32 = tint_symbol(&(sb), 0u);
-  var b : u32 = tint_symbol_1(&(sb), 4u);
-  var c : f32 = tint_symbol_2(&(sb), 8u);
-  var d : vec2<i32> = tint_symbol_3(&(sb), 16u);
-  var e : vec2<u32> = tint_symbol_4(&(sb), 24u);
-  var f : vec2<f32> = tint_symbol_5(&(sb), 32u);
-  var g : vec3<i32> = tint_symbol_6(&(sb), 48u);
-  var h : vec3<u32> = tint_symbol_7(&(sb), 64u);
-  var i : vec3<f32> = tint_symbol_8(&(sb), 80u);
-  var j : vec4<i32> = tint_symbol_9(&(sb), 96u);
-  var k : vec4<u32> = tint_symbol_10(&(sb), 112u);
-  var l : vec4<f32> = tint_symbol_11(&(sb), 128u);
-  var m : mat2x2<f32> = tint_symbol_12(&(sb), 144u);
-  var n : mat2x3<f32> = tint_symbol_13(&(sb), 160u);
-  var o : mat2x4<f32> = tint_symbol_14(&(sb), 192u);
-  var p : mat3x2<f32> = tint_symbol_15(&(sb), 224u);
-  var q : mat3x3<f32> = tint_symbol_16(&(sb), 256u);
-  var r : mat3x4<f32> = tint_symbol_17(&(sb), 304u);
-  var s : mat4x2<f32> = tint_symbol_18(&(sb), 352u);
-  var t : mat4x3<f32> = tint_symbol_19(&(sb), 384u);
-  var u : mat4x4<f32> = tint_symbol_20(&(sb), 448u);
-  var v : array<vec3<f32>, 2> = tint_symbol_21(&(sb), 512u);
+  var scalar_f32 : f32 = tint_symbol(&(sb), 0u);
+  var scalar_i32 : i32 = tint_symbol_1(&(sb), 4u);
+  var scalar_u32 : u32 = tint_symbol_2(&(sb), 8u);
+  var scalar_f16 : f16 = tint_symbol_3(&(sb), 12u);
+  var vec2_f32 : vec2<f32> = tint_symbol_4(&(sb), 16u);
+  var vec2_i32 : vec2<i32> = tint_symbol_5(&(sb), 24u);
+  var vec2_u32 : vec2<u32> = tint_symbol_6(&(sb), 32u);
+  var vec2_f16 : vec2<f16> = tint_symbol_7(&(sb), 40u);
+  var vec3_f32 : vec3<f32> = tint_symbol_8(&(sb), 48u);
+  var vec3_i32 : vec3<i32> = tint_symbol_9(&(sb), 64u);
+  var vec3_u32 : vec3<u32> = tint_symbol_10(&(sb), 80u);
+  var vec3_f16 : vec3<f16> = tint_symbol_11(&(sb), 96u);
+  var vec4_f32 : vec4<f32> = tint_symbol_12(&(sb), 112u);
+  var vec4_i32 : vec4<i32> = tint_symbol_13(&(sb), 128u);
+  var vec4_u32 : vec4<u32> = tint_symbol_14(&(sb), 144u);
+  var vec4_f16 : vec4<f16> = tint_symbol_15(&(sb), 160u);
+  var mat2x2_f32 : mat2x2<f32> = tint_symbol_16(&(sb), 168u);
+  var mat2x3_f32 : mat2x3<f32> = tint_symbol_17(&(sb), 192u);
+  var mat2x4_f32 : mat2x4<f32> = tint_symbol_18(&(sb), 224u);
+  var mat3x2_f32 : mat3x2<f32> = tint_symbol_19(&(sb), 256u);
+  var mat3x3_f32 : mat3x3<f32> = tint_symbol_20(&(sb), 288u);
+  var mat3x4_f32 : mat3x4<f32> = tint_symbol_21(&(sb), 336u);
+  var mat4x2_f32 : mat4x2<f32> = tint_symbol_22(&(sb), 384u);
+  var mat4x3_f32 : mat4x3<f32> = tint_symbol_23(&(sb), 416u);
+  var mat4x4_f32 : mat4x4<f32> = tint_symbol_24(&(sb), 480u);
+  var mat2x2_f16 : mat2x2<f16> = tint_symbol_25(&(sb), 544u);
+  var mat2x3_f16 : mat2x3<f16> = tint_symbol_26(&(sb), 552u);
+  var mat2x4_f16 : mat2x4<f16> = tint_symbol_27(&(sb), 568u);
+  var mat3x2_f16 : mat3x2<f16> = tint_symbol_28(&(sb), 584u);
+  var mat3x3_f16 : mat3x3<f16> = tint_symbol_29(&(sb), 600u);
+  var mat3x4_f16 : mat3x4<f16> = tint_symbol_30(&(sb), 624u);
+  var mat4x2_f16 : mat4x2<f16> = tint_symbol_31(&(sb), 648u);
+  var mat4x3_f16 : mat4x3<f16> = tint_symbol_32(&(sb), 664u);
+  var mat4x4_f16 : mat4x4<f16> = tint_symbol_33(&(sb), 696u);
+  var arr2_vec3_f32 : array<vec3<f32>, 2> = tint_symbol_34(&(sb), 736u);
+  var arr2_vec3_f16 : array<vec3<f16>, 2> = tint_symbol_35(&(sb), 768u);
 }
 )";
 
@@ -247,192 +363,308 @@
 
 TEST_F(DecomposeMemoryAccessTest, SB_BasicLoad_OutOfOrder) {
     auto* src = R"(
+enable f16;
+
 @compute @workgroup_size(1)
 fn main() {
-  var a : i32 = sb.a;
-  var b : u32 = sb.b;
-  var c : f32 = sb.c;
-  var d : vec2<i32> = sb.d;
-  var e : vec2<u32> = sb.e;
-  var f : vec2<f32> = sb.f;
-  var g : vec3<i32> = sb.g;
-  var h : vec3<u32> = sb.h;
-  var i : vec3<f32> = sb.i;
-  var j : vec4<i32> = sb.j;
-  var k : vec4<u32> = sb.k;
-  var l : vec4<f32> = sb.l;
-  var m : mat2x2<f32> = sb.m;
-  var n : mat2x3<f32> = sb.n;
-  var o : mat2x4<f32> = sb.o;
-  var p : mat3x2<f32> = sb.p;
-  var q : mat3x3<f32> = sb.q;
-  var r : mat3x4<f32> = sb.r;
-  var s : mat4x2<f32> = sb.s;
-  var t : mat4x3<f32> = sb.t;
-  var u : mat4x4<f32> = sb.u;
-  var v : array<vec3<f32>, 2> = sb.v;
+  var scalar_f32 : f32 = sb.scalar_f32;
+  var scalar_i32 : i32 = sb.scalar_i32;
+  var scalar_u32 : u32 = sb.scalar_u32;
+  var scalar_f16 : f16 = sb.scalar_f16;
+  var vec2_f32 : vec2<f32> = sb.vec2_f32;
+  var vec2_i32 : vec2<i32> = sb.vec2_i32;
+  var vec2_u32 : vec2<u32> = sb.vec2_u32;
+  var vec2_f16 : vec2<f16> = sb.vec2_f16;
+  var vec3_f32 : vec3<f32> = sb.vec3_f32;
+  var vec3_i32 : vec3<i32> = sb.vec3_i32;
+  var vec3_u32 : vec3<u32> = sb.vec3_u32;
+  var vec3_f16 : vec3<f16> = sb.vec3_f16;
+  var vec4_f32 : vec4<f32> = sb.vec4_f32;
+  var vec4_i32 : vec4<i32> = sb.vec4_i32;
+  var vec4_u32 : vec4<u32> = sb.vec4_u32;
+  var vec4_f16 : vec4<f16> = sb.vec4_f16;
+  var mat2x2_f32 : mat2x2<f32> = sb.mat2x2_f32;
+  var mat2x3_f32 : mat2x3<f32> = sb.mat2x3_f32;
+  var mat2x4_f32 : mat2x4<f32> = sb.mat2x4_f32;
+  var mat3x2_f32 : mat3x2<f32> = sb.mat3x2_f32;
+  var mat3x3_f32 : mat3x3<f32> = sb.mat3x3_f32;
+  var mat3x4_f32 : mat3x4<f32> = sb.mat3x4_f32;
+  var mat4x2_f32 : mat4x2<f32> = sb.mat4x2_f32;
+  var mat4x3_f32 : mat4x3<f32> = sb.mat4x3_f32;
+  var mat4x4_f32 : mat4x4<f32> = sb.mat4x4_f32;
+  var mat2x2_f16 : mat2x2<f16> = sb.mat2x2_f16;
+  var mat2x3_f16 : mat2x3<f16> = sb.mat2x3_f16;
+  var mat2x4_f16 : mat2x4<f16> = sb.mat2x4_f16;
+  var mat3x2_f16 : mat3x2<f16> = sb.mat3x2_f16;
+  var mat3x3_f16 : mat3x3<f16> = sb.mat3x3_f16;
+  var mat3x4_f16 : mat3x4<f16> = sb.mat3x4_f16;
+  var mat4x2_f16 : mat4x2<f16> = sb.mat4x2_f16;
+  var mat4x3_f16 : mat4x3<f16> = sb.mat4x3_f16;
+  var mat4x4_f16 : mat4x4<f16> = sb.mat4x4_f16;
+  var arr2_vec3_f32 : array<vec3<f32>, 2> = sb.arr2_vec3_f32;
+  var arr2_vec3_f16 : array<vec3<f16>, 2> = sb.arr2_vec3_f16;
 }
 
 @group(0) @binding(0) var<storage, read_write> sb : SB;
 
 struct SB {
-  a : i32,
-  b : u32,
-  c : f32,
-  d : vec2<i32>,
-  e : vec2<u32>,
-  f : vec2<f32>,
-  g : vec3<i32>,
-  h : vec3<u32>,
-  i : vec3<f32>,
-  j : vec4<i32>,
-  k : vec4<u32>,
-  l : vec4<f32>,
-  m : mat2x2<f32>,
-  n : mat2x3<f32>,
-  o : mat2x4<f32>,
-  p : mat3x2<f32>,
-  q : mat3x3<f32>,
-  r : mat3x4<f32>,
-  s : mat4x2<f32>,
-  t : mat4x3<f32>,
-  u : mat4x4<f32>,
-  v : array<vec3<f32>, 2>,
+  scalar_f32 : f32,
+  scalar_i32 : i32,
+  scalar_u32 : u32,
+  scalar_f16 : f16,
+  vec2_f32 : vec2<f32>,
+  vec2_i32 : vec2<i32>,
+  vec2_u32 : vec2<u32>,
+  vec2_f16 : vec2<f16>,
+  vec3_f32 : vec3<f32>,
+  vec3_i32 : vec3<i32>,
+  vec3_u32 : vec3<u32>,
+  vec3_f16 : vec3<f16>,
+  vec4_f32 : vec4<f32>,
+  vec4_i32 : vec4<i32>,
+  vec4_u32 : vec4<u32>,
+  vec4_f16 : vec4<f16>,
+  mat2x2_f32 : mat2x2<f32>,
+  mat2x3_f32 : mat2x3<f32>,
+  mat2x4_f32 : mat2x4<f32>,
+  mat3x2_f32 : mat3x2<f32>,
+  mat3x3_f32 : mat3x3<f32>,
+  mat3x4_f32 : mat3x4<f32>,
+  mat4x2_f32 : mat4x2<f32>,
+  mat4x3_f32 : mat4x3<f32>,
+  mat4x4_f32 : mat4x4<f32>,
+  mat2x2_f16 : mat2x2<f16>,
+  mat2x3_f16 : mat2x3<f16>,
+  mat2x4_f16 : mat2x4<f16>,
+  mat3x2_f16 : mat3x2<f16>,
+  mat3x3_f16 : mat3x3<f16>,
+  mat3x4_f16 : mat3x4<f16>,
+  mat4x2_f16 : mat4x2<f16>,
+  mat4x3_f16 : mat4x3<f16>,
+  mat4x4_f16 : mat4x4<f16>,
+  arr2_vec3_f32 : array<vec3<f32>, 2>,
+  arr2_vec3_f16 : array<vec3<f16>, 2>,
 };
 )";
 
     auto* expect = R"(
-@internal(intrinsic_load_storage_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> i32
-
-@internal(intrinsic_load_storage_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_1(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> u32
+enable f16;
 
 @internal(intrinsic_load_storage_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_2(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> f32
+fn tint_symbol(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> f32
 
-@internal(intrinsic_load_storage_vec2_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_3(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec2<i32>
+@internal(intrinsic_load_storage_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_1(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> i32
 
-@internal(intrinsic_load_storage_vec2_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_4(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec2<u32>
+@internal(intrinsic_load_storage_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_2(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> u32
+
+@internal(intrinsic_load_storage_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_3(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> f16
 
 @internal(intrinsic_load_storage_vec2_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_5(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec2<f32>
+fn tint_symbol_4(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec2<f32>
 
-@internal(intrinsic_load_storage_vec3_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_6(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec3<i32>
+@internal(intrinsic_load_storage_vec2_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_5(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec2<i32>
 
-@internal(intrinsic_load_storage_vec3_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_7(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec3<u32>
+@internal(intrinsic_load_storage_vec2_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_6(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec2<u32>
+
+@internal(intrinsic_load_storage_vec2_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_7(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec2<f16>
 
 @internal(intrinsic_load_storage_vec3_f32) @internal(disable_validation__function_has_no_body)
 fn tint_symbol_8(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec3<f32>
 
-@internal(intrinsic_load_storage_vec4_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_9(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec4<i32>
+@internal(intrinsic_load_storage_vec3_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_9(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec3<i32>
 
-@internal(intrinsic_load_storage_vec4_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_10(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec4<u32>
+@internal(intrinsic_load_storage_vec3_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_10(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec3<u32>
+
+@internal(intrinsic_load_storage_vec3_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_11(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec3<f16>
 
 @internal(intrinsic_load_storage_vec4_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_11(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec4<f32>
+fn tint_symbol_12(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec4<f32>
 
-fn tint_symbol_12(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat2x2<f32> {
-  return mat2x2<f32>(tint_symbol_5(buffer, (offset + 0u)), tint_symbol_5(buffer, (offset + 8u)));
+@internal(intrinsic_load_storage_vec4_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_13(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec4<i32>
+
+@internal(intrinsic_load_storage_vec4_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_14(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec4<u32>
+
+@internal(intrinsic_load_storage_vec4_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_15(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec4<f16>
+
+fn tint_symbol_16(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat2x2<f32> {
+  return mat2x2<f32>(tint_symbol_4(buffer, (offset + 0u)), tint_symbol_4(buffer, (offset + 8u)));
 }
 
-fn tint_symbol_13(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat2x3<f32> {
+fn tint_symbol_17(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat2x3<f32> {
   return mat2x3<f32>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 16u)));
 }
 
-fn tint_symbol_14(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat2x4<f32> {
-  return mat2x4<f32>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 16u)));
+fn tint_symbol_18(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat2x4<f32> {
+  return mat2x4<f32>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 16u)));
 }
 
-fn tint_symbol_15(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat3x2<f32> {
-  return mat3x2<f32>(tint_symbol_5(buffer, (offset + 0u)), tint_symbol_5(buffer, (offset + 8u)), tint_symbol_5(buffer, (offset + 16u)));
+fn tint_symbol_19(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat3x2<f32> {
+  return mat3x2<f32>(tint_symbol_4(buffer, (offset + 0u)), tint_symbol_4(buffer, (offset + 8u)), tint_symbol_4(buffer, (offset + 16u)));
 }
 
-fn tint_symbol_16(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat3x3<f32> {
+fn tint_symbol_20(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat3x3<f32> {
   return mat3x3<f32>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 16u)), tint_symbol_8(buffer, (offset + 32u)));
 }
 
-fn tint_symbol_17(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat3x4<f32> {
-  return mat3x4<f32>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 16u)), tint_symbol_11(buffer, (offset + 32u)));
+fn tint_symbol_21(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat3x4<f32> {
+  return mat3x4<f32>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 16u)), tint_symbol_12(buffer, (offset + 32u)));
 }
 
-fn tint_symbol_18(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat4x2<f32> {
-  return mat4x2<f32>(tint_symbol_5(buffer, (offset + 0u)), tint_symbol_5(buffer, (offset + 8u)), tint_symbol_5(buffer, (offset + 16u)), tint_symbol_5(buffer, (offset + 24u)));
+fn tint_symbol_22(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat4x2<f32> {
+  return mat4x2<f32>(tint_symbol_4(buffer, (offset + 0u)), tint_symbol_4(buffer, (offset + 8u)), tint_symbol_4(buffer, (offset + 16u)), tint_symbol_4(buffer, (offset + 24u)));
 }
 
-fn tint_symbol_19(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat4x3<f32> {
+fn tint_symbol_23(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat4x3<f32> {
   return mat4x3<f32>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 16u)), tint_symbol_8(buffer, (offset + 32u)), tint_symbol_8(buffer, (offset + 48u)));
 }
 
-fn tint_symbol_20(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat4x4<f32> {
-  return mat4x4<f32>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 16u)), tint_symbol_11(buffer, (offset + 32u)), tint_symbol_11(buffer, (offset + 48u)));
+fn tint_symbol_24(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat4x4<f32> {
+  return mat4x4<f32>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 16u)), tint_symbol_12(buffer, (offset + 32u)), tint_symbol_12(buffer, (offset + 48u)));
 }
 
-fn tint_symbol_21(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> array<vec3<f32>, 2u> {
+fn tint_symbol_25(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat2x2<f16> {
+  return mat2x2<f16>(tint_symbol_7(buffer, (offset + 0u)), tint_symbol_7(buffer, (offset + 4u)));
+}
+
+fn tint_symbol_26(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat2x3<f16> {
+  return mat2x3<f16>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 8u)));
+}
+
+fn tint_symbol_27(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat2x4<f16> {
+  return mat2x4<f16>(tint_symbol_15(buffer, (offset + 0u)), tint_symbol_15(buffer, (offset + 8u)));
+}
+
+fn tint_symbol_28(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat3x2<f16> {
+  return mat3x2<f16>(tint_symbol_7(buffer, (offset + 0u)), tint_symbol_7(buffer, (offset + 4u)), tint_symbol_7(buffer, (offset + 8u)));
+}
+
+fn tint_symbol_29(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat3x3<f16> {
+  return mat3x3<f16>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 8u)), tint_symbol_11(buffer, (offset + 16u)));
+}
+
+fn tint_symbol_30(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat3x4<f16> {
+  return mat3x4<f16>(tint_symbol_15(buffer, (offset + 0u)), tint_symbol_15(buffer, (offset + 8u)), tint_symbol_15(buffer, (offset + 16u)));
+}
+
+fn tint_symbol_31(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat4x2<f16> {
+  return mat4x2<f16>(tint_symbol_7(buffer, (offset + 0u)), tint_symbol_7(buffer, (offset + 4u)), tint_symbol_7(buffer, (offset + 8u)), tint_symbol_7(buffer, (offset + 12u)));
+}
+
+fn tint_symbol_32(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat4x3<f16> {
+  return mat4x3<f16>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 8u)), tint_symbol_11(buffer, (offset + 16u)), tint_symbol_11(buffer, (offset + 24u)));
+}
+
+fn tint_symbol_33(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat4x4<f16> {
+  return mat4x4<f16>(tint_symbol_15(buffer, (offset + 0u)), tint_symbol_15(buffer, (offset + 8u)), tint_symbol_15(buffer, (offset + 16u)), tint_symbol_15(buffer, (offset + 24u)));
+}
+
+fn tint_symbol_34(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> array<vec3<f32>, 2u> {
   var arr : array<vec3<f32>, 2u>;
-  for(var i_1 = 0u; (i_1 < 2u); i_1 = (i_1 + 1u)) {
-    arr[i_1] = tint_symbol_8(buffer, (offset + (i_1 * 16u)));
+  for(var i = 0u; (i < 2u); i = (i + 1u)) {
+    arr[i] = tint_symbol_8(buffer, (offset + (i * 16u)));
   }
   return arr;
 }
 
+fn tint_symbol_35(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> array<vec3<f16>, 2u> {
+  var arr_1 : array<vec3<f16>, 2u>;
+  for(var i_1 = 0u; (i_1 < 2u); i_1 = (i_1 + 1u)) {
+    arr_1[i_1] = tint_symbol_11(buffer, (offset + (i_1 * 8u)));
+  }
+  return arr_1;
+}
+
 @compute @workgroup_size(1)
 fn main() {
-  var a : i32 = tint_symbol(&(sb), 0u);
-  var b : u32 = tint_symbol_1(&(sb), 4u);
-  var c : f32 = tint_symbol_2(&(sb), 8u);
-  var d : vec2<i32> = tint_symbol_3(&(sb), 16u);
-  var e : vec2<u32> = tint_symbol_4(&(sb), 24u);
-  var f : vec2<f32> = tint_symbol_5(&(sb), 32u);
-  var g : vec3<i32> = tint_symbol_6(&(sb), 48u);
-  var h : vec3<u32> = tint_symbol_7(&(sb), 64u);
-  var i : vec3<f32> = tint_symbol_8(&(sb), 80u);
-  var j : vec4<i32> = tint_symbol_9(&(sb), 96u);
-  var k : vec4<u32> = tint_symbol_10(&(sb), 112u);
-  var l : vec4<f32> = tint_symbol_11(&(sb), 128u);
-  var m : mat2x2<f32> = tint_symbol_12(&(sb), 144u);
-  var n : mat2x3<f32> = tint_symbol_13(&(sb), 160u);
-  var o : mat2x4<f32> = tint_symbol_14(&(sb), 192u);
-  var p : mat3x2<f32> = tint_symbol_15(&(sb), 224u);
-  var q : mat3x3<f32> = tint_symbol_16(&(sb), 256u);
-  var r : mat3x4<f32> = tint_symbol_17(&(sb), 304u);
-  var s : mat4x2<f32> = tint_symbol_18(&(sb), 352u);
-  var t : mat4x3<f32> = tint_symbol_19(&(sb), 384u);
-  var u : mat4x4<f32> = tint_symbol_20(&(sb), 448u);
-  var v : array<vec3<f32>, 2> = tint_symbol_21(&(sb), 512u);
+  var scalar_f32 : f32 = tint_symbol(&(sb), 0u);
+  var scalar_i32 : i32 = tint_symbol_1(&(sb), 4u);
+  var scalar_u32 : u32 = tint_symbol_2(&(sb), 8u);
+  var scalar_f16 : f16 = tint_symbol_3(&(sb), 12u);
+  var vec2_f32 : vec2<f32> = tint_symbol_4(&(sb), 16u);
+  var vec2_i32 : vec2<i32> = tint_symbol_5(&(sb), 24u);
+  var vec2_u32 : vec2<u32> = tint_symbol_6(&(sb), 32u);
+  var vec2_f16 : vec2<f16> = tint_symbol_7(&(sb), 40u);
+  var vec3_f32 : vec3<f32> = tint_symbol_8(&(sb), 48u);
+  var vec3_i32 : vec3<i32> = tint_symbol_9(&(sb), 64u);
+  var vec3_u32 : vec3<u32> = tint_symbol_10(&(sb), 80u);
+  var vec3_f16 : vec3<f16> = tint_symbol_11(&(sb), 96u);
+  var vec4_f32 : vec4<f32> = tint_symbol_12(&(sb), 112u);
+  var vec4_i32 : vec4<i32> = tint_symbol_13(&(sb), 128u);
+  var vec4_u32 : vec4<u32> = tint_symbol_14(&(sb), 144u);
+  var vec4_f16 : vec4<f16> = tint_symbol_15(&(sb), 160u);
+  var mat2x2_f32 : mat2x2<f32> = tint_symbol_16(&(sb), 168u);
+  var mat2x3_f32 : mat2x3<f32> = tint_symbol_17(&(sb), 192u);
+  var mat2x4_f32 : mat2x4<f32> = tint_symbol_18(&(sb), 224u);
+  var mat3x2_f32 : mat3x2<f32> = tint_symbol_19(&(sb), 256u);
+  var mat3x3_f32 : mat3x3<f32> = tint_symbol_20(&(sb), 288u);
+  var mat3x4_f32 : mat3x4<f32> = tint_symbol_21(&(sb), 336u);
+  var mat4x2_f32 : mat4x2<f32> = tint_symbol_22(&(sb), 384u);
+  var mat4x3_f32 : mat4x3<f32> = tint_symbol_23(&(sb), 416u);
+  var mat4x4_f32 : mat4x4<f32> = tint_symbol_24(&(sb), 480u);
+  var mat2x2_f16 : mat2x2<f16> = tint_symbol_25(&(sb), 544u);
+  var mat2x3_f16 : mat2x3<f16> = tint_symbol_26(&(sb), 552u);
+  var mat2x4_f16 : mat2x4<f16> = tint_symbol_27(&(sb), 568u);
+  var mat3x2_f16 : mat3x2<f16> = tint_symbol_28(&(sb), 584u);
+  var mat3x3_f16 : mat3x3<f16> = tint_symbol_29(&(sb), 600u);
+  var mat3x4_f16 : mat3x4<f16> = tint_symbol_30(&(sb), 624u);
+  var mat4x2_f16 : mat4x2<f16> = tint_symbol_31(&(sb), 648u);
+  var mat4x3_f16 : mat4x3<f16> = tint_symbol_32(&(sb), 664u);
+  var mat4x4_f16 : mat4x4<f16> = tint_symbol_33(&(sb), 696u);
+  var arr2_vec3_f32 : array<vec3<f32>, 2> = tint_symbol_34(&(sb), 736u);
+  var arr2_vec3_f16 : array<vec3<f16>, 2> = tint_symbol_35(&(sb), 768u);
 }
 
 @group(0) @binding(0) var<storage, read_write> sb : SB;
 
 struct SB {
-  a : i32,
-  b : u32,
-  c : f32,
-  d : vec2<i32>,
-  e : vec2<u32>,
-  f : vec2<f32>,
-  g : vec3<i32>,
-  h : vec3<u32>,
-  i : vec3<f32>,
-  j : vec4<i32>,
-  k : vec4<u32>,
-  l : vec4<f32>,
-  m : mat2x2<f32>,
-  n : mat2x3<f32>,
-  o : mat2x4<f32>,
-  p : mat3x2<f32>,
-  q : mat3x3<f32>,
-  r : mat3x4<f32>,
-  s : mat4x2<f32>,
-  t : mat4x3<f32>,
-  u : mat4x4<f32>,
-  v : array<vec3<f32>, 2>,
+  scalar_f32 : f32,
+  scalar_i32 : i32,
+  scalar_u32 : u32,
+  scalar_f16 : f16,
+  vec2_f32 : vec2<f32>,
+  vec2_i32 : vec2<i32>,
+  vec2_u32 : vec2<u32>,
+  vec2_f16 : vec2<f16>,
+  vec3_f32 : vec3<f32>,
+  vec3_i32 : vec3<i32>,
+  vec3_u32 : vec3<u32>,
+  vec3_f16 : vec3<f16>,
+  vec4_f32 : vec4<f32>,
+  vec4_i32 : vec4<i32>,
+  vec4_u32 : vec4<u32>,
+  vec4_f16 : vec4<f16>,
+  mat2x2_f32 : mat2x2<f32>,
+  mat2x3_f32 : mat2x3<f32>,
+  mat2x4_f32 : mat2x4<f32>,
+  mat3x2_f32 : mat3x2<f32>,
+  mat3x3_f32 : mat3x3<f32>,
+  mat3x4_f32 : mat3x4<f32>,
+  mat4x2_f32 : mat4x2<f32>,
+  mat4x3_f32 : mat4x3<f32>,
+  mat4x4_f32 : mat4x4<f32>,
+  mat2x2_f16 : mat2x2<f16>,
+  mat2x3_f16 : mat2x3<f16>,
+  mat2x4_f16 : mat2x4<f16>,
+  mat3x2_f16 : mat3x2<f16>,
+  mat3x3_f16 : mat3x3<f16>,
+  mat3x4_f16 : mat3x4<f16>,
+  mat4x2_f16 : mat4x2<f16>,
+  mat4x3_f16 : mat4x3<f16>,
+  mat4x4_f16 : mat4x4<f16>,
+  arr2_vec3_f32 : array<vec3<f32>, 2>,
+  arr2_vec3_f16 : array<vec3<f16>, 2>,
 }
 )";
 
@@ -443,192 +675,308 @@
 
 TEST_F(DecomposeMemoryAccessTest, UB_BasicLoad) {
     auto* src = R"(
+enable f16;
+
 struct UB {
-  a : i32,
-  b : u32,
-  c : f32,
-  d : vec2<i32>,
-  e : vec2<u32>,
-  f : vec2<f32>,
-  g : vec3<i32>,
-  h : vec3<u32>,
-  i : vec3<f32>,
-  j : vec4<i32>,
-  k : vec4<u32>,
-  l : vec4<f32>,
-  m : mat2x2<f32>,
-  n : mat2x3<f32>,
-  o : mat2x4<f32>,
-  p : mat3x2<f32>,
-  q : mat3x3<f32>,
-  r : mat3x4<f32>,
-  s : mat4x2<f32>,
-  t : mat4x3<f32>,
-  u : mat4x4<f32>,
-  v : array<vec3<f32>, 2>,
+  scalar_f32 : f32,
+  scalar_i32 : i32,
+  scalar_u32 : u32,
+  scalar_f16 : f16,
+  vec2_f32 : vec2<f32>,
+  vec2_i32 : vec2<i32>,
+  vec2_u32 : vec2<u32>,
+  vec2_f16 : vec2<f16>,
+  vec3_f32 : vec3<f32>,
+  vec3_i32 : vec3<i32>,
+  vec3_u32 : vec3<u32>,
+  vec3_f16 : vec3<f16>,
+  vec4_f32 : vec4<f32>,
+  vec4_i32 : vec4<i32>,
+  vec4_u32 : vec4<u32>,
+  vec4_f16 : vec4<f16>,
+  mat2x2_f32 : mat2x2<f32>,
+  mat2x3_f32 : mat2x3<f32>,
+  mat2x4_f32 : mat2x4<f32>,
+  mat3x2_f32 : mat3x2<f32>,
+  mat3x3_f32 : mat3x3<f32>,
+  mat3x4_f32 : mat3x4<f32>,
+  mat4x2_f32 : mat4x2<f32>,
+  mat4x3_f32 : mat4x3<f32>,
+  mat4x4_f32 : mat4x4<f32>,
+  mat2x2_f16 : mat2x2<f16>,
+  mat2x3_f16 : mat2x3<f16>,
+  mat2x4_f16 : mat2x4<f16>,
+  mat3x2_f16 : mat3x2<f16>,
+  mat3x3_f16 : mat3x3<f16>,
+  mat3x4_f16 : mat3x4<f16>,
+  mat4x2_f16 : mat4x2<f16>,
+  mat4x3_f16 : mat4x3<f16>,
+  mat4x4_f16 : mat4x4<f16>,
+  arr2_vec3_f32 : array<vec3<f32>, 2>,
+  arr2_mat4x2_f16 : array<mat4x2<f16>, 2>,
 };
 
 @group(0) @binding(0) var<uniform> ub : UB;
 
 @compute @workgroup_size(1)
 fn main() {
-  var a : i32 = ub.a;
-  var b : u32 = ub.b;
-  var c : f32 = ub.c;
-  var d : vec2<i32> = ub.d;
-  var e : vec2<u32> = ub.e;
-  var f : vec2<f32> = ub.f;
-  var g : vec3<i32> = ub.g;
-  var h : vec3<u32> = ub.h;
-  var i : vec3<f32> = ub.i;
-  var j : vec4<i32> = ub.j;
-  var k : vec4<u32> = ub.k;
-  var l : vec4<f32> = ub.l;
-  var m : mat2x2<f32> = ub.m;
-  var n : mat2x3<f32> = ub.n;
-  var o : mat2x4<f32> = ub.o;
-  var p : mat3x2<f32> = ub.p;
-  var q : mat3x3<f32> = ub.q;
-  var r : mat3x4<f32> = ub.r;
-  var s : mat4x2<f32> = ub.s;
-  var t : mat4x3<f32> = ub.t;
-  var u : mat4x4<f32> = ub.u;
-  var v : array<vec3<f32>, 2> = ub.v;
+  var scalar_f32 : f32 = ub.scalar_f32;
+  var scalar_i32 : i32 = ub.scalar_i32;
+  var scalar_u32 : u32 = ub.scalar_u32;
+  var scalar_f16 : f16 = ub.scalar_f16;
+  var vec2_f32 : vec2<f32> = ub.vec2_f32;
+  var vec2_i32 : vec2<i32> = ub.vec2_i32;
+  var vec2_u32 : vec2<u32> = ub.vec2_u32;
+  var vec2_f16 : vec2<f16> = ub.vec2_f16;
+  var vec3_f32 : vec3<f32> = ub.vec3_f32;
+  var vec3_i32 : vec3<i32> = ub.vec3_i32;
+  var vec3_u32 : vec3<u32> = ub.vec3_u32;
+  var vec3_f16 : vec3<f16> = ub.vec3_f16;
+  var vec4_f32 : vec4<f32> = ub.vec4_f32;
+  var vec4_i32 : vec4<i32> = ub.vec4_i32;
+  var vec4_u32 : vec4<u32> = ub.vec4_u32;
+  var vec4_f16 : vec4<f16> = ub.vec4_f16;
+  var mat2x2_f32 : mat2x2<f32> = ub.mat2x2_f32;
+  var mat2x3_f32 : mat2x3<f32> = ub.mat2x3_f32;
+  var mat2x4_f32 : mat2x4<f32> = ub.mat2x4_f32;
+  var mat3x2_f32 : mat3x2<f32> = ub.mat3x2_f32;
+  var mat3x3_f32 : mat3x3<f32> = ub.mat3x3_f32;
+  var mat3x4_f32 : mat3x4<f32> = ub.mat3x4_f32;
+  var mat4x2_f32 : mat4x2<f32> = ub.mat4x2_f32;
+  var mat4x3_f32 : mat4x3<f32> = ub.mat4x3_f32;
+  var mat4x4_f32 : mat4x4<f32> = ub.mat4x4_f32;
+  var mat2x2_f16 : mat2x2<f16> = ub.mat2x2_f16;
+  var mat2x3_f16 : mat2x3<f16> = ub.mat2x3_f16;
+  var mat2x4_f16 : mat2x4<f16> = ub.mat2x4_f16;
+  var mat3x2_f16 : mat3x2<f16> = ub.mat3x2_f16;
+  var mat3x3_f16 : mat3x3<f16> = ub.mat3x3_f16;
+  var mat3x4_f16 : mat3x4<f16> = ub.mat3x4_f16;
+  var mat4x2_f16 : mat4x2<f16> = ub.mat4x2_f16;
+  var mat4x3_f16 : mat4x3<f16> = ub.mat4x3_f16;
+  var mat4x4_f16 : mat4x4<f16> = ub.mat4x4_f16;
+  var arr2_vec3_f32 : array<vec3<f32>, 2> = ub.arr2_vec3_f32;
+  var arr2_mat4x2_f16 : array<mat4x2<f16>, 2> = ub.arr2_mat4x2_f16;
 }
 )";
 
     auto* expect = R"(
+enable f16;
+
 struct UB {
-  a : i32,
-  b : u32,
-  c : f32,
-  d : vec2<i32>,
-  e : vec2<u32>,
-  f : vec2<f32>,
-  g : vec3<i32>,
-  h : vec3<u32>,
-  i : vec3<f32>,
-  j : vec4<i32>,
-  k : vec4<u32>,
-  l : vec4<f32>,
-  m : mat2x2<f32>,
-  n : mat2x3<f32>,
-  o : mat2x4<f32>,
-  p : mat3x2<f32>,
-  q : mat3x3<f32>,
-  r : mat3x4<f32>,
-  s : mat4x2<f32>,
-  t : mat4x3<f32>,
-  u : mat4x4<f32>,
-  v : array<vec3<f32>, 2>,
+  scalar_f32 : f32,
+  scalar_i32 : i32,
+  scalar_u32 : u32,
+  scalar_f16 : f16,
+  vec2_f32 : vec2<f32>,
+  vec2_i32 : vec2<i32>,
+  vec2_u32 : vec2<u32>,
+  vec2_f16 : vec2<f16>,
+  vec3_f32 : vec3<f32>,
+  vec3_i32 : vec3<i32>,
+  vec3_u32 : vec3<u32>,
+  vec3_f16 : vec3<f16>,
+  vec4_f32 : vec4<f32>,
+  vec4_i32 : vec4<i32>,
+  vec4_u32 : vec4<u32>,
+  vec4_f16 : vec4<f16>,
+  mat2x2_f32 : mat2x2<f32>,
+  mat2x3_f32 : mat2x3<f32>,
+  mat2x4_f32 : mat2x4<f32>,
+  mat3x2_f32 : mat3x2<f32>,
+  mat3x3_f32 : mat3x3<f32>,
+  mat3x4_f32 : mat3x4<f32>,
+  mat4x2_f32 : mat4x2<f32>,
+  mat4x3_f32 : mat4x3<f32>,
+  mat4x4_f32 : mat4x4<f32>,
+  mat2x2_f16 : mat2x2<f16>,
+  mat2x3_f16 : mat2x3<f16>,
+  mat2x4_f16 : mat2x4<f16>,
+  mat3x2_f16 : mat3x2<f16>,
+  mat3x3_f16 : mat3x3<f16>,
+  mat3x4_f16 : mat3x4<f16>,
+  mat4x2_f16 : mat4x2<f16>,
+  mat4x3_f16 : mat4x3<f16>,
+  mat4x4_f16 : mat4x4<f16>,
+  arr2_vec3_f32 : array<vec3<f32>, 2>,
+  arr2_mat4x2_f16 : array<mat4x2<f16>, 2>,
 }
 
 @group(0) @binding(0) var<uniform> ub : UB;
 
+@internal(intrinsic_load_uniform_f32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> f32
+
 @internal(intrinsic_load_uniform_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> i32
+fn tint_symbol_1(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> i32
 
 @internal(intrinsic_load_uniform_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_1(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> u32
+fn tint_symbol_2(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> u32
 
-@internal(intrinsic_load_uniform_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_2(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> f32
-
-@internal(intrinsic_load_uniform_vec2_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_3(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec2<i32>
-
-@internal(intrinsic_load_uniform_vec2_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_4(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec2<u32>
+@internal(intrinsic_load_uniform_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_3(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> f16
 
 @internal(intrinsic_load_uniform_vec2_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_5(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec2<f32>
+fn tint_symbol_4(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec2<f32>
 
-@internal(intrinsic_load_uniform_vec3_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_6(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec3<i32>
+@internal(intrinsic_load_uniform_vec2_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_5(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec2<i32>
 
-@internal(intrinsic_load_uniform_vec3_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_7(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec3<u32>
+@internal(intrinsic_load_uniform_vec2_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_6(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec2<u32>
+
+@internal(intrinsic_load_uniform_vec2_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_7(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec2<f16>
 
 @internal(intrinsic_load_uniform_vec3_f32) @internal(disable_validation__function_has_no_body)
 fn tint_symbol_8(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec3<f32>
 
-@internal(intrinsic_load_uniform_vec4_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_9(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec4<i32>
+@internal(intrinsic_load_uniform_vec3_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_9(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec3<i32>
 
-@internal(intrinsic_load_uniform_vec4_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_10(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec4<u32>
+@internal(intrinsic_load_uniform_vec3_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_10(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec3<u32>
+
+@internal(intrinsic_load_uniform_vec3_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_11(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec3<f16>
 
 @internal(intrinsic_load_uniform_vec4_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_11(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec4<f32>
+fn tint_symbol_12(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec4<f32>
 
-fn tint_symbol_12(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x2<f32> {
-  return mat2x2<f32>(tint_symbol_5(buffer, (offset + 0u)), tint_symbol_5(buffer, (offset + 8u)));
+@internal(intrinsic_load_uniform_vec4_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_13(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec4<i32>
+
+@internal(intrinsic_load_uniform_vec4_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_14(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec4<u32>
+
+@internal(intrinsic_load_uniform_vec4_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_15(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec4<f16>
+
+fn tint_symbol_16(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x2<f32> {
+  return mat2x2<f32>(tint_symbol_4(buffer, (offset + 0u)), tint_symbol_4(buffer, (offset + 8u)));
 }
 
-fn tint_symbol_13(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x3<f32> {
+fn tint_symbol_17(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x3<f32> {
   return mat2x3<f32>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 16u)));
 }
 
-fn tint_symbol_14(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x4<f32> {
-  return mat2x4<f32>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 16u)));
+fn tint_symbol_18(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x4<f32> {
+  return mat2x4<f32>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 16u)));
 }
 
-fn tint_symbol_15(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x2<f32> {
-  return mat3x2<f32>(tint_symbol_5(buffer, (offset + 0u)), tint_symbol_5(buffer, (offset + 8u)), tint_symbol_5(buffer, (offset + 16u)));
+fn tint_symbol_19(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x2<f32> {
+  return mat3x2<f32>(tint_symbol_4(buffer, (offset + 0u)), tint_symbol_4(buffer, (offset + 8u)), tint_symbol_4(buffer, (offset + 16u)));
 }
 
-fn tint_symbol_16(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x3<f32> {
+fn tint_symbol_20(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x3<f32> {
   return mat3x3<f32>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 16u)), tint_symbol_8(buffer, (offset + 32u)));
 }
 
-fn tint_symbol_17(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x4<f32> {
-  return mat3x4<f32>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 16u)), tint_symbol_11(buffer, (offset + 32u)));
+fn tint_symbol_21(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x4<f32> {
+  return mat3x4<f32>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 16u)), tint_symbol_12(buffer, (offset + 32u)));
 }
 
-fn tint_symbol_18(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x2<f32> {
-  return mat4x2<f32>(tint_symbol_5(buffer, (offset + 0u)), tint_symbol_5(buffer, (offset + 8u)), tint_symbol_5(buffer, (offset + 16u)), tint_symbol_5(buffer, (offset + 24u)));
+fn tint_symbol_22(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x2<f32> {
+  return mat4x2<f32>(tint_symbol_4(buffer, (offset + 0u)), tint_symbol_4(buffer, (offset + 8u)), tint_symbol_4(buffer, (offset + 16u)), tint_symbol_4(buffer, (offset + 24u)));
 }
 
-fn tint_symbol_19(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x3<f32> {
+fn tint_symbol_23(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x3<f32> {
   return mat4x3<f32>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 16u)), tint_symbol_8(buffer, (offset + 32u)), tint_symbol_8(buffer, (offset + 48u)));
 }
 
-fn tint_symbol_20(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x4<f32> {
-  return mat4x4<f32>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 16u)), tint_symbol_11(buffer, (offset + 32u)), tint_symbol_11(buffer, (offset + 48u)));
+fn tint_symbol_24(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x4<f32> {
+  return mat4x4<f32>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 16u)), tint_symbol_12(buffer, (offset + 32u)), tint_symbol_12(buffer, (offset + 48u)));
 }
 
-fn tint_symbol_21(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> array<vec3<f32>, 2u> {
+fn tint_symbol_25(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x2<f16> {
+  return mat2x2<f16>(tint_symbol_7(buffer, (offset + 0u)), tint_symbol_7(buffer, (offset + 4u)));
+}
+
+fn tint_symbol_26(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x3<f16> {
+  return mat2x3<f16>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 8u)));
+}
+
+fn tint_symbol_27(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x4<f16> {
+  return mat2x4<f16>(tint_symbol_15(buffer, (offset + 0u)), tint_symbol_15(buffer, (offset + 8u)));
+}
+
+fn tint_symbol_28(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x2<f16> {
+  return mat3x2<f16>(tint_symbol_7(buffer, (offset + 0u)), tint_symbol_7(buffer, (offset + 4u)), tint_symbol_7(buffer, (offset + 8u)));
+}
+
+fn tint_symbol_29(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x3<f16> {
+  return mat3x3<f16>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 8u)), tint_symbol_11(buffer, (offset + 16u)));
+}
+
+fn tint_symbol_30(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x4<f16> {
+  return mat3x4<f16>(tint_symbol_15(buffer, (offset + 0u)), tint_symbol_15(buffer, (offset + 8u)), tint_symbol_15(buffer, (offset + 16u)));
+}
+
+fn tint_symbol_31(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x2<f16> {
+  return mat4x2<f16>(tint_symbol_7(buffer, (offset + 0u)), tint_symbol_7(buffer, (offset + 4u)), tint_symbol_7(buffer, (offset + 8u)), tint_symbol_7(buffer, (offset + 12u)));
+}
+
+fn tint_symbol_32(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x3<f16> {
+  return mat4x3<f16>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 8u)), tint_symbol_11(buffer, (offset + 16u)), tint_symbol_11(buffer, (offset + 24u)));
+}
+
+fn tint_symbol_33(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x4<f16> {
+  return mat4x4<f16>(tint_symbol_15(buffer, (offset + 0u)), tint_symbol_15(buffer, (offset + 8u)), tint_symbol_15(buffer, (offset + 16u)), tint_symbol_15(buffer, (offset + 24u)));
+}
+
+fn tint_symbol_34(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> array<vec3<f32>, 2u> {
   var arr : array<vec3<f32>, 2u>;
-  for(var i_1 = 0u; (i_1 < 2u); i_1 = (i_1 + 1u)) {
-    arr[i_1] = tint_symbol_8(buffer, (offset + (i_1 * 16u)));
+  for(var i = 0u; (i < 2u); i = (i + 1u)) {
+    arr[i] = tint_symbol_8(buffer, (offset + (i * 16u)));
   }
   return arr;
 }
 
+fn tint_symbol_35(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> array<mat4x2<f16>, 2u> {
+  var arr_1 : array<mat4x2<f16>, 2u>;
+  for(var i_1 = 0u; (i_1 < 2u); i_1 = (i_1 + 1u)) {
+    arr_1[i_1] = tint_symbol_31(buffer, (offset + (i_1 * 16u)));
+  }
+  return arr_1;
+}
+
 @compute @workgroup_size(1)
 fn main() {
-  var a : i32 = tint_symbol(&(ub), 0u);
-  var b : u32 = tint_symbol_1(&(ub), 4u);
-  var c : f32 = tint_symbol_2(&(ub), 8u);
-  var d : vec2<i32> = tint_symbol_3(&(ub), 16u);
-  var e : vec2<u32> = tint_symbol_4(&(ub), 24u);
-  var f : vec2<f32> = tint_symbol_5(&(ub), 32u);
-  var g : vec3<i32> = tint_symbol_6(&(ub), 48u);
-  var h : vec3<u32> = tint_symbol_7(&(ub), 64u);
-  var i : vec3<f32> = tint_symbol_8(&(ub), 80u);
-  var j : vec4<i32> = tint_symbol_9(&(ub), 96u);
-  var k : vec4<u32> = tint_symbol_10(&(ub), 112u);
-  var l : vec4<f32> = tint_symbol_11(&(ub), 128u);
-  var m : mat2x2<f32> = tint_symbol_12(&(ub), 144u);
-  var n : mat2x3<f32> = tint_symbol_13(&(ub), 160u);
-  var o : mat2x4<f32> = tint_symbol_14(&(ub), 192u);
-  var p : mat3x2<f32> = tint_symbol_15(&(ub), 224u);
-  var q : mat3x3<f32> = tint_symbol_16(&(ub), 256u);
-  var r : mat3x4<f32> = tint_symbol_17(&(ub), 304u);
-  var s : mat4x2<f32> = tint_symbol_18(&(ub), 352u);
-  var t : mat4x3<f32> = tint_symbol_19(&(ub), 384u);
-  var u : mat4x4<f32> = tint_symbol_20(&(ub), 448u);
-  var v : array<vec3<f32>, 2> = tint_symbol_21(&(ub), 512u);
+  var scalar_f32 : f32 = tint_symbol(&(ub), 0u);
+  var scalar_i32 : i32 = tint_symbol_1(&(ub), 4u);
+  var scalar_u32 : u32 = tint_symbol_2(&(ub), 8u);
+  var scalar_f16 : f16 = tint_symbol_3(&(ub), 12u);
+  var vec2_f32 : vec2<f32> = tint_symbol_4(&(ub), 16u);
+  var vec2_i32 : vec2<i32> = tint_symbol_5(&(ub), 24u);
+  var vec2_u32 : vec2<u32> = tint_symbol_6(&(ub), 32u);
+  var vec2_f16 : vec2<f16> = tint_symbol_7(&(ub), 40u);
+  var vec3_f32 : vec3<f32> = tint_symbol_8(&(ub), 48u);
+  var vec3_i32 : vec3<i32> = tint_symbol_9(&(ub), 64u);
+  var vec3_u32 : vec3<u32> = tint_symbol_10(&(ub), 80u);
+  var vec3_f16 : vec3<f16> = tint_symbol_11(&(ub), 96u);
+  var vec4_f32 : vec4<f32> = tint_symbol_12(&(ub), 112u);
+  var vec4_i32 : vec4<i32> = tint_symbol_13(&(ub), 128u);
+  var vec4_u32 : vec4<u32> = tint_symbol_14(&(ub), 144u);
+  var vec4_f16 : vec4<f16> = tint_symbol_15(&(ub), 160u);
+  var mat2x2_f32 : mat2x2<f32> = tint_symbol_16(&(ub), 168u);
+  var mat2x3_f32 : mat2x3<f32> = tint_symbol_17(&(ub), 192u);
+  var mat2x4_f32 : mat2x4<f32> = tint_symbol_18(&(ub), 224u);
+  var mat3x2_f32 : mat3x2<f32> = tint_symbol_19(&(ub), 256u);
+  var mat3x3_f32 : mat3x3<f32> = tint_symbol_20(&(ub), 288u);
+  var mat3x4_f32 : mat3x4<f32> = tint_symbol_21(&(ub), 336u);
+  var mat4x2_f32 : mat4x2<f32> = tint_symbol_22(&(ub), 384u);
+  var mat4x3_f32 : mat4x3<f32> = tint_symbol_23(&(ub), 416u);
+  var mat4x4_f32 : mat4x4<f32> = tint_symbol_24(&(ub), 480u);
+  var mat2x2_f16 : mat2x2<f16> = tint_symbol_25(&(ub), 544u);
+  var mat2x3_f16 : mat2x3<f16> = tint_symbol_26(&(ub), 552u);
+  var mat2x4_f16 : mat2x4<f16> = tint_symbol_27(&(ub), 568u);
+  var mat3x2_f16 : mat3x2<f16> = tint_symbol_28(&(ub), 584u);
+  var mat3x3_f16 : mat3x3<f16> = tint_symbol_29(&(ub), 600u);
+  var mat3x4_f16 : mat3x4<f16> = tint_symbol_30(&(ub), 624u);
+  var mat4x2_f16 : mat4x2<f16> = tint_symbol_31(&(ub), 648u);
+  var mat4x3_f16 : mat4x3<f16> = tint_symbol_32(&(ub), 664u);
+  var mat4x4_f16 : mat4x4<f16> = tint_symbol_33(&(ub), 696u);
+  var arr2_vec3_f32 : array<vec3<f32>, 2> = tint_symbol_34(&(ub), 736u);
+  var arr2_mat4x2_f16 : array<mat4x2<f16>, 2> = tint_symbol_35(&(ub), 768u);
 }
 )";
 
@@ -639,192 +987,308 @@
 
 TEST_F(DecomposeMemoryAccessTest, UB_BasicLoad_OutOfOrder) {
     auto* src = R"(
+enable f16;
+
 @compute @workgroup_size(1)
 fn main() {
-  var a : i32 = ub.a;
-  var b : u32 = ub.b;
-  var c : f32 = ub.c;
-  var d : vec2<i32> = ub.d;
-  var e : vec2<u32> = ub.e;
-  var f : vec2<f32> = ub.f;
-  var g : vec3<i32> = ub.g;
-  var h : vec3<u32> = ub.h;
-  var i : vec3<f32> = ub.i;
-  var j : vec4<i32> = ub.j;
-  var k : vec4<u32> = ub.k;
-  var l : vec4<f32> = ub.l;
-  var m : mat2x2<f32> = ub.m;
-  var n : mat2x3<f32> = ub.n;
-  var o : mat2x4<f32> = ub.o;
-  var p : mat3x2<f32> = ub.p;
-  var q : mat3x3<f32> = ub.q;
-  var r : mat3x4<f32> = ub.r;
-  var s : mat4x2<f32> = ub.s;
-  var t : mat4x3<f32> = ub.t;
-  var u : mat4x4<f32> = ub.u;
-  var v : array<vec3<f32>, 2> = ub.v;
+  var scalar_f32 : f32 = ub.scalar_f32;
+  var scalar_i32 : i32 = ub.scalar_i32;
+  var scalar_u32 : u32 = ub.scalar_u32;
+  var scalar_f16 : f16 = ub.scalar_f16;
+  var vec2_f32 : vec2<f32> = ub.vec2_f32;
+  var vec2_i32 : vec2<i32> = ub.vec2_i32;
+  var vec2_u32 : vec2<u32> = ub.vec2_u32;
+  var vec2_f16 : vec2<f16> = ub.vec2_f16;
+  var vec3_f32 : vec3<f32> = ub.vec3_f32;
+  var vec3_i32 : vec3<i32> = ub.vec3_i32;
+  var vec3_u32 : vec3<u32> = ub.vec3_u32;
+  var vec3_f16 : vec3<f16> = ub.vec3_f16;
+  var vec4_f32 : vec4<f32> = ub.vec4_f32;
+  var vec4_i32 : vec4<i32> = ub.vec4_i32;
+  var vec4_u32 : vec4<u32> = ub.vec4_u32;
+  var vec4_f16 : vec4<f16> = ub.vec4_f16;
+  var mat2x2_f32 : mat2x2<f32> = ub.mat2x2_f32;
+  var mat2x3_f32 : mat2x3<f32> = ub.mat2x3_f32;
+  var mat2x4_f32 : mat2x4<f32> = ub.mat2x4_f32;
+  var mat3x2_f32 : mat3x2<f32> = ub.mat3x2_f32;
+  var mat3x3_f32 : mat3x3<f32> = ub.mat3x3_f32;
+  var mat3x4_f32 : mat3x4<f32> = ub.mat3x4_f32;
+  var mat4x2_f32 : mat4x2<f32> = ub.mat4x2_f32;
+  var mat4x3_f32 : mat4x3<f32> = ub.mat4x3_f32;
+  var mat4x4_f32 : mat4x4<f32> = ub.mat4x4_f32;
+  var mat2x2_f16 : mat2x2<f16> = ub.mat2x2_f16;
+  var mat2x3_f16 : mat2x3<f16> = ub.mat2x3_f16;
+  var mat2x4_f16 : mat2x4<f16> = ub.mat2x4_f16;
+  var mat3x2_f16 : mat3x2<f16> = ub.mat3x2_f16;
+  var mat3x3_f16 : mat3x3<f16> = ub.mat3x3_f16;
+  var mat3x4_f16 : mat3x4<f16> = ub.mat3x4_f16;
+  var mat4x2_f16 : mat4x2<f16> = ub.mat4x2_f16;
+  var mat4x3_f16 : mat4x3<f16> = ub.mat4x3_f16;
+  var mat4x4_f16 : mat4x4<f16> = ub.mat4x4_f16;
+  var arr2_vec3_f32 : array<vec3<f32>, 2> = ub.arr2_vec3_f32;
+  var arr2_mat4x2_f16 : array<mat4x2<f16>, 2> = ub.arr2_mat4x2_f16;
 }
 
 @group(0) @binding(0) var<uniform> ub : UB;
 
 struct UB {
-  a : i32,
-  b : u32,
-  c : f32,
-  d : vec2<i32>,
-  e : vec2<u32>,
-  f : vec2<f32>,
-  g : vec3<i32>,
-  h : vec3<u32>,
-  i : vec3<f32>,
-  j : vec4<i32>,
-  k : vec4<u32>,
-  l : vec4<f32>,
-  m : mat2x2<f32>,
-  n : mat2x3<f32>,
-  o : mat2x4<f32>,
-  p : mat3x2<f32>,
-  q : mat3x3<f32>,
-  r : mat3x4<f32>,
-  s : mat4x2<f32>,
-  t : mat4x3<f32>,
-  u : mat4x4<f32>,
-  v : array<vec3<f32>, 2>,
+  scalar_f32 : f32,
+  scalar_i32 : i32,
+  scalar_u32 : u32,
+  scalar_f16 : f16,
+  vec2_f32 : vec2<f32>,
+  vec2_i32 : vec2<i32>,
+  vec2_u32 : vec2<u32>,
+  vec2_f16 : vec2<f16>,
+  vec3_f32 : vec3<f32>,
+  vec3_i32 : vec3<i32>,
+  vec3_u32 : vec3<u32>,
+  vec3_f16 : vec3<f16>,
+  vec4_f32 : vec4<f32>,
+  vec4_i32 : vec4<i32>,
+  vec4_u32 : vec4<u32>,
+  vec4_f16 : vec4<f16>,
+  mat2x2_f32 : mat2x2<f32>,
+  mat2x3_f32 : mat2x3<f32>,
+  mat2x4_f32 : mat2x4<f32>,
+  mat3x2_f32 : mat3x2<f32>,
+  mat3x3_f32 : mat3x3<f32>,
+  mat3x4_f32 : mat3x4<f32>,
+  mat4x2_f32 : mat4x2<f32>,
+  mat4x3_f32 : mat4x3<f32>,
+  mat4x4_f32 : mat4x4<f32>,
+  mat2x2_f16 : mat2x2<f16>,
+  mat2x3_f16 : mat2x3<f16>,
+  mat2x4_f16 : mat2x4<f16>,
+  mat3x2_f16 : mat3x2<f16>,
+  mat3x3_f16 : mat3x3<f16>,
+  mat3x4_f16 : mat3x4<f16>,
+  mat4x2_f16 : mat4x2<f16>,
+  mat4x3_f16 : mat4x3<f16>,
+  mat4x4_f16 : mat4x4<f16>,
+  arr2_vec3_f32 : array<vec3<f32>, 2>,
+  arr2_mat4x2_f16 : array<mat4x2<f16>, 2>,
 };
 )";
 
     auto* expect = R"(
-@internal(intrinsic_load_uniform_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> i32
-
-@internal(intrinsic_load_uniform_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_1(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> u32
+enable f16;
 
 @internal(intrinsic_load_uniform_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_2(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> f32
+fn tint_symbol(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> f32
 
-@internal(intrinsic_load_uniform_vec2_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_3(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec2<i32>
+@internal(intrinsic_load_uniform_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_1(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> i32
 
-@internal(intrinsic_load_uniform_vec2_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_4(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec2<u32>
+@internal(intrinsic_load_uniform_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_2(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> u32
+
+@internal(intrinsic_load_uniform_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_3(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> f16
 
 @internal(intrinsic_load_uniform_vec2_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_5(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec2<f32>
+fn tint_symbol_4(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec2<f32>
 
-@internal(intrinsic_load_uniform_vec3_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_6(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec3<i32>
+@internal(intrinsic_load_uniform_vec2_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_5(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec2<i32>
 
-@internal(intrinsic_load_uniform_vec3_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_7(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec3<u32>
+@internal(intrinsic_load_uniform_vec2_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_6(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec2<u32>
+
+@internal(intrinsic_load_uniform_vec2_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_7(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec2<f16>
 
 @internal(intrinsic_load_uniform_vec3_f32) @internal(disable_validation__function_has_no_body)
 fn tint_symbol_8(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec3<f32>
 
-@internal(intrinsic_load_uniform_vec4_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_9(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec4<i32>
+@internal(intrinsic_load_uniform_vec3_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_9(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec3<i32>
 
-@internal(intrinsic_load_uniform_vec4_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_10(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec4<u32>
+@internal(intrinsic_load_uniform_vec3_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_10(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec3<u32>
+
+@internal(intrinsic_load_uniform_vec3_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_11(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec3<f16>
 
 @internal(intrinsic_load_uniform_vec4_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_11(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec4<f32>
+fn tint_symbol_12(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec4<f32>
 
-fn tint_symbol_12(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x2<f32> {
-  return mat2x2<f32>(tint_symbol_5(buffer, (offset + 0u)), tint_symbol_5(buffer, (offset + 8u)));
+@internal(intrinsic_load_uniform_vec4_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_13(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec4<i32>
+
+@internal(intrinsic_load_uniform_vec4_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_14(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec4<u32>
+
+@internal(intrinsic_load_uniform_vec4_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_15(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec4<f16>
+
+fn tint_symbol_16(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x2<f32> {
+  return mat2x2<f32>(tint_symbol_4(buffer, (offset + 0u)), tint_symbol_4(buffer, (offset + 8u)));
 }
 
-fn tint_symbol_13(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x3<f32> {
+fn tint_symbol_17(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x3<f32> {
   return mat2x3<f32>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 16u)));
 }
 
-fn tint_symbol_14(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x4<f32> {
-  return mat2x4<f32>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 16u)));
+fn tint_symbol_18(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x4<f32> {
+  return mat2x4<f32>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 16u)));
 }
 
-fn tint_symbol_15(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x2<f32> {
-  return mat3x2<f32>(tint_symbol_5(buffer, (offset + 0u)), tint_symbol_5(buffer, (offset + 8u)), tint_symbol_5(buffer, (offset + 16u)));
+fn tint_symbol_19(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x2<f32> {
+  return mat3x2<f32>(tint_symbol_4(buffer, (offset + 0u)), tint_symbol_4(buffer, (offset + 8u)), tint_symbol_4(buffer, (offset + 16u)));
 }
 
-fn tint_symbol_16(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x3<f32> {
+fn tint_symbol_20(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x3<f32> {
   return mat3x3<f32>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 16u)), tint_symbol_8(buffer, (offset + 32u)));
 }
 
-fn tint_symbol_17(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x4<f32> {
-  return mat3x4<f32>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 16u)), tint_symbol_11(buffer, (offset + 32u)));
+fn tint_symbol_21(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x4<f32> {
+  return mat3x4<f32>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 16u)), tint_symbol_12(buffer, (offset + 32u)));
 }
 
-fn tint_symbol_18(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x2<f32> {
-  return mat4x2<f32>(tint_symbol_5(buffer, (offset + 0u)), tint_symbol_5(buffer, (offset + 8u)), tint_symbol_5(buffer, (offset + 16u)), tint_symbol_5(buffer, (offset + 24u)));
+fn tint_symbol_22(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x2<f32> {
+  return mat4x2<f32>(tint_symbol_4(buffer, (offset + 0u)), tint_symbol_4(buffer, (offset + 8u)), tint_symbol_4(buffer, (offset + 16u)), tint_symbol_4(buffer, (offset + 24u)));
 }
 
-fn tint_symbol_19(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x3<f32> {
+fn tint_symbol_23(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x3<f32> {
   return mat4x3<f32>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 16u)), tint_symbol_8(buffer, (offset + 32u)), tint_symbol_8(buffer, (offset + 48u)));
 }
 
-fn tint_symbol_20(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x4<f32> {
-  return mat4x4<f32>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 16u)), tint_symbol_11(buffer, (offset + 32u)), tint_symbol_11(buffer, (offset + 48u)));
+fn tint_symbol_24(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x4<f32> {
+  return mat4x4<f32>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 16u)), tint_symbol_12(buffer, (offset + 32u)), tint_symbol_12(buffer, (offset + 48u)));
 }
 
-fn tint_symbol_21(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> array<vec3<f32>, 2u> {
+fn tint_symbol_25(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x2<f16> {
+  return mat2x2<f16>(tint_symbol_7(buffer, (offset + 0u)), tint_symbol_7(buffer, (offset + 4u)));
+}
+
+fn tint_symbol_26(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x3<f16> {
+  return mat2x3<f16>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 8u)));
+}
+
+fn tint_symbol_27(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x4<f16> {
+  return mat2x4<f16>(tint_symbol_15(buffer, (offset + 0u)), tint_symbol_15(buffer, (offset + 8u)));
+}
+
+fn tint_symbol_28(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x2<f16> {
+  return mat3x2<f16>(tint_symbol_7(buffer, (offset + 0u)), tint_symbol_7(buffer, (offset + 4u)), tint_symbol_7(buffer, (offset + 8u)));
+}
+
+fn tint_symbol_29(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x3<f16> {
+  return mat3x3<f16>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 8u)), tint_symbol_11(buffer, (offset + 16u)));
+}
+
+fn tint_symbol_30(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x4<f16> {
+  return mat3x4<f16>(tint_symbol_15(buffer, (offset + 0u)), tint_symbol_15(buffer, (offset + 8u)), tint_symbol_15(buffer, (offset + 16u)));
+}
+
+fn tint_symbol_31(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x2<f16> {
+  return mat4x2<f16>(tint_symbol_7(buffer, (offset + 0u)), tint_symbol_7(buffer, (offset + 4u)), tint_symbol_7(buffer, (offset + 8u)), tint_symbol_7(buffer, (offset + 12u)));
+}
+
+fn tint_symbol_32(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x3<f16> {
+  return mat4x3<f16>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 8u)), tint_symbol_11(buffer, (offset + 16u)), tint_symbol_11(buffer, (offset + 24u)));
+}
+
+fn tint_symbol_33(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x4<f16> {
+  return mat4x4<f16>(tint_symbol_15(buffer, (offset + 0u)), tint_symbol_15(buffer, (offset + 8u)), tint_symbol_15(buffer, (offset + 16u)), tint_symbol_15(buffer, (offset + 24u)));
+}
+
+fn tint_symbol_34(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> array<vec3<f32>, 2u> {
   var arr : array<vec3<f32>, 2u>;
-  for(var i_1 = 0u; (i_1 < 2u); i_1 = (i_1 + 1u)) {
-    arr[i_1] = tint_symbol_8(buffer, (offset + (i_1 * 16u)));
+  for(var i = 0u; (i < 2u); i = (i + 1u)) {
+    arr[i] = tint_symbol_8(buffer, (offset + (i * 16u)));
   }
   return arr;
 }
 
+fn tint_symbol_35(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> array<mat4x2<f16>, 2u> {
+  var arr_1 : array<mat4x2<f16>, 2u>;
+  for(var i_1 = 0u; (i_1 < 2u); i_1 = (i_1 + 1u)) {
+    arr_1[i_1] = tint_symbol_31(buffer, (offset + (i_1 * 16u)));
+  }
+  return arr_1;
+}
+
 @compute @workgroup_size(1)
 fn main() {
-  var a : i32 = tint_symbol(&(ub), 0u);
-  var b : u32 = tint_symbol_1(&(ub), 4u);
-  var c : f32 = tint_symbol_2(&(ub), 8u);
-  var d : vec2<i32> = tint_symbol_3(&(ub), 16u);
-  var e : vec2<u32> = tint_symbol_4(&(ub), 24u);
-  var f : vec2<f32> = tint_symbol_5(&(ub), 32u);
-  var g : vec3<i32> = tint_symbol_6(&(ub), 48u);
-  var h : vec3<u32> = tint_symbol_7(&(ub), 64u);
-  var i : vec3<f32> = tint_symbol_8(&(ub), 80u);
-  var j : vec4<i32> = tint_symbol_9(&(ub), 96u);
-  var k : vec4<u32> = tint_symbol_10(&(ub), 112u);
-  var l : vec4<f32> = tint_symbol_11(&(ub), 128u);
-  var m : mat2x2<f32> = tint_symbol_12(&(ub), 144u);
-  var n : mat2x3<f32> = tint_symbol_13(&(ub), 160u);
-  var o : mat2x4<f32> = tint_symbol_14(&(ub), 192u);
-  var p : mat3x2<f32> = tint_symbol_15(&(ub), 224u);
-  var q : mat3x3<f32> = tint_symbol_16(&(ub), 256u);
-  var r : mat3x4<f32> = tint_symbol_17(&(ub), 304u);
-  var s : mat4x2<f32> = tint_symbol_18(&(ub), 352u);
-  var t : mat4x3<f32> = tint_symbol_19(&(ub), 384u);
-  var u : mat4x4<f32> = tint_symbol_20(&(ub), 448u);
-  var v : array<vec3<f32>, 2> = tint_symbol_21(&(ub), 512u);
+  var scalar_f32 : f32 = tint_symbol(&(ub), 0u);
+  var scalar_i32 : i32 = tint_symbol_1(&(ub), 4u);
+  var scalar_u32 : u32 = tint_symbol_2(&(ub), 8u);
+  var scalar_f16 : f16 = tint_symbol_3(&(ub), 12u);
+  var vec2_f32 : vec2<f32> = tint_symbol_4(&(ub), 16u);
+  var vec2_i32 : vec2<i32> = tint_symbol_5(&(ub), 24u);
+  var vec2_u32 : vec2<u32> = tint_symbol_6(&(ub), 32u);
+  var vec2_f16 : vec2<f16> = tint_symbol_7(&(ub), 40u);
+  var vec3_f32 : vec3<f32> = tint_symbol_8(&(ub), 48u);
+  var vec3_i32 : vec3<i32> = tint_symbol_9(&(ub), 64u);
+  var vec3_u32 : vec3<u32> = tint_symbol_10(&(ub), 80u);
+  var vec3_f16 : vec3<f16> = tint_symbol_11(&(ub), 96u);
+  var vec4_f32 : vec4<f32> = tint_symbol_12(&(ub), 112u);
+  var vec4_i32 : vec4<i32> = tint_symbol_13(&(ub), 128u);
+  var vec4_u32 : vec4<u32> = tint_symbol_14(&(ub), 144u);
+  var vec4_f16 : vec4<f16> = tint_symbol_15(&(ub), 160u);
+  var mat2x2_f32 : mat2x2<f32> = tint_symbol_16(&(ub), 168u);
+  var mat2x3_f32 : mat2x3<f32> = tint_symbol_17(&(ub), 192u);
+  var mat2x4_f32 : mat2x4<f32> = tint_symbol_18(&(ub), 224u);
+  var mat3x2_f32 : mat3x2<f32> = tint_symbol_19(&(ub), 256u);
+  var mat3x3_f32 : mat3x3<f32> = tint_symbol_20(&(ub), 288u);
+  var mat3x4_f32 : mat3x4<f32> = tint_symbol_21(&(ub), 336u);
+  var mat4x2_f32 : mat4x2<f32> = tint_symbol_22(&(ub), 384u);
+  var mat4x3_f32 : mat4x3<f32> = tint_symbol_23(&(ub), 416u);
+  var mat4x4_f32 : mat4x4<f32> = tint_symbol_24(&(ub), 480u);
+  var mat2x2_f16 : mat2x2<f16> = tint_symbol_25(&(ub), 544u);
+  var mat2x3_f16 : mat2x3<f16> = tint_symbol_26(&(ub), 552u);
+  var mat2x4_f16 : mat2x4<f16> = tint_symbol_27(&(ub), 568u);
+  var mat3x2_f16 : mat3x2<f16> = tint_symbol_28(&(ub), 584u);
+  var mat3x3_f16 : mat3x3<f16> = tint_symbol_29(&(ub), 600u);
+  var mat3x4_f16 : mat3x4<f16> = tint_symbol_30(&(ub), 624u);
+  var mat4x2_f16 : mat4x2<f16> = tint_symbol_31(&(ub), 648u);
+  var mat4x3_f16 : mat4x3<f16> = tint_symbol_32(&(ub), 664u);
+  var mat4x4_f16 : mat4x4<f16> = tint_symbol_33(&(ub), 696u);
+  var arr2_vec3_f32 : array<vec3<f32>, 2> = tint_symbol_34(&(ub), 736u);
+  var arr2_mat4x2_f16 : array<mat4x2<f16>, 2> = tint_symbol_35(&(ub), 768u);
 }
 
 @group(0) @binding(0) var<uniform> ub : UB;
 
 struct UB {
-  a : i32,
-  b : u32,
-  c : f32,
-  d : vec2<i32>,
-  e : vec2<u32>,
-  f : vec2<f32>,
-  g : vec3<i32>,
-  h : vec3<u32>,
-  i : vec3<f32>,
-  j : vec4<i32>,
-  k : vec4<u32>,
-  l : vec4<f32>,
-  m : mat2x2<f32>,
-  n : mat2x3<f32>,
-  o : mat2x4<f32>,
-  p : mat3x2<f32>,
-  q : mat3x3<f32>,
-  r : mat3x4<f32>,
-  s : mat4x2<f32>,
-  t : mat4x3<f32>,
-  u : mat4x4<f32>,
-  v : array<vec3<f32>, 2>,
+  scalar_f32 : f32,
+  scalar_i32 : i32,
+  scalar_u32 : u32,
+  scalar_f16 : f16,
+  vec2_f32 : vec2<f32>,
+  vec2_i32 : vec2<i32>,
+  vec2_u32 : vec2<u32>,
+  vec2_f16 : vec2<f16>,
+  vec3_f32 : vec3<f32>,
+  vec3_i32 : vec3<i32>,
+  vec3_u32 : vec3<u32>,
+  vec3_f16 : vec3<f16>,
+  vec4_f32 : vec4<f32>,
+  vec4_i32 : vec4<i32>,
+  vec4_u32 : vec4<u32>,
+  vec4_f16 : vec4<f16>,
+  mat2x2_f32 : mat2x2<f32>,
+  mat2x3_f32 : mat2x3<f32>,
+  mat2x4_f32 : mat2x4<f32>,
+  mat3x2_f32 : mat3x2<f32>,
+  mat3x3_f32 : mat3x3<f32>,
+  mat3x4_f32 : mat3x4<f32>,
+  mat4x2_f32 : mat4x2<f32>,
+  mat4x3_f32 : mat4x3<f32>,
+  mat4x4_f32 : mat4x4<f32>,
+  mat2x2_f16 : mat2x2<f16>,
+  mat2x3_f16 : mat2x3<f16>,
+  mat2x4_f16 : mat2x4<f16>,
+  mat3x2_f16 : mat3x2<f16>,
+  mat3x3_f16 : mat3x3<f16>,
+  mat3x4_f16 : mat3x4<f16>,
+  mat4x2_f16 : mat4x2<f16>,
+  mat4x3_f16 : mat4x3<f16>,
+  mat4x4_f16 : mat4x4<f16>,
+  arr2_vec3_f32 : array<vec3<f32>, 2>,
+  arr2_mat4x2_f16 : array<mat4x2<f16>, 2>,
 }
 )";
 
@@ -835,209 +1299,342 @@
 
 TEST_F(DecomposeMemoryAccessTest, SB_BasicStore) {
     auto* src = R"(
+enable f16;
+
 struct SB {
-  a : i32,
-  b : u32,
-  c : f32,
-  d : vec2<i32>,
-  e : vec2<u32>,
-  f : vec2<f32>,
-  g : vec3<i32>,
-  h : vec3<u32>,
-  i : vec3<f32>,
-  j : vec4<i32>,
-  k : vec4<u32>,
-  l : vec4<f32>,
-  m : mat2x2<f32>,
-  n : mat2x3<f32>,
-  o : mat2x4<f32>,
-  p : mat3x2<f32>,
-  q : mat3x3<f32>,
-  r : mat3x4<f32>,
-  s : mat4x2<f32>,
-  t : mat4x3<f32>,
-  u : mat4x4<f32>,
-  v : array<vec3<f32>, 2>,
+  scalar_f32 : f32,
+  scalar_i32 : i32,
+  scalar_u32 : u32,
+  scalar_f16 : f16,
+  vec2_f32 : vec2<f32>,
+  vec2_i32 : vec2<i32>,
+  vec2_u32 : vec2<u32>,
+  vec2_f16 : vec2<f16>,
+  vec3_f32 : vec3<f32>,
+  vec3_i32 : vec3<i32>,
+  vec3_u32 : vec3<u32>,
+  vec3_f16 : vec3<f16>,
+  vec4_f32 : vec4<f32>,
+  vec4_i32 : vec4<i32>,
+  vec4_u32 : vec4<u32>,
+  vec4_f16 : vec4<f16>,
+  mat2x2_f32 : mat2x2<f32>,
+  mat2x3_f32 : mat2x3<f32>,
+  mat2x4_f32 : mat2x4<f32>,
+  mat3x2_f32 : mat3x2<f32>,
+  mat3x3_f32 : mat3x3<f32>,
+  mat3x4_f32 : mat3x4<f32>,
+  mat4x2_f32 : mat4x2<f32>,
+  mat4x3_f32 : mat4x3<f32>,
+  mat4x4_f32 : mat4x4<f32>,
+  mat2x2_f16 : mat2x2<f16>,
+  mat2x3_f16 : mat2x3<f16>,
+  mat2x4_f16 : mat2x4<f16>,
+  mat3x2_f16 : mat3x2<f16>,
+  mat3x3_f16 : mat3x3<f16>,
+  mat3x4_f16 : mat3x4<f16>,
+  mat4x2_f16 : mat4x2<f16>,
+  mat4x3_f16 : mat4x3<f16>,
+  mat4x4_f16 : mat4x4<f16>,
+  arr2_vec3_f32 : array<vec3<f32>, 2>,
+  arr2_mat4x2_f16 : array<mat4x2<f16>, 2>,
 };
 
 @group(0) @binding(0) var<storage, read_write> sb : SB;
 
 @compute @workgroup_size(1)
 fn main() {
-  sb.a = i32();
-  sb.b = u32();
-  sb.c = f32();
-  sb.d = vec2<i32>();
-  sb.e = vec2<u32>();
-  sb.f = vec2<f32>();
-  sb.g = vec3<i32>();
-  sb.h = vec3<u32>();
-  sb.i = vec3<f32>();
-  sb.j = vec4<i32>();
-  sb.k = vec4<u32>();
-  sb.l = vec4<f32>();
-  sb.m = mat2x2<f32>();
-  sb.n = mat2x3<f32>();
-  sb.o = mat2x4<f32>();
-  sb.p = mat3x2<f32>();
-  sb.q = mat3x3<f32>();
-  sb.r = mat3x4<f32>();
-  sb.s = mat4x2<f32>();
-  sb.t = mat4x3<f32>();
-  sb.u = mat4x4<f32>();
-  sb.v = array<vec3<f32>, 2>();
+  sb.scalar_f32 = f32();
+  sb.scalar_i32 = i32();
+  sb.scalar_u32 = u32();
+  sb.scalar_f16 = f16();
+  sb.vec2_f32 = vec2<f32>();
+  sb.vec2_i32 = vec2<i32>();
+  sb.vec2_u32 = vec2<u32>();
+  sb.vec2_f16 = vec2<f16>();
+  sb.vec3_f32 = vec3<f32>();
+  sb.vec3_i32 = vec3<i32>();
+  sb.vec3_u32 = vec3<u32>();
+  sb.vec3_f16 = vec3<f16>();
+  sb.vec4_f32 = vec4<f32>();
+  sb.vec4_i32 = vec4<i32>();
+  sb.vec4_u32 = vec4<u32>();
+  sb.vec4_f16 = vec4<f16>();
+  sb.mat2x2_f32 = mat2x2<f32>();
+  sb.mat2x3_f32 = mat2x3<f32>();
+  sb.mat2x4_f32 = mat2x4<f32>();
+  sb.mat3x2_f32 = mat3x2<f32>();
+  sb.mat3x3_f32 = mat3x3<f32>();
+  sb.mat3x4_f32 = mat3x4<f32>();
+  sb.mat4x2_f32 = mat4x2<f32>();
+  sb.mat4x3_f32 = mat4x3<f32>();
+  sb.mat4x4_f32 = mat4x4<f32>();
+  sb.mat2x2_f16 = mat2x2<f16>();
+  sb.mat2x3_f16 = mat2x3<f16>();
+  sb.mat2x4_f16 = mat2x4<f16>();
+  sb.mat3x2_f16 = mat3x2<f16>();
+  sb.mat3x3_f16 = mat3x3<f16>();
+  sb.mat3x4_f16 = mat3x4<f16>();
+  sb.mat4x2_f16 = mat4x2<f16>();
+  sb.mat4x3_f16 = mat4x3<f16>();
+  sb.mat4x4_f16 = mat4x4<f16>();
+  sb.arr2_vec3_f32 = array<vec3<f32>, 2>();
+  sb.arr2_mat4x2_f16 = array<mat4x2<f16>, 2>();
 }
 )";
 
     auto* expect = R"(
+enable f16;
+
 struct SB {
-  a : i32,
-  b : u32,
-  c : f32,
-  d : vec2<i32>,
-  e : vec2<u32>,
-  f : vec2<f32>,
-  g : vec3<i32>,
-  h : vec3<u32>,
-  i : vec3<f32>,
-  j : vec4<i32>,
-  k : vec4<u32>,
-  l : vec4<f32>,
-  m : mat2x2<f32>,
-  n : mat2x3<f32>,
-  o : mat2x4<f32>,
-  p : mat3x2<f32>,
-  q : mat3x3<f32>,
-  r : mat3x4<f32>,
-  s : mat4x2<f32>,
-  t : mat4x3<f32>,
-  u : mat4x4<f32>,
-  v : array<vec3<f32>, 2>,
+  scalar_f32 : f32,
+  scalar_i32 : i32,
+  scalar_u32 : u32,
+  scalar_f16 : f16,
+  vec2_f32 : vec2<f32>,
+  vec2_i32 : vec2<i32>,
+  vec2_u32 : vec2<u32>,
+  vec2_f16 : vec2<f16>,
+  vec3_f32 : vec3<f32>,
+  vec3_i32 : vec3<i32>,
+  vec3_u32 : vec3<u32>,
+  vec3_f16 : vec3<f16>,
+  vec4_f32 : vec4<f32>,
+  vec4_i32 : vec4<i32>,
+  vec4_u32 : vec4<u32>,
+  vec4_f16 : vec4<f16>,
+  mat2x2_f32 : mat2x2<f32>,
+  mat2x3_f32 : mat2x3<f32>,
+  mat2x4_f32 : mat2x4<f32>,
+  mat3x2_f32 : mat3x2<f32>,
+  mat3x3_f32 : mat3x3<f32>,
+  mat3x4_f32 : mat3x4<f32>,
+  mat4x2_f32 : mat4x2<f32>,
+  mat4x3_f32 : mat4x3<f32>,
+  mat4x4_f32 : mat4x4<f32>,
+  mat2x2_f16 : mat2x2<f16>,
+  mat2x3_f16 : mat2x3<f16>,
+  mat2x4_f16 : mat2x4<f16>,
+  mat3x2_f16 : mat3x2<f16>,
+  mat3x3_f16 : mat3x3<f16>,
+  mat3x4_f16 : mat3x4<f16>,
+  mat4x2_f16 : mat4x2<f16>,
+  mat4x3_f16 : mat4x3<f16>,
+  mat4x4_f16 : mat4x4<f16>,
+  arr2_vec3_f32 : array<vec3<f32>, 2>,
+  arr2_mat4x2_f16 : array<mat4x2<f16>, 2>,
 }
 
 @group(0) @binding(0) var<storage, read_write> sb : SB;
 
+@internal(intrinsic_store_storage_f32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : f32)
+
 @internal(intrinsic_store_storage_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : i32)
+fn tint_symbol_1(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : i32)
 
 @internal(intrinsic_store_storage_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_1(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : u32)
+fn tint_symbol_2(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : u32)
 
-@internal(intrinsic_store_storage_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_2(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : f32)
-
-@internal(intrinsic_store_storage_vec2_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_3(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec2<i32>)
-
-@internal(intrinsic_store_storage_vec2_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_4(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec2<u32>)
+@internal(intrinsic_store_storage_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_3(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : f16)
 
 @internal(intrinsic_store_storage_vec2_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_5(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec2<f32>)
+fn tint_symbol_4(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec2<f32>)
 
-@internal(intrinsic_store_storage_vec3_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_6(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec3<i32>)
+@internal(intrinsic_store_storage_vec2_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_5(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec2<i32>)
 
-@internal(intrinsic_store_storage_vec3_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_7(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec3<u32>)
+@internal(intrinsic_store_storage_vec2_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_6(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec2<u32>)
+
+@internal(intrinsic_store_storage_vec2_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_7(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec2<f16>)
 
 @internal(intrinsic_store_storage_vec3_f32) @internal(disable_validation__function_has_no_body)
 fn tint_symbol_8(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec3<f32>)
 
-@internal(intrinsic_store_storage_vec4_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_9(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec4<i32>)
+@internal(intrinsic_store_storage_vec3_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_9(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec3<i32>)
 
-@internal(intrinsic_store_storage_vec4_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_10(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec4<u32>)
+@internal(intrinsic_store_storage_vec3_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_10(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec3<u32>)
+
+@internal(intrinsic_store_storage_vec3_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_11(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec3<f16>)
 
 @internal(intrinsic_store_storage_vec4_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_11(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec4<f32>)
+fn tint_symbol_12(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec4<f32>)
 
-fn tint_symbol_12(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat2x2<f32>) {
-  tint_symbol_5(buffer, (offset + 0u), value[0u]);
-  tint_symbol_5(buffer, (offset + 8u), value[1u]);
+@internal(intrinsic_store_storage_vec4_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_13(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec4<i32>)
+
+@internal(intrinsic_store_storage_vec4_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_14(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec4<u32>)
+
+@internal(intrinsic_store_storage_vec4_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_15(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec4<f16>)
+
+fn tint_symbol_16(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat2x2<f32>) {
+  tint_symbol_4(buffer, (offset + 0u), value[0u]);
+  tint_symbol_4(buffer, (offset + 8u), value[1u]);
 }
 
-fn tint_symbol_13(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat2x3<f32>) {
+fn tint_symbol_17(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat2x3<f32>) {
   tint_symbol_8(buffer, (offset + 0u), value[0u]);
   tint_symbol_8(buffer, (offset + 16u), value[1u]);
 }
 
-fn tint_symbol_14(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat2x4<f32>) {
-  tint_symbol_11(buffer, (offset + 0u), value[0u]);
-  tint_symbol_11(buffer, (offset + 16u), value[1u]);
+fn tint_symbol_18(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat2x4<f32>) {
+  tint_symbol_12(buffer, (offset + 0u), value[0u]);
+  tint_symbol_12(buffer, (offset + 16u), value[1u]);
 }
 
-fn tint_symbol_15(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat3x2<f32>) {
-  tint_symbol_5(buffer, (offset + 0u), value[0u]);
-  tint_symbol_5(buffer, (offset + 8u), value[1u]);
-  tint_symbol_5(buffer, (offset + 16u), value[2u]);
+fn tint_symbol_19(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat3x2<f32>) {
+  tint_symbol_4(buffer, (offset + 0u), value[0u]);
+  tint_symbol_4(buffer, (offset + 8u), value[1u]);
+  tint_symbol_4(buffer, (offset + 16u), value[2u]);
 }
 
-fn tint_symbol_16(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat3x3<f32>) {
+fn tint_symbol_20(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat3x3<f32>) {
   tint_symbol_8(buffer, (offset + 0u), value[0u]);
   tint_symbol_8(buffer, (offset + 16u), value[1u]);
   tint_symbol_8(buffer, (offset + 32u), value[2u]);
 }
 
-fn tint_symbol_17(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat3x4<f32>) {
-  tint_symbol_11(buffer, (offset + 0u), value[0u]);
-  tint_symbol_11(buffer, (offset + 16u), value[1u]);
-  tint_symbol_11(buffer, (offset + 32u), value[2u]);
+fn tint_symbol_21(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat3x4<f32>) {
+  tint_symbol_12(buffer, (offset + 0u), value[0u]);
+  tint_symbol_12(buffer, (offset + 16u), value[1u]);
+  tint_symbol_12(buffer, (offset + 32u), value[2u]);
 }
 
-fn tint_symbol_18(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat4x2<f32>) {
-  tint_symbol_5(buffer, (offset + 0u), value[0u]);
-  tint_symbol_5(buffer, (offset + 8u), value[1u]);
-  tint_symbol_5(buffer, (offset + 16u), value[2u]);
-  tint_symbol_5(buffer, (offset + 24u), value[3u]);
+fn tint_symbol_22(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat4x2<f32>) {
+  tint_symbol_4(buffer, (offset + 0u), value[0u]);
+  tint_symbol_4(buffer, (offset + 8u), value[1u]);
+  tint_symbol_4(buffer, (offset + 16u), value[2u]);
+  tint_symbol_4(buffer, (offset + 24u), value[3u]);
 }
 
-fn tint_symbol_19(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat4x3<f32>) {
+fn tint_symbol_23(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat4x3<f32>) {
   tint_symbol_8(buffer, (offset + 0u), value[0u]);
   tint_symbol_8(buffer, (offset + 16u), value[1u]);
   tint_symbol_8(buffer, (offset + 32u), value[2u]);
   tint_symbol_8(buffer, (offset + 48u), value[3u]);
 }
 
-fn tint_symbol_20(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat4x4<f32>) {
-  tint_symbol_11(buffer, (offset + 0u), value[0u]);
-  tint_symbol_11(buffer, (offset + 16u), value[1u]);
-  tint_symbol_11(buffer, (offset + 32u), value[2u]);
-  tint_symbol_11(buffer, (offset + 48u), value[3u]);
+fn tint_symbol_24(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat4x4<f32>) {
+  tint_symbol_12(buffer, (offset + 0u), value[0u]);
+  tint_symbol_12(buffer, (offset + 16u), value[1u]);
+  tint_symbol_12(buffer, (offset + 32u), value[2u]);
+  tint_symbol_12(buffer, (offset + 48u), value[3u]);
 }
 
-fn tint_symbol_21(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : array<vec3<f32>, 2u>) {
+fn tint_symbol_25(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat2x2<f16>) {
+  tint_symbol_7(buffer, (offset + 0u), value[0u]);
+  tint_symbol_7(buffer, (offset + 4u), value[1u]);
+}
+
+fn tint_symbol_26(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat2x3<f16>) {
+  tint_symbol_11(buffer, (offset + 0u), value[0u]);
+  tint_symbol_11(buffer, (offset + 8u), value[1u]);
+}
+
+fn tint_symbol_27(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat2x4<f16>) {
+  tint_symbol_15(buffer, (offset + 0u), value[0u]);
+  tint_symbol_15(buffer, (offset + 8u), value[1u]);
+}
+
+fn tint_symbol_28(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat3x2<f16>) {
+  tint_symbol_7(buffer, (offset + 0u), value[0u]);
+  tint_symbol_7(buffer, (offset + 4u), value[1u]);
+  tint_symbol_7(buffer, (offset + 8u), value[2u]);
+}
+
+fn tint_symbol_29(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat3x3<f16>) {
+  tint_symbol_11(buffer, (offset + 0u), value[0u]);
+  tint_symbol_11(buffer, (offset + 8u), value[1u]);
+  tint_symbol_11(buffer, (offset + 16u), value[2u]);
+}
+
+fn tint_symbol_30(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat3x4<f16>) {
+  tint_symbol_15(buffer, (offset + 0u), value[0u]);
+  tint_symbol_15(buffer, (offset + 8u), value[1u]);
+  tint_symbol_15(buffer, (offset + 16u), value[2u]);
+}
+
+fn tint_symbol_31(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat4x2<f16>) {
+  tint_symbol_7(buffer, (offset + 0u), value[0u]);
+  tint_symbol_7(buffer, (offset + 4u), value[1u]);
+  tint_symbol_7(buffer, (offset + 8u), value[2u]);
+  tint_symbol_7(buffer, (offset + 12u), value[3u]);
+}
+
+fn tint_symbol_32(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat4x3<f16>) {
+  tint_symbol_11(buffer, (offset + 0u), value[0u]);
+  tint_symbol_11(buffer, (offset + 8u), value[1u]);
+  tint_symbol_11(buffer, (offset + 16u), value[2u]);
+  tint_symbol_11(buffer, (offset + 24u), value[3u]);
+}
+
+fn tint_symbol_33(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat4x4<f16>) {
+  tint_symbol_15(buffer, (offset + 0u), value[0u]);
+  tint_symbol_15(buffer, (offset + 8u), value[1u]);
+  tint_symbol_15(buffer, (offset + 16u), value[2u]);
+  tint_symbol_15(buffer, (offset + 24u), value[3u]);
+}
+
+fn tint_symbol_34(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : array<vec3<f32>, 2u>) {
   var array = value;
+  for(var i = 0u; (i < 2u); i = (i + 1u)) {
+    tint_symbol_8(buffer, (offset + (i * 16u)), array[i]);
+  }
+}
+
+fn tint_symbol_35(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : array<mat4x2<f16>, 2u>) {
+  var array_1 = value;
   for(var i_1 = 0u; (i_1 < 2u); i_1 = (i_1 + 1u)) {
-    tint_symbol_8(buffer, (offset + (i_1 * 16u)), array[i_1]);
+    tint_symbol_31(buffer, (offset + (i_1 * 16u)), array_1[i_1]);
   }
 }
 
 @compute @workgroup_size(1)
 fn main() {
-  tint_symbol(&(sb), 0u, i32());
-  tint_symbol_1(&(sb), 4u, u32());
-  tint_symbol_2(&(sb), 8u, f32());
-  tint_symbol_3(&(sb), 16u, vec2<i32>());
-  tint_symbol_4(&(sb), 24u, vec2<u32>());
-  tint_symbol_5(&(sb), 32u, vec2<f32>());
-  tint_symbol_6(&(sb), 48u, vec3<i32>());
-  tint_symbol_7(&(sb), 64u, vec3<u32>());
-  tint_symbol_8(&(sb), 80u, vec3<f32>());
-  tint_symbol_9(&(sb), 96u, vec4<i32>());
-  tint_symbol_10(&(sb), 112u, vec4<u32>());
-  tint_symbol_11(&(sb), 128u, vec4<f32>());
-  tint_symbol_12(&(sb), 144u, mat2x2<f32>());
-  tint_symbol_13(&(sb), 160u, mat2x3<f32>());
-  tint_symbol_14(&(sb), 192u, mat2x4<f32>());
-  tint_symbol_15(&(sb), 224u, mat3x2<f32>());
-  tint_symbol_16(&(sb), 256u, mat3x3<f32>());
-  tint_symbol_17(&(sb), 304u, mat3x4<f32>());
-  tint_symbol_18(&(sb), 352u, mat4x2<f32>());
-  tint_symbol_19(&(sb), 384u, mat4x3<f32>());
-  tint_symbol_20(&(sb), 448u, mat4x4<f32>());
-  tint_symbol_21(&(sb), 512u, array<vec3<f32>, 2>());
+  tint_symbol(&(sb), 0u, f32());
+  tint_symbol_1(&(sb), 4u, i32());
+  tint_symbol_2(&(sb), 8u, u32());
+  tint_symbol_3(&(sb), 12u, f16());
+  tint_symbol_4(&(sb), 16u, vec2<f32>());
+  tint_symbol_5(&(sb), 24u, vec2<i32>());
+  tint_symbol_6(&(sb), 32u, vec2<u32>());
+  tint_symbol_7(&(sb), 40u, vec2<f16>());
+  tint_symbol_8(&(sb), 48u, vec3<f32>());
+  tint_symbol_9(&(sb), 64u, vec3<i32>());
+  tint_symbol_10(&(sb), 80u, vec3<u32>());
+  tint_symbol_11(&(sb), 96u, vec3<f16>());
+  tint_symbol_12(&(sb), 112u, vec4<f32>());
+  tint_symbol_13(&(sb), 128u, vec4<i32>());
+  tint_symbol_14(&(sb), 144u, vec4<u32>());
+  tint_symbol_15(&(sb), 160u, vec4<f16>());
+  tint_symbol_16(&(sb), 168u, mat2x2<f32>());
+  tint_symbol_17(&(sb), 192u, mat2x3<f32>());
+  tint_symbol_18(&(sb), 224u, mat2x4<f32>());
+  tint_symbol_19(&(sb), 256u, mat3x2<f32>());
+  tint_symbol_20(&(sb), 288u, mat3x3<f32>());
+  tint_symbol_21(&(sb), 336u, mat3x4<f32>());
+  tint_symbol_22(&(sb), 384u, mat4x2<f32>());
+  tint_symbol_23(&(sb), 416u, mat4x3<f32>());
+  tint_symbol_24(&(sb), 480u, mat4x4<f32>());
+  tint_symbol_25(&(sb), 544u, mat2x2<f16>());
+  tint_symbol_26(&(sb), 552u, mat2x3<f16>());
+  tint_symbol_27(&(sb), 568u, mat2x4<f16>());
+  tint_symbol_28(&(sb), 584u, mat3x2<f16>());
+  tint_symbol_29(&(sb), 600u, mat3x3<f16>());
+  tint_symbol_30(&(sb), 624u, mat3x4<f16>());
+  tint_symbol_31(&(sb), 648u, mat4x2<f16>());
+  tint_symbol_32(&(sb), 664u, mat4x3<f16>());
+  tint_symbol_33(&(sb), 696u, mat4x4<f16>());
+  tint_symbol_34(&(sb), 736u, array<vec3<f32>, 2>());
+  tint_symbol_35(&(sb), 768u, array<mat4x2<f16>, 2>());
 }
 )";
 
@@ -1048,209 +1645,342 @@
 
 TEST_F(DecomposeMemoryAccessTest, SB_BasicStore_OutOfOrder) {
     auto* src = R"(
+enable f16;
+
 @compute @workgroup_size(1)
 fn main() {
-  sb.a = i32();
-  sb.b = u32();
-  sb.c = f32();
-  sb.d = vec2<i32>();
-  sb.e = vec2<u32>();
-  sb.f = vec2<f32>();
-  sb.g = vec3<i32>();
-  sb.h = vec3<u32>();
-  sb.i = vec3<f32>();
-  sb.j = vec4<i32>();
-  sb.k = vec4<u32>();
-  sb.l = vec4<f32>();
-  sb.m = mat2x2<f32>();
-  sb.n = mat2x3<f32>();
-  sb.o = mat2x4<f32>();
-  sb.p = mat3x2<f32>();
-  sb.q = mat3x3<f32>();
-  sb.r = mat3x4<f32>();
-  sb.s = mat4x2<f32>();
-  sb.t = mat4x3<f32>();
-  sb.u = mat4x4<f32>();
-  sb.v = array<vec3<f32>, 2>();
+  sb.scalar_f32 = f32();
+  sb.scalar_i32 = i32();
+  sb.scalar_u32 = u32();
+  sb.scalar_f16 = f16();
+  sb.vec2_f32 = vec2<f32>();
+  sb.vec2_i32 = vec2<i32>();
+  sb.vec2_u32 = vec2<u32>();
+  sb.vec2_f16 = vec2<f16>();
+  sb.vec3_f32 = vec3<f32>();
+  sb.vec3_i32 = vec3<i32>();
+  sb.vec3_u32 = vec3<u32>();
+  sb.vec3_f16 = vec3<f16>();
+  sb.vec4_f32 = vec4<f32>();
+  sb.vec4_i32 = vec4<i32>();
+  sb.vec4_u32 = vec4<u32>();
+  sb.vec4_f16 = vec4<f16>();
+  sb.mat2x2_f32 = mat2x2<f32>();
+  sb.mat2x3_f32 = mat2x3<f32>();
+  sb.mat2x4_f32 = mat2x4<f32>();
+  sb.mat3x2_f32 = mat3x2<f32>();
+  sb.mat3x3_f32 = mat3x3<f32>();
+  sb.mat3x4_f32 = mat3x4<f32>();
+  sb.mat4x2_f32 = mat4x2<f32>();
+  sb.mat4x3_f32 = mat4x3<f32>();
+  sb.mat4x4_f32 = mat4x4<f32>();
+  sb.mat2x2_f16 = mat2x2<f16>();
+  sb.mat2x3_f16 = mat2x3<f16>();
+  sb.mat2x4_f16 = mat2x4<f16>();
+  sb.mat3x2_f16 = mat3x2<f16>();
+  sb.mat3x3_f16 = mat3x3<f16>();
+  sb.mat3x4_f16 = mat3x4<f16>();
+  sb.mat4x2_f16 = mat4x2<f16>();
+  sb.mat4x3_f16 = mat4x3<f16>();
+  sb.mat4x4_f16 = mat4x4<f16>();
+  sb.arr2_vec3_f32 = array<vec3<f32>, 2>();
+  sb.arr2_mat4x2_f16 = array<mat4x2<f16>, 2>();
 }
 
 @group(0) @binding(0) var<storage, read_write> sb : SB;
 
 struct SB {
-  a : i32,
-  b : u32,
-  c : f32,
-  d : vec2<i32>,
-  e : vec2<u32>,
-  f : vec2<f32>,
-  g : vec3<i32>,
-  h : vec3<u32>,
-  i : vec3<f32>,
-  j : vec4<i32>,
-  k : vec4<u32>,
-  l : vec4<f32>,
-  m : mat2x2<f32>,
-  n : mat2x3<f32>,
-  o : mat2x4<f32>,
-  p : mat3x2<f32>,
-  q : mat3x3<f32>,
-  r : mat3x4<f32>,
-  s : mat4x2<f32>,
-  t : mat4x3<f32>,
-  u : mat4x4<f32>,
-  v : array<vec3<f32>, 2>,
+  scalar_f32 : f32,
+  scalar_i32 : i32,
+  scalar_u32 : u32,
+  scalar_f16 : f16,
+  vec2_f32 : vec2<f32>,
+  vec2_i32 : vec2<i32>,
+  vec2_u32 : vec2<u32>,
+  vec2_f16 : vec2<f16>,
+  vec3_f32 : vec3<f32>,
+  vec3_i32 : vec3<i32>,
+  vec3_u32 : vec3<u32>,
+  vec3_f16 : vec3<f16>,
+  vec4_f32 : vec4<f32>,
+  vec4_i32 : vec4<i32>,
+  vec4_u32 : vec4<u32>,
+  vec4_f16 : vec4<f16>,
+  mat2x2_f32 : mat2x2<f32>,
+  mat2x3_f32 : mat2x3<f32>,
+  mat2x4_f32 : mat2x4<f32>,
+  mat3x2_f32 : mat3x2<f32>,
+  mat3x3_f32 : mat3x3<f32>,
+  mat3x4_f32 : mat3x4<f32>,
+  mat4x2_f32 : mat4x2<f32>,
+  mat4x3_f32 : mat4x3<f32>,
+  mat4x4_f32 : mat4x4<f32>,
+  mat2x2_f16 : mat2x2<f16>,
+  mat2x3_f16 : mat2x3<f16>,
+  mat2x4_f16 : mat2x4<f16>,
+  mat3x2_f16 : mat3x2<f16>,
+  mat3x3_f16 : mat3x3<f16>,
+  mat3x4_f16 : mat3x4<f16>,
+  mat4x2_f16 : mat4x2<f16>,
+  mat4x3_f16 : mat4x3<f16>,
+  mat4x4_f16 : mat4x4<f16>,
+  arr2_vec3_f32 : array<vec3<f32>, 2>,
+  arr2_mat4x2_f16 : array<mat4x2<f16>, 2>,
 };
 )";
 
     auto* expect = R"(
-@internal(intrinsic_store_storage_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : i32)
-
-@internal(intrinsic_store_storage_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_1(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : u32)
+enable f16;
 
 @internal(intrinsic_store_storage_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_2(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : f32)
+fn tint_symbol(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : f32)
 
-@internal(intrinsic_store_storage_vec2_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_3(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec2<i32>)
+@internal(intrinsic_store_storage_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_1(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : i32)
 
-@internal(intrinsic_store_storage_vec2_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_4(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec2<u32>)
+@internal(intrinsic_store_storage_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_2(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : u32)
+
+@internal(intrinsic_store_storage_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_3(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : f16)
 
 @internal(intrinsic_store_storage_vec2_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_5(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec2<f32>)
+fn tint_symbol_4(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec2<f32>)
 
-@internal(intrinsic_store_storage_vec3_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_6(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec3<i32>)
+@internal(intrinsic_store_storage_vec2_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_5(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec2<i32>)
 
-@internal(intrinsic_store_storage_vec3_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_7(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec3<u32>)
+@internal(intrinsic_store_storage_vec2_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_6(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec2<u32>)
+
+@internal(intrinsic_store_storage_vec2_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_7(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec2<f16>)
 
 @internal(intrinsic_store_storage_vec3_f32) @internal(disable_validation__function_has_no_body)
 fn tint_symbol_8(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec3<f32>)
 
-@internal(intrinsic_store_storage_vec4_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_9(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec4<i32>)
+@internal(intrinsic_store_storage_vec3_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_9(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec3<i32>)
 
-@internal(intrinsic_store_storage_vec4_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_10(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec4<u32>)
+@internal(intrinsic_store_storage_vec3_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_10(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec3<u32>)
+
+@internal(intrinsic_store_storage_vec3_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_11(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec3<f16>)
 
 @internal(intrinsic_store_storage_vec4_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_11(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec4<f32>)
+fn tint_symbol_12(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec4<f32>)
 
-fn tint_symbol_12(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat2x2<f32>) {
-  tint_symbol_5(buffer, (offset + 0u), value[0u]);
-  tint_symbol_5(buffer, (offset + 8u), value[1u]);
+@internal(intrinsic_store_storage_vec4_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_13(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec4<i32>)
+
+@internal(intrinsic_store_storage_vec4_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_14(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec4<u32>)
+
+@internal(intrinsic_store_storage_vec4_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_15(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec4<f16>)
+
+fn tint_symbol_16(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat2x2<f32>) {
+  tint_symbol_4(buffer, (offset + 0u), value[0u]);
+  tint_symbol_4(buffer, (offset + 8u), value[1u]);
 }
 
-fn tint_symbol_13(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat2x3<f32>) {
+fn tint_symbol_17(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat2x3<f32>) {
   tint_symbol_8(buffer, (offset + 0u), value[0u]);
   tint_symbol_8(buffer, (offset + 16u), value[1u]);
 }
 
-fn tint_symbol_14(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat2x4<f32>) {
-  tint_symbol_11(buffer, (offset + 0u), value[0u]);
-  tint_symbol_11(buffer, (offset + 16u), value[1u]);
+fn tint_symbol_18(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat2x4<f32>) {
+  tint_symbol_12(buffer, (offset + 0u), value[0u]);
+  tint_symbol_12(buffer, (offset + 16u), value[1u]);
 }
 
-fn tint_symbol_15(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat3x2<f32>) {
-  tint_symbol_5(buffer, (offset + 0u), value[0u]);
-  tint_symbol_5(buffer, (offset + 8u), value[1u]);
-  tint_symbol_5(buffer, (offset + 16u), value[2u]);
+fn tint_symbol_19(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat3x2<f32>) {
+  tint_symbol_4(buffer, (offset + 0u), value[0u]);
+  tint_symbol_4(buffer, (offset + 8u), value[1u]);
+  tint_symbol_4(buffer, (offset + 16u), value[2u]);
 }
 
-fn tint_symbol_16(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat3x3<f32>) {
+fn tint_symbol_20(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat3x3<f32>) {
   tint_symbol_8(buffer, (offset + 0u), value[0u]);
   tint_symbol_8(buffer, (offset + 16u), value[1u]);
   tint_symbol_8(buffer, (offset + 32u), value[2u]);
 }
 
-fn tint_symbol_17(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat3x4<f32>) {
-  tint_symbol_11(buffer, (offset + 0u), value[0u]);
-  tint_symbol_11(buffer, (offset + 16u), value[1u]);
-  tint_symbol_11(buffer, (offset + 32u), value[2u]);
+fn tint_symbol_21(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat3x4<f32>) {
+  tint_symbol_12(buffer, (offset + 0u), value[0u]);
+  tint_symbol_12(buffer, (offset + 16u), value[1u]);
+  tint_symbol_12(buffer, (offset + 32u), value[2u]);
 }
 
-fn tint_symbol_18(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat4x2<f32>) {
-  tint_symbol_5(buffer, (offset + 0u), value[0u]);
-  tint_symbol_5(buffer, (offset + 8u), value[1u]);
-  tint_symbol_5(buffer, (offset + 16u), value[2u]);
-  tint_symbol_5(buffer, (offset + 24u), value[3u]);
+fn tint_symbol_22(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat4x2<f32>) {
+  tint_symbol_4(buffer, (offset + 0u), value[0u]);
+  tint_symbol_4(buffer, (offset + 8u), value[1u]);
+  tint_symbol_4(buffer, (offset + 16u), value[2u]);
+  tint_symbol_4(buffer, (offset + 24u), value[3u]);
 }
 
-fn tint_symbol_19(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat4x3<f32>) {
+fn tint_symbol_23(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat4x3<f32>) {
   tint_symbol_8(buffer, (offset + 0u), value[0u]);
   tint_symbol_8(buffer, (offset + 16u), value[1u]);
   tint_symbol_8(buffer, (offset + 32u), value[2u]);
   tint_symbol_8(buffer, (offset + 48u), value[3u]);
 }
 
-fn tint_symbol_20(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat4x4<f32>) {
-  tint_symbol_11(buffer, (offset + 0u), value[0u]);
-  tint_symbol_11(buffer, (offset + 16u), value[1u]);
-  tint_symbol_11(buffer, (offset + 32u), value[2u]);
-  tint_symbol_11(buffer, (offset + 48u), value[3u]);
+fn tint_symbol_24(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat4x4<f32>) {
+  tint_symbol_12(buffer, (offset + 0u), value[0u]);
+  tint_symbol_12(buffer, (offset + 16u), value[1u]);
+  tint_symbol_12(buffer, (offset + 32u), value[2u]);
+  tint_symbol_12(buffer, (offset + 48u), value[3u]);
 }
 
-fn tint_symbol_21(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : array<vec3<f32>, 2u>) {
+fn tint_symbol_25(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat2x2<f16>) {
+  tint_symbol_7(buffer, (offset + 0u), value[0u]);
+  tint_symbol_7(buffer, (offset + 4u), value[1u]);
+}
+
+fn tint_symbol_26(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat2x3<f16>) {
+  tint_symbol_11(buffer, (offset + 0u), value[0u]);
+  tint_symbol_11(buffer, (offset + 8u), value[1u]);
+}
+
+fn tint_symbol_27(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat2x4<f16>) {
+  tint_symbol_15(buffer, (offset + 0u), value[0u]);
+  tint_symbol_15(buffer, (offset + 8u), value[1u]);
+}
+
+fn tint_symbol_28(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat3x2<f16>) {
+  tint_symbol_7(buffer, (offset + 0u), value[0u]);
+  tint_symbol_7(buffer, (offset + 4u), value[1u]);
+  tint_symbol_7(buffer, (offset + 8u), value[2u]);
+}
+
+fn tint_symbol_29(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat3x3<f16>) {
+  tint_symbol_11(buffer, (offset + 0u), value[0u]);
+  tint_symbol_11(buffer, (offset + 8u), value[1u]);
+  tint_symbol_11(buffer, (offset + 16u), value[2u]);
+}
+
+fn tint_symbol_30(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat3x4<f16>) {
+  tint_symbol_15(buffer, (offset + 0u), value[0u]);
+  tint_symbol_15(buffer, (offset + 8u), value[1u]);
+  tint_symbol_15(buffer, (offset + 16u), value[2u]);
+}
+
+fn tint_symbol_31(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat4x2<f16>) {
+  tint_symbol_7(buffer, (offset + 0u), value[0u]);
+  tint_symbol_7(buffer, (offset + 4u), value[1u]);
+  tint_symbol_7(buffer, (offset + 8u), value[2u]);
+  tint_symbol_7(buffer, (offset + 12u), value[3u]);
+}
+
+fn tint_symbol_32(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat4x3<f16>) {
+  tint_symbol_11(buffer, (offset + 0u), value[0u]);
+  tint_symbol_11(buffer, (offset + 8u), value[1u]);
+  tint_symbol_11(buffer, (offset + 16u), value[2u]);
+  tint_symbol_11(buffer, (offset + 24u), value[3u]);
+}
+
+fn tint_symbol_33(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat4x4<f16>) {
+  tint_symbol_15(buffer, (offset + 0u), value[0u]);
+  tint_symbol_15(buffer, (offset + 8u), value[1u]);
+  tint_symbol_15(buffer, (offset + 16u), value[2u]);
+  tint_symbol_15(buffer, (offset + 24u), value[3u]);
+}
+
+fn tint_symbol_34(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : array<vec3<f32>, 2u>) {
   var array = value;
+  for(var i = 0u; (i < 2u); i = (i + 1u)) {
+    tint_symbol_8(buffer, (offset + (i * 16u)), array[i]);
+  }
+}
+
+fn tint_symbol_35(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : array<mat4x2<f16>, 2u>) {
+  var array_1 = value;
   for(var i_1 = 0u; (i_1 < 2u); i_1 = (i_1 + 1u)) {
-    tint_symbol_8(buffer, (offset + (i_1 * 16u)), array[i_1]);
+    tint_symbol_31(buffer, (offset + (i_1 * 16u)), array_1[i_1]);
   }
 }
 
 @compute @workgroup_size(1)
 fn main() {
-  tint_symbol(&(sb), 0u, i32());
-  tint_symbol_1(&(sb), 4u, u32());
-  tint_symbol_2(&(sb), 8u, f32());
-  tint_symbol_3(&(sb), 16u, vec2<i32>());
-  tint_symbol_4(&(sb), 24u, vec2<u32>());
-  tint_symbol_5(&(sb), 32u, vec2<f32>());
-  tint_symbol_6(&(sb), 48u, vec3<i32>());
-  tint_symbol_7(&(sb), 64u, vec3<u32>());
-  tint_symbol_8(&(sb), 80u, vec3<f32>());
-  tint_symbol_9(&(sb), 96u, vec4<i32>());
-  tint_symbol_10(&(sb), 112u, vec4<u32>());
-  tint_symbol_11(&(sb), 128u, vec4<f32>());
-  tint_symbol_12(&(sb), 144u, mat2x2<f32>());
-  tint_symbol_13(&(sb), 160u, mat2x3<f32>());
-  tint_symbol_14(&(sb), 192u, mat2x4<f32>());
-  tint_symbol_15(&(sb), 224u, mat3x2<f32>());
-  tint_symbol_16(&(sb), 256u, mat3x3<f32>());
-  tint_symbol_17(&(sb), 304u, mat3x4<f32>());
-  tint_symbol_18(&(sb), 352u, mat4x2<f32>());
-  tint_symbol_19(&(sb), 384u, mat4x3<f32>());
-  tint_symbol_20(&(sb), 448u, mat4x4<f32>());
-  tint_symbol_21(&(sb), 512u, array<vec3<f32>, 2>());
+  tint_symbol(&(sb), 0u, f32());
+  tint_symbol_1(&(sb), 4u, i32());
+  tint_symbol_2(&(sb), 8u, u32());
+  tint_symbol_3(&(sb), 12u, f16());
+  tint_symbol_4(&(sb), 16u, vec2<f32>());
+  tint_symbol_5(&(sb), 24u, vec2<i32>());
+  tint_symbol_6(&(sb), 32u, vec2<u32>());
+  tint_symbol_7(&(sb), 40u, vec2<f16>());
+  tint_symbol_8(&(sb), 48u, vec3<f32>());
+  tint_symbol_9(&(sb), 64u, vec3<i32>());
+  tint_symbol_10(&(sb), 80u, vec3<u32>());
+  tint_symbol_11(&(sb), 96u, vec3<f16>());
+  tint_symbol_12(&(sb), 112u, vec4<f32>());
+  tint_symbol_13(&(sb), 128u, vec4<i32>());
+  tint_symbol_14(&(sb), 144u, vec4<u32>());
+  tint_symbol_15(&(sb), 160u, vec4<f16>());
+  tint_symbol_16(&(sb), 168u, mat2x2<f32>());
+  tint_symbol_17(&(sb), 192u, mat2x3<f32>());
+  tint_symbol_18(&(sb), 224u, mat2x4<f32>());
+  tint_symbol_19(&(sb), 256u, mat3x2<f32>());
+  tint_symbol_20(&(sb), 288u, mat3x3<f32>());
+  tint_symbol_21(&(sb), 336u, mat3x4<f32>());
+  tint_symbol_22(&(sb), 384u, mat4x2<f32>());
+  tint_symbol_23(&(sb), 416u, mat4x3<f32>());
+  tint_symbol_24(&(sb), 480u, mat4x4<f32>());
+  tint_symbol_25(&(sb), 544u, mat2x2<f16>());
+  tint_symbol_26(&(sb), 552u, mat2x3<f16>());
+  tint_symbol_27(&(sb), 568u, mat2x4<f16>());
+  tint_symbol_28(&(sb), 584u, mat3x2<f16>());
+  tint_symbol_29(&(sb), 600u, mat3x3<f16>());
+  tint_symbol_30(&(sb), 624u, mat3x4<f16>());
+  tint_symbol_31(&(sb), 648u, mat4x2<f16>());
+  tint_symbol_32(&(sb), 664u, mat4x3<f16>());
+  tint_symbol_33(&(sb), 696u, mat4x4<f16>());
+  tint_symbol_34(&(sb), 736u, array<vec3<f32>, 2>());
+  tint_symbol_35(&(sb), 768u, array<mat4x2<f16>, 2>());
 }
 
 @group(0) @binding(0) var<storage, read_write> sb : SB;
 
 struct SB {
-  a : i32,
-  b : u32,
-  c : f32,
-  d : vec2<i32>,
-  e : vec2<u32>,
-  f : vec2<f32>,
-  g : vec3<i32>,
-  h : vec3<u32>,
-  i : vec3<f32>,
-  j : vec4<i32>,
-  k : vec4<u32>,
-  l : vec4<f32>,
-  m : mat2x2<f32>,
-  n : mat2x3<f32>,
-  o : mat2x4<f32>,
-  p : mat3x2<f32>,
-  q : mat3x3<f32>,
-  r : mat3x4<f32>,
-  s : mat4x2<f32>,
-  t : mat4x3<f32>,
-  u : mat4x4<f32>,
-  v : array<vec3<f32>, 2>,
+  scalar_f32 : f32,
+  scalar_i32 : i32,
+  scalar_u32 : u32,
+  scalar_f16 : f16,
+  vec2_f32 : vec2<f32>,
+  vec2_i32 : vec2<i32>,
+  vec2_u32 : vec2<u32>,
+  vec2_f16 : vec2<f16>,
+  vec3_f32 : vec3<f32>,
+  vec3_i32 : vec3<i32>,
+  vec3_u32 : vec3<u32>,
+  vec3_f16 : vec3<f16>,
+  vec4_f32 : vec4<f32>,
+  vec4_i32 : vec4<i32>,
+  vec4_u32 : vec4<u32>,
+  vec4_f16 : vec4<f16>,
+  mat2x2_f32 : mat2x2<f32>,
+  mat2x3_f32 : mat2x3<f32>,
+  mat2x4_f32 : mat2x4<f32>,
+  mat3x2_f32 : mat3x2<f32>,
+  mat3x3_f32 : mat3x3<f32>,
+  mat3x4_f32 : mat3x4<f32>,
+  mat4x2_f32 : mat4x2<f32>,
+  mat4x3_f32 : mat4x3<f32>,
+  mat4x4_f32 : mat4x4<f32>,
+  mat2x2_f16 : mat2x2<f16>,
+  mat2x3_f16 : mat2x3<f16>,
+  mat2x4_f16 : mat2x4<f16>,
+  mat3x2_f16 : mat3x2<f16>,
+  mat3x3_f16 : mat3x3<f16>,
+  mat3x4_f16 : mat3x4<f16>,
+  mat4x2_f16 : mat4x2<f16>,
+  mat4x3_f16 : mat4x3<f16>,
+  mat4x4_f16 : mat4x4<f16>,
+  arr2_vec3_f32 : array<vec3<f32>, 2>,
+  arr2_mat4x2_f16 : array<mat4x2<f16>, 2>,
 }
 )";
 
@@ -1261,29 +1991,45 @@
 
 TEST_F(DecomposeMemoryAccessTest, LoadStructure) {
     auto* src = R"(
+enable f16;
+
 struct SB {
-  a : i32,
-  b : u32,
-  c : f32,
-  d : vec2<i32>,
-  e : vec2<u32>,
-  f : vec2<f32>,
-  g : vec3<i32>,
-  h : vec3<u32>,
-  i : vec3<f32>,
-  j : vec4<i32>,
-  k : vec4<u32>,
-  l : vec4<f32>,
-  m : mat2x2<f32>,
-  n : mat2x3<f32>,
-  o : mat2x4<f32>,
-  p : mat3x2<f32>,
-  q : mat3x3<f32>,
-  r : mat3x4<f32>,
-  s : mat4x2<f32>,
-  t : mat4x3<f32>,
-  u : mat4x4<f32>,
-  v : array<vec3<f32>, 2>,
+  scalar_f32 : f32,
+  scalar_i32 : i32,
+  scalar_u32 : u32,
+  scalar_f16 : f16,
+  vec2_f32 : vec2<f32>,
+  vec2_i32 : vec2<i32>,
+  vec2_u32 : vec2<u32>,
+  vec2_f16 : vec2<f16>,
+  vec3_f32 : vec3<f32>,
+  vec3_i32 : vec3<i32>,
+  vec3_u32 : vec3<u32>,
+  vec3_f16 : vec3<f16>,
+  vec4_f32 : vec4<f32>,
+  vec4_i32 : vec4<i32>,
+  vec4_u32 : vec4<u32>,
+  vec4_f16 : vec4<f16>,
+  mat2x2_f32 : mat2x2<f32>,
+  mat2x3_f32 : mat2x3<f32>,
+  mat2x4_f32 : mat2x4<f32>,
+  mat3x2_f32 : mat3x2<f32>,
+  mat3x3_f32 : mat3x3<f32>,
+  mat3x4_f32 : mat3x4<f32>,
+  mat4x2_f32 : mat4x2<f32>,
+  mat4x3_f32 : mat4x3<f32>,
+  mat4x4_f32 : mat4x4<f32>,
+  mat2x2_f16 : mat2x2<f16>,
+  mat2x3_f16 : mat2x3<f16>,
+  mat2x4_f16 : mat2x4<f16>,
+  mat3x2_f16 : mat3x2<f16>,
+  mat3x3_f16 : mat3x3<f16>,
+  mat3x4_f16 : mat3x4<f16>,
+  mat4x2_f16 : mat4x2<f16>,
+  mat4x3_f16 : mat4x3<f16>,
+  mat4x4_f16 : mat4x4<f16>,
+  arr2_vec3_f32 : array<vec3<f32>, 2>,
+  arr2_mat4x2_f16 : array<mat4x2<f16>, 2>,
 };
 
 @group(0) @binding(0) var<storage, read_write> sb : SB;
@@ -1295,115 +2041,187 @@
 )";
 
     auto* expect = R"(
+enable f16;
+
 struct SB {
-  a : i32,
-  b : u32,
-  c : f32,
-  d : vec2<i32>,
-  e : vec2<u32>,
-  f : vec2<f32>,
-  g : vec3<i32>,
-  h : vec3<u32>,
-  i : vec3<f32>,
-  j : vec4<i32>,
-  k : vec4<u32>,
-  l : vec4<f32>,
-  m : mat2x2<f32>,
-  n : mat2x3<f32>,
-  o : mat2x4<f32>,
-  p : mat3x2<f32>,
-  q : mat3x3<f32>,
-  r : mat3x4<f32>,
-  s : mat4x2<f32>,
-  t : mat4x3<f32>,
-  u : mat4x4<f32>,
-  v : array<vec3<f32>, 2>,
+  scalar_f32 : f32,
+  scalar_i32 : i32,
+  scalar_u32 : u32,
+  scalar_f16 : f16,
+  vec2_f32 : vec2<f32>,
+  vec2_i32 : vec2<i32>,
+  vec2_u32 : vec2<u32>,
+  vec2_f16 : vec2<f16>,
+  vec3_f32 : vec3<f32>,
+  vec3_i32 : vec3<i32>,
+  vec3_u32 : vec3<u32>,
+  vec3_f16 : vec3<f16>,
+  vec4_f32 : vec4<f32>,
+  vec4_i32 : vec4<i32>,
+  vec4_u32 : vec4<u32>,
+  vec4_f16 : vec4<f16>,
+  mat2x2_f32 : mat2x2<f32>,
+  mat2x3_f32 : mat2x3<f32>,
+  mat2x4_f32 : mat2x4<f32>,
+  mat3x2_f32 : mat3x2<f32>,
+  mat3x3_f32 : mat3x3<f32>,
+  mat3x4_f32 : mat3x4<f32>,
+  mat4x2_f32 : mat4x2<f32>,
+  mat4x3_f32 : mat4x3<f32>,
+  mat4x4_f32 : mat4x4<f32>,
+  mat2x2_f16 : mat2x2<f16>,
+  mat2x3_f16 : mat2x3<f16>,
+  mat2x4_f16 : mat2x4<f16>,
+  mat3x2_f16 : mat3x2<f16>,
+  mat3x3_f16 : mat3x3<f16>,
+  mat3x4_f16 : mat3x4<f16>,
+  mat4x2_f16 : mat4x2<f16>,
+  mat4x3_f16 : mat4x3<f16>,
+  mat4x4_f16 : mat4x4<f16>,
+  arr2_vec3_f32 : array<vec3<f32>, 2>,
+  arr2_mat4x2_f16 : array<mat4x2<f16>, 2>,
 }
 
 @group(0) @binding(0) var<storage, read_write> sb : SB;
 
+@internal(intrinsic_load_storage_f32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_1(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> f32
+
 @internal(intrinsic_load_storage_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_1(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> i32
+fn tint_symbol_2(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> i32
 
 @internal(intrinsic_load_storage_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_2(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> u32
+fn tint_symbol_3(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> u32
 
-@internal(intrinsic_load_storage_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_3(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> f32
-
-@internal(intrinsic_load_storage_vec2_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_4(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec2<i32>
-
-@internal(intrinsic_load_storage_vec2_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_5(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec2<u32>
+@internal(intrinsic_load_storage_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_4(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> f16
 
 @internal(intrinsic_load_storage_vec2_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_6(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec2<f32>
+fn tint_symbol_5(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec2<f32>
 
-@internal(intrinsic_load_storage_vec3_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_7(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec3<i32>
+@internal(intrinsic_load_storage_vec2_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_6(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec2<i32>
 
-@internal(intrinsic_load_storage_vec3_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_8(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec3<u32>
+@internal(intrinsic_load_storage_vec2_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_7(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec2<u32>
+
+@internal(intrinsic_load_storage_vec2_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_8(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec2<f16>
 
 @internal(intrinsic_load_storage_vec3_f32) @internal(disable_validation__function_has_no_body)
 fn tint_symbol_9(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec3<f32>
 
-@internal(intrinsic_load_storage_vec4_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_10(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec4<i32>
+@internal(intrinsic_load_storage_vec3_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_10(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec3<i32>
 
-@internal(intrinsic_load_storage_vec4_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_11(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec4<u32>
+@internal(intrinsic_load_storage_vec3_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_11(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec3<u32>
+
+@internal(intrinsic_load_storage_vec3_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_12(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec3<f16>
 
 @internal(intrinsic_load_storage_vec4_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_12(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec4<f32>
+fn tint_symbol_13(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec4<f32>
 
-fn tint_symbol_13(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat2x2<f32> {
-  return mat2x2<f32>(tint_symbol_6(buffer, (offset + 0u)), tint_symbol_6(buffer, (offset + 8u)));
+@internal(intrinsic_load_storage_vec4_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_14(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec4<i32>
+
+@internal(intrinsic_load_storage_vec4_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_15(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec4<u32>
+
+@internal(intrinsic_load_storage_vec4_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_16(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec4<f16>
+
+fn tint_symbol_17(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat2x2<f32> {
+  return mat2x2<f32>(tint_symbol_5(buffer, (offset + 0u)), tint_symbol_5(buffer, (offset + 8u)));
 }
 
-fn tint_symbol_14(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat2x3<f32> {
+fn tint_symbol_18(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat2x3<f32> {
   return mat2x3<f32>(tint_symbol_9(buffer, (offset + 0u)), tint_symbol_9(buffer, (offset + 16u)));
 }
 
-fn tint_symbol_15(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat2x4<f32> {
-  return mat2x4<f32>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 16u)));
+fn tint_symbol_19(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat2x4<f32> {
+  return mat2x4<f32>(tint_symbol_13(buffer, (offset + 0u)), tint_symbol_13(buffer, (offset + 16u)));
 }
 
-fn tint_symbol_16(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat3x2<f32> {
-  return mat3x2<f32>(tint_symbol_6(buffer, (offset + 0u)), tint_symbol_6(buffer, (offset + 8u)), tint_symbol_6(buffer, (offset + 16u)));
+fn tint_symbol_20(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat3x2<f32> {
+  return mat3x2<f32>(tint_symbol_5(buffer, (offset + 0u)), tint_symbol_5(buffer, (offset + 8u)), tint_symbol_5(buffer, (offset + 16u)));
 }
 
-fn tint_symbol_17(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat3x3<f32> {
+fn tint_symbol_21(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat3x3<f32> {
   return mat3x3<f32>(tint_symbol_9(buffer, (offset + 0u)), tint_symbol_9(buffer, (offset + 16u)), tint_symbol_9(buffer, (offset + 32u)));
 }
 
-fn tint_symbol_18(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat3x4<f32> {
-  return mat3x4<f32>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 16u)), tint_symbol_12(buffer, (offset + 32u)));
+fn tint_symbol_22(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat3x4<f32> {
+  return mat3x4<f32>(tint_symbol_13(buffer, (offset + 0u)), tint_symbol_13(buffer, (offset + 16u)), tint_symbol_13(buffer, (offset + 32u)));
 }
 
-fn tint_symbol_19(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat4x2<f32> {
-  return mat4x2<f32>(tint_symbol_6(buffer, (offset + 0u)), tint_symbol_6(buffer, (offset + 8u)), tint_symbol_6(buffer, (offset + 16u)), tint_symbol_6(buffer, (offset + 24u)));
+fn tint_symbol_23(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat4x2<f32> {
+  return mat4x2<f32>(tint_symbol_5(buffer, (offset + 0u)), tint_symbol_5(buffer, (offset + 8u)), tint_symbol_5(buffer, (offset + 16u)), tint_symbol_5(buffer, (offset + 24u)));
 }
 
-fn tint_symbol_20(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat4x3<f32> {
+fn tint_symbol_24(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat4x3<f32> {
   return mat4x3<f32>(tint_symbol_9(buffer, (offset + 0u)), tint_symbol_9(buffer, (offset + 16u)), tint_symbol_9(buffer, (offset + 32u)), tint_symbol_9(buffer, (offset + 48u)));
 }
 
-fn tint_symbol_21(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat4x4<f32> {
-  return mat4x4<f32>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 16u)), tint_symbol_12(buffer, (offset + 32u)), tint_symbol_12(buffer, (offset + 48u)));
+fn tint_symbol_25(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat4x4<f32> {
+  return mat4x4<f32>(tint_symbol_13(buffer, (offset + 0u)), tint_symbol_13(buffer, (offset + 16u)), tint_symbol_13(buffer, (offset + 32u)), tint_symbol_13(buffer, (offset + 48u)));
 }
 
-fn tint_symbol_22(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> array<vec3<f32>, 2u> {
+fn tint_symbol_26(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat2x2<f16> {
+  return mat2x2<f16>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 4u)));
+}
+
+fn tint_symbol_27(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat2x3<f16> {
+  return mat2x3<f16>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 8u)));
+}
+
+fn tint_symbol_28(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat2x4<f16> {
+  return mat2x4<f16>(tint_symbol_16(buffer, (offset + 0u)), tint_symbol_16(buffer, (offset + 8u)));
+}
+
+fn tint_symbol_29(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat3x2<f16> {
+  return mat3x2<f16>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 4u)), tint_symbol_8(buffer, (offset + 8u)));
+}
+
+fn tint_symbol_30(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat3x3<f16> {
+  return mat3x3<f16>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 8u)), tint_symbol_12(buffer, (offset + 16u)));
+}
+
+fn tint_symbol_31(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat3x4<f16> {
+  return mat3x4<f16>(tint_symbol_16(buffer, (offset + 0u)), tint_symbol_16(buffer, (offset + 8u)), tint_symbol_16(buffer, (offset + 16u)));
+}
+
+fn tint_symbol_32(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat4x2<f16> {
+  return mat4x2<f16>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 4u)), tint_symbol_8(buffer, (offset + 8u)), tint_symbol_8(buffer, (offset + 12u)));
+}
+
+fn tint_symbol_33(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat4x3<f16> {
+  return mat4x3<f16>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 8u)), tint_symbol_12(buffer, (offset + 16u)), tint_symbol_12(buffer, (offset + 24u)));
+}
+
+fn tint_symbol_34(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat4x4<f16> {
+  return mat4x4<f16>(tint_symbol_16(buffer, (offset + 0u)), tint_symbol_16(buffer, (offset + 8u)), tint_symbol_16(buffer, (offset + 16u)), tint_symbol_16(buffer, (offset + 24u)));
+}
+
+fn tint_symbol_35(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> array<vec3<f32>, 2u> {
   var arr : array<vec3<f32>, 2u>;
-  for(var i_1 = 0u; (i_1 < 2u); i_1 = (i_1 + 1u)) {
-    arr[i_1] = tint_symbol_9(buffer, (offset + (i_1 * 16u)));
+  for(var i = 0u; (i < 2u); i = (i + 1u)) {
+    arr[i] = tint_symbol_9(buffer, (offset + (i * 16u)));
   }
   return arr;
 }
 
+fn tint_symbol_36(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> array<mat4x2<f16>, 2u> {
+  var arr_1 : array<mat4x2<f16>, 2u>;
+  for(var i_1 = 0u; (i_1 < 2u); i_1 = (i_1 + 1u)) {
+    arr_1[i_1] = tint_symbol_32(buffer, (offset + (i_1 * 16u)));
+  }
+  return arr_1;
+}
+
 fn tint_symbol(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> SB {
-  return SB(tint_symbol_1(buffer, (offset + 0u)), tint_symbol_2(buffer, (offset + 4u)), tint_symbol_3(buffer, (offset + 8u)), tint_symbol_4(buffer, (offset + 16u)), tint_symbol_5(buffer, (offset + 24u)), tint_symbol_6(buffer, (offset + 32u)), tint_symbol_7(buffer, (offset + 48u)), tint_symbol_8(buffer, (offset + 64u)), tint_symbol_9(buffer, (offset + 80u)), tint_symbol_10(buffer, (offset + 96u)), tint_symbol_11(buffer, (offset + 112u)), tint_symbol_12(buffer, (offset + 128u)), tint_symbol_13(buffer, (offset + 144u)), tint_symbol_14(buffer, (offset + 160u)), tint_symbol_15(buffer, (offset + 192u)), tint_symbol_16(buffer, (offset + 224u)), tint_symbol_17(buffer, (offset + 256u)), tint_symbol_18(buffer, (offset + 304u)), tint_symbol_19(buffer, (offset + 352u)), tint_symbol_20(buffer, (offset + 384u)), tint_symbol_21(buffer, (offset + 448u)), tint_symbol_22(buffer, (offset + 512u)));
+  return SB(tint_symbol_1(buffer, (offset + 0u)), tint_symbol_2(buffer, (offset + 4u)), tint_symbol_3(buffer, (offset + 8u)), tint_symbol_4(buffer, (offset + 12u)), tint_symbol_5(buffer, (offset + 16u)), tint_symbol_6(buffer, (offset + 24u)), tint_symbol_7(buffer, (offset + 32u)), tint_symbol_8(buffer, (offset + 40u)), tint_symbol_9(buffer, (offset + 48u)), tint_symbol_10(buffer, (offset + 64u)), tint_symbol_11(buffer, (offset + 80u)), tint_symbol_12(buffer, (offset + 96u)), tint_symbol_13(buffer, (offset + 112u)), tint_symbol_14(buffer, (offset + 128u)), tint_symbol_15(buffer, (offset + 144u)), tint_symbol_16(buffer, (offset + 160u)), tint_symbol_17(buffer, (offset + 168u)), tint_symbol_18(buffer, (offset + 192u)), tint_symbol_19(buffer, (offset + 224u)), tint_symbol_20(buffer, (offset + 256u)), tint_symbol_21(buffer, (offset + 288u)), tint_symbol_22(buffer, (offset + 336u)), tint_symbol_23(buffer, (offset + 384u)), tint_symbol_24(buffer, (offset + 416u)), tint_symbol_25(buffer, (offset + 480u)), tint_symbol_26(buffer, (offset + 544u)), tint_symbol_27(buffer, (offset + 552u)), tint_symbol_28(buffer, (offset + 568u)), tint_symbol_29(buffer, (offset + 584u)), tint_symbol_30(buffer, (offset + 600u)), tint_symbol_31(buffer, (offset + 624u)), tint_symbol_32(buffer, (offset + 648u)), tint_symbol_33(buffer, (offset + 664u)), tint_symbol_34(buffer, (offset + 696u)), tint_symbol_35(buffer, (offset + 736u)), tint_symbol_36(buffer, (offset + 768u)));
 }
 
 @compute @workgroup_size(1)
@@ -1419,6 +2237,8 @@
 
 TEST_F(DecomposeMemoryAccessTest, LoadStructure_OutOfOrder) {
     auto* src = R"(
+enable f16;
+
 @compute @workgroup_size(1)
 fn main() {
   var x : SB = sb;
@@ -1427,114 +2247,186 @@
 @group(0) @binding(0) var<storage, read_write> sb : SB;
 
 struct SB {
-  a : i32,
-  b : u32,
-  c : f32,
-  d : vec2<i32>,
-  e : vec2<u32>,
-  f : vec2<f32>,
-  g : vec3<i32>,
-  h : vec3<u32>,
-  i : vec3<f32>,
-  j : vec4<i32>,
-  k : vec4<u32>,
-  l : vec4<f32>,
-  m : mat2x2<f32>,
-  n : mat2x3<f32>,
-  o : mat2x4<f32>,
-  p : mat3x2<f32>,
-  q : mat3x3<f32>,
-  r : mat3x4<f32>,
-  s : mat4x2<f32>,
-  t : mat4x3<f32>,
-  u : mat4x4<f32>,
-  v : array<vec3<f32>, 2>,
+  scalar_f32 : f32,
+  scalar_i32 : i32,
+  scalar_u32 : u32,
+  scalar_f16 : f16,
+  vec2_f32 : vec2<f32>,
+  vec2_i32 : vec2<i32>,
+  vec2_u32 : vec2<u32>,
+  vec2_f16 : vec2<f16>,
+  vec3_f32 : vec3<f32>,
+  vec3_i32 : vec3<i32>,
+  vec3_u32 : vec3<u32>,
+  vec3_f16 : vec3<f16>,
+  vec4_f32 : vec4<f32>,
+  vec4_i32 : vec4<i32>,
+  vec4_u32 : vec4<u32>,
+  vec4_f16 : vec4<f16>,
+  mat2x2_f32 : mat2x2<f32>,
+  mat2x3_f32 : mat2x3<f32>,
+  mat2x4_f32 : mat2x4<f32>,
+  mat3x2_f32 : mat3x2<f32>,
+  mat3x3_f32 : mat3x3<f32>,
+  mat3x4_f32 : mat3x4<f32>,
+  mat4x2_f32 : mat4x2<f32>,
+  mat4x3_f32 : mat4x3<f32>,
+  mat4x4_f32 : mat4x4<f32>,
+  mat2x2_f16 : mat2x2<f16>,
+  mat2x3_f16 : mat2x3<f16>,
+  mat2x4_f16 : mat2x4<f16>,
+  mat3x2_f16 : mat3x2<f16>,
+  mat3x3_f16 : mat3x3<f16>,
+  mat3x4_f16 : mat3x4<f16>,
+  mat4x2_f16 : mat4x2<f16>,
+  mat4x3_f16 : mat4x3<f16>,
+  mat4x4_f16 : mat4x4<f16>,
+  arr2_vec3_f32 : array<vec3<f32>, 2>,
+  arr2_mat4x2_f16 : array<mat4x2<f16>, 2>,
 };
 )";
 
     auto* expect = R"(
-@internal(intrinsic_load_storage_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_1(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> i32
-
-@internal(intrinsic_load_storage_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_2(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> u32
+enable f16;
 
 @internal(intrinsic_load_storage_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_3(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> f32
+fn tint_symbol_1(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> f32
 
-@internal(intrinsic_load_storage_vec2_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_4(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec2<i32>
+@internal(intrinsic_load_storage_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_2(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> i32
 
-@internal(intrinsic_load_storage_vec2_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_5(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec2<u32>
+@internal(intrinsic_load_storage_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_3(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> u32
+
+@internal(intrinsic_load_storage_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_4(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> f16
 
 @internal(intrinsic_load_storage_vec2_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_6(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec2<f32>
+fn tint_symbol_5(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec2<f32>
 
-@internal(intrinsic_load_storage_vec3_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_7(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec3<i32>
+@internal(intrinsic_load_storage_vec2_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_6(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec2<i32>
 
-@internal(intrinsic_load_storage_vec3_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_8(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec3<u32>
+@internal(intrinsic_load_storage_vec2_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_7(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec2<u32>
+
+@internal(intrinsic_load_storage_vec2_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_8(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec2<f16>
 
 @internal(intrinsic_load_storage_vec3_f32) @internal(disable_validation__function_has_no_body)
 fn tint_symbol_9(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec3<f32>
 
-@internal(intrinsic_load_storage_vec4_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_10(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec4<i32>
+@internal(intrinsic_load_storage_vec3_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_10(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec3<i32>
 
-@internal(intrinsic_load_storage_vec4_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_11(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec4<u32>
+@internal(intrinsic_load_storage_vec3_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_11(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec3<u32>
+
+@internal(intrinsic_load_storage_vec3_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_12(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec3<f16>
 
 @internal(intrinsic_load_storage_vec4_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_12(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec4<f32>
+fn tint_symbol_13(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec4<f32>
 
-fn tint_symbol_13(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat2x2<f32> {
-  return mat2x2<f32>(tint_symbol_6(buffer, (offset + 0u)), tint_symbol_6(buffer, (offset + 8u)));
+@internal(intrinsic_load_storage_vec4_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_14(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec4<i32>
+
+@internal(intrinsic_load_storage_vec4_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_15(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec4<u32>
+
+@internal(intrinsic_load_storage_vec4_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_16(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> vec4<f16>
+
+fn tint_symbol_17(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat2x2<f32> {
+  return mat2x2<f32>(tint_symbol_5(buffer, (offset + 0u)), tint_symbol_5(buffer, (offset + 8u)));
 }
 
-fn tint_symbol_14(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat2x3<f32> {
+fn tint_symbol_18(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat2x3<f32> {
   return mat2x3<f32>(tint_symbol_9(buffer, (offset + 0u)), tint_symbol_9(buffer, (offset + 16u)));
 }
 
-fn tint_symbol_15(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat2x4<f32> {
-  return mat2x4<f32>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 16u)));
+fn tint_symbol_19(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat2x4<f32> {
+  return mat2x4<f32>(tint_symbol_13(buffer, (offset + 0u)), tint_symbol_13(buffer, (offset + 16u)));
 }
 
-fn tint_symbol_16(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat3x2<f32> {
-  return mat3x2<f32>(tint_symbol_6(buffer, (offset + 0u)), tint_symbol_6(buffer, (offset + 8u)), tint_symbol_6(buffer, (offset + 16u)));
+fn tint_symbol_20(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat3x2<f32> {
+  return mat3x2<f32>(tint_symbol_5(buffer, (offset + 0u)), tint_symbol_5(buffer, (offset + 8u)), tint_symbol_5(buffer, (offset + 16u)));
 }
 
-fn tint_symbol_17(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat3x3<f32> {
+fn tint_symbol_21(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat3x3<f32> {
   return mat3x3<f32>(tint_symbol_9(buffer, (offset + 0u)), tint_symbol_9(buffer, (offset + 16u)), tint_symbol_9(buffer, (offset + 32u)));
 }
 
-fn tint_symbol_18(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat3x4<f32> {
-  return mat3x4<f32>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 16u)), tint_symbol_12(buffer, (offset + 32u)));
+fn tint_symbol_22(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat3x4<f32> {
+  return mat3x4<f32>(tint_symbol_13(buffer, (offset + 0u)), tint_symbol_13(buffer, (offset + 16u)), tint_symbol_13(buffer, (offset + 32u)));
 }
 
-fn tint_symbol_19(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat4x2<f32> {
-  return mat4x2<f32>(tint_symbol_6(buffer, (offset + 0u)), tint_symbol_6(buffer, (offset + 8u)), tint_symbol_6(buffer, (offset + 16u)), tint_symbol_6(buffer, (offset + 24u)));
+fn tint_symbol_23(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat4x2<f32> {
+  return mat4x2<f32>(tint_symbol_5(buffer, (offset + 0u)), tint_symbol_5(buffer, (offset + 8u)), tint_symbol_5(buffer, (offset + 16u)), tint_symbol_5(buffer, (offset + 24u)));
 }
 
-fn tint_symbol_20(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat4x3<f32> {
+fn tint_symbol_24(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat4x3<f32> {
   return mat4x3<f32>(tint_symbol_9(buffer, (offset + 0u)), tint_symbol_9(buffer, (offset + 16u)), tint_symbol_9(buffer, (offset + 32u)), tint_symbol_9(buffer, (offset + 48u)));
 }
 
-fn tint_symbol_21(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat4x4<f32> {
-  return mat4x4<f32>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 16u)), tint_symbol_12(buffer, (offset + 32u)), tint_symbol_12(buffer, (offset + 48u)));
+fn tint_symbol_25(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat4x4<f32> {
+  return mat4x4<f32>(tint_symbol_13(buffer, (offset + 0u)), tint_symbol_13(buffer, (offset + 16u)), tint_symbol_13(buffer, (offset + 32u)), tint_symbol_13(buffer, (offset + 48u)));
 }
 
-fn tint_symbol_22(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> array<vec3<f32>, 2u> {
+fn tint_symbol_26(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat2x2<f16> {
+  return mat2x2<f16>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 4u)));
+}
+
+fn tint_symbol_27(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat2x3<f16> {
+  return mat2x3<f16>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 8u)));
+}
+
+fn tint_symbol_28(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat2x4<f16> {
+  return mat2x4<f16>(tint_symbol_16(buffer, (offset + 0u)), tint_symbol_16(buffer, (offset + 8u)));
+}
+
+fn tint_symbol_29(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat3x2<f16> {
+  return mat3x2<f16>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 4u)), tint_symbol_8(buffer, (offset + 8u)));
+}
+
+fn tint_symbol_30(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat3x3<f16> {
+  return mat3x3<f16>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 8u)), tint_symbol_12(buffer, (offset + 16u)));
+}
+
+fn tint_symbol_31(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat3x4<f16> {
+  return mat3x4<f16>(tint_symbol_16(buffer, (offset + 0u)), tint_symbol_16(buffer, (offset + 8u)), tint_symbol_16(buffer, (offset + 16u)));
+}
+
+fn tint_symbol_32(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat4x2<f16> {
+  return mat4x2<f16>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 4u)), tint_symbol_8(buffer, (offset + 8u)), tint_symbol_8(buffer, (offset + 12u)));
+}
+
+fn tint_symbol_33(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat4x3<f16> {
+  return mat4x3<f16>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 8u)), tint_symbol_12(buffer, (offset + 16u)), tint_symbol_12(buffer, (offset + 24u)));
+}
+
+fn tint_symbol_34(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> mat4x4<f16> {
+  return mat4x4<f16>(tint_symbol_16(buffer, (offset + 0u)), tint_symbol_16(buffer, (offset + 8u)), tint_symbol_16(buffer, (offset + 16u)), tint_symbol_16(buffer, (offset + 24u)));
+}
+
+fn tint_symbol_35(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> array<vec3<f32>, 2u> {
   var arr : array<vec3<f32>, 2u>;
-  for(var i_1 = 0u; (i_1 < 2u); i_1 = (i_1 + 1u)) {
-    arr[i_1] = tint_symbol_9(buffer, (offset + (i_1 * 16u)));
+  for(var i = 0u; (i < 2u); i = (i + 1u)) {
+    arr[i] = tint_symbol_9(buffer, (offset + (i * 16u)));
   }
   return arr;
 }
 
+fn tint_symbol_36(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> array<mat4x2<f16>, 2u> {
+  var arr_1 : array<mat4x2<f16>, 2u>;
+  for(var i_1 = 0u; (i_1 < 2u); i_1 = (i_1 + 1u)) {
+    arr_1[i_1] = tint_symbol_32(buffer, (offset + (i_1 * 16u)));
+  }
+  return arr_1;
+}
+
 fn tint_symbol(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32) -> SB {
-  return SB(tint_symbol_1(buffer, (offset + 0u)), tint_symbol_2(buffer, (offset + 4u)), tint_symbol_3(buffer, (offset + 8u)), tint_symbol_4(buffer, (offset + 16u)), tint_symbol_5(buffer, (offset + 24u)), tint_symbol_6(buffer, (offset + 32u)), tint_symbol_7(buffer, (offset + 48u)), tint_symbol_8(buffer, (offset + 64u)), tint_symbol_9(buffer, (offset + 80u)), tint_symbol_10(buffer, (offset + 96u)), tint_symbol_11(buffer, (offset + 112u)), tint_symbol_12(buffer, (offset + 128u)), tint_symbol_13(buffer, (offset + 144u)), tint_symbol_14(buffer, (offset + 160u)), tint_symbol_15(buffer, (offset + 192u)), tint_symbol_16(buffer, (offset + 224u)), tint_symbol_17(buffer, (offset + 256u)), tint_symbol_18(buffer, (offset + 304u)), tint_symbol_19(buffer, (offset + 352u)), tint_symbol_20(buffer, (offset + 384u)), tint_symbol_21(buffer, (offset + 448u)), tint_symbol_22(buffer, (offset + 512u)));
+  return SB(tint_symbol_1(buffer, (offset + 0u)), tint_symbol_2(buffer, (offset + 4u)), tint_symbol_3(buffer, (offset + 8u)), tint_symbol_4(buffer, (offset + 12u)), tint_symbol_5(buffer, (offset + 16u)), tint_symbol_6(buffer, (offset + 24u)), tint_symbol_7(buffer, (offset + 32u)), tint_symbol_8(buffer, (offset + 40u)), tint_symbol_9(buffer, (offset + 48u)), tint_symbol_10(buffer, (offset + 64u)), tint_symbol_11(buffer, (offset + 80u)), tint_symbol_12(buffer, (offset + 96u)), tint_symbol_13(buffer, (offset + 112u)), tint_symbol_14(buffer, (offset + 128u)), tint_symbol_15(buffer, (offset + 144u)), tint_symbol_16(buffer, (offset + 160u)), tint_symbol_17(buffer, (offset + 168u)), tint_symbol_18(buffer, (offset + 192u)), tint_symbol_19(buffer, (offset + 224u)), tint_symbol_20(buffer, (offset + 256u)), tint_symbol_21(buffer, (offset + 288u)), tint_symbol_22(buffer, (offset + 336u)), tint_symbol_23(buffer, (offset + 384u)), tint_symbol_24(buffer, (offset + 416u)), tint_symbol_25(buffer, (offset + 480u)), tint_symbol_26(buffer, (offset + 544u)), tint_symbol_27(buffer, (offset + 552u)), tint_symbol_28(buffer, (offset + 568u)), tint_symbol_29(buffer, (offset + 584u)), tint_symbol_30(buffer, (offset + 600u)), tint_symbol_31(buffer, (offset + 624u)), tint_symbol_32(buffer, (offset + 648u)), tint_symbol_33(buffer, (offset + 664u)), tint_symbol_34(buffer, (offset + 696u)), tint_symbol_35(buffer, (offset + 736u)), tint_symbol_36(buffer, (offset + 768u)));
 }
 
 @compute @workgroup_size(1)
@@ -1545,28 +2437,42 @@
 @group(0) @binding(0) var<storage, read_write> sb : SB;
 
 struct SB {
-  a : i32,
-  b : u32,
-  c : f32,
-  d : vec2<i32>,
-  e : vec2<u32>,
-  f : vec2<f32>,
-  g : vec3<i32>,
-  h : vec3<u32>,
-  i : vec3<f32>,
-  j : vec4<i32>,
-  k : vec4<u32>,
-  l : vec4<f32>,
-  m : mat2x2<f32>,
-  n : mat2x3<f32>,
-  o : mat2x4<f32>,
-  p : mat3x2<f32>,
-  q : mat3x3<f32>,
-  r : mat3x4<f32>,
-  s : mat4x2<f32>,
-  t : mat4x3<f32>,
-  u : mat4x4<f32>,
-  v : array<vec3<f32>, 2>,
+  scalar_f32 : f32,
+  scalar_i32 : i32,
+  scalar_u32 : u32,
+  scalar_f16 : f16,
+  vec2_f32 : vec2<f32>,
+  vec2_i32 : vec2<i32>,
+  vec2_u32 : vec2<u32>,
+  vec2_f16 : vec2<f16>,
+  vec3_f32 : vec3<f32>,
+  vec3_i32 : vec3<i32>,
+  vec3_u32 : vec3<u32>,
+  vec3_f16 : vec3<f16>,
+  vec4_f32 : vec4<f32>,
+  vec4_i32 : vec4<i32>,
+  vec4_u32 : vec4<u32>,
+  vec4_f16 : vec4<f16>,
+  mat2x2_f32 : mat2x2<f32>,
+  mat2x3_f32 : mat2x3<f32>,
+  mat2x4_f32 : mat2x4<f32>,
+  mat3x2_f32 : mat3x2<f32>,
+  mat3x3_f32 : mat3x3<f32>,
+  mat3x4_f32 : mat3x4<f32>,
+  mat4x2_f32 : mat4x2<f32>,
+  mat4x3_f32 : mat4x3<f32>,
+  mat4x4_f32 : mat4x4<f32>,
+  mat2x2_f16 : mat2x2<f16>,
+  mat2x3_f16 : mat2x3<f16>,
+  mat2x4_f16 : mat2x4<f16>,
+  mat3x2_f16 : mat3x2<f16>,
+  mat3x3_f16 : mat3x3<f16>,
+  mat3x4_f16 : mat3x4<f16>,
+  mat4x2_f16 : mat4x2<f16>,
+  mat4x3_f16 : mat4x3<f16>,
+  mat4x4_f16 : mat4x4<f16>,
+  arr2_vec3_f32 : array<vec3<f32>, 2>,
+  arr2_mat4x2_f16 : array<mat4x2<f16>, 2>,
 }
 )";
 
@@ -1577,29 +2483,45 @@
 
 TEST_F(DecomposeMemoryAccessTest, StoreStructure) {
     auto* src = R"(
+enable f16;
+
 struct SB {
-  a : i32,
-  b : u32,
-  c : f32,
-  d : vec2<i32>,
-  e : vec2<u32>,
-  f : vec2<f32>,
-  g : vec3<i32>,
-  h : vec3<u32>,
-  i : vec3<f32>,
-  j : vec4<i32>,
-  k : vec4<u32>,
-  l : vec4<f32>,
-  m : mat2x2<f32>,
-  n : mat2x3<f32>,
-  o : mat2x4<f32>,
-  p : mat3x2<f32>,
-  q : mat3x3<f32>,
-  r : mat3x4<f32>,
-  s : mat4x2<f32>,
-  t : mat4x3<f32>,
-  u : mat4x4<f32>,
-  v : array<vec3<f32>, 2>,
+  scalar_f32 : f32,
+  scalar_i32 : i32,
+  scalar_u32 : u32,
+  scalar_f16 : f16,
+  vec2_f32 : vec2<f32>,
+  vec2_i32 : vec2<i32>,
+  vec2_u32 : vec2<u32>,
+  vec2_f16 : vec2<f16>,
+  vec3_f32 : vec3<f32>,
+  vec3_i32 : vec3<i32>,
+  vec3_u32 : vec3<u32>,
+  vec3_f16 : vec3<f16>,
+  vec4_f32 : vec4<f32>,
+  vec4_i32 : vec4<i32>,
+  vec4_u32 : vec4<u32>,
+  vec4_f16 : vec4<f16>,
+  mat2x2_f32 : mat2x2<f32>,
+  mat2x3_f32 : mat2x3<f32>,
+  mat2x4_f32 : mat2x4<f32>,
+  mat3x2_f32 : mat3x2<f32>,
+  mat3x3_f32 : mat3x3<f32>,
+  mat3x4_f32 : mat3x4<f32>,
+  mat4x2_f32 : mat4x2<f32>,
+  mat4x3_f32 : mat4x3<f32>,
+  mat4x4_f32 : mat4x4<f32>,
+  mat2x2_f16 : mat2x2<f16>,
+  mat2x3_f16 : mat2x3<f16>,
+  mat2x4_f16 : mat2x4<f16>,
+  mat3x2_f16 : mat3x2<f16>,
+  mat3x3_f16 : mat3x3<f16>,
+  mat3x4_f16 : mat3x4<f16>,
+  mat4x2_f16 : mat4x2<f16>,
+  mat4x3_f16 : mat4x3<f16>,
+  mat4x4_f16 : mat4x4<f16>,
+  arr2_vec3_f32 : array<vec3<f32>, 2>,
+  arr2_mat4x2_f16 : array<mat4x2<f16>, 2>,
 };
 
 @group(0) @binding(0) var<storage, read_write> sb : SB;
@@ -1611,153 +2533,256 @@
 )";
 
     auto* expect = R"(
+enable f16;
+
 struct SB {
-  a : i32,
-  b : u32,
-  c : f32,
-  d : vec2<i32>,
-  e : vec2<u32>,
-  f : vec2<f32>,
-  g : vec3<i32>,
-  h : vec3<u32>,
-  i : vec3<f32>,
-  j : vec4<i32>,
-  k : vec4<u32>,
-  l : vec4<f32>,
-  m : mat2x2<f32>,
-  n : mat2x3<f32>,
-  o : mat2x4<f32>,
-  p : mat3x2<f32>,
-  q : mat3x3<f32>,
-  r : mat3x4<f32>,
-  s : mat4x2<f32>,
-  t : mat4x3<f32>,
-  u : mat4x4<f32>,
-  v : array<vec3<f32>, 2>,
+  scalar_f32 : f32,
+  scalar_i32 : i32,
+  scalar_u32 : u32,
+  scalar_f16 : f16,
+  vec2_f32 : vec2<f32>,
+  vec2_i32 : vec2<i32>,
+  vec2_u32 : vec2<u32>,
+  vec2_f16 : vec2<f16>,
+  vec3_f32 : vec3<f32>,
+  vec3_i32 : vec3<i32>,
+  vec3_u32 : vec3<u32>,
+  vec3_f16 : vec3<f16>,
+  vec4_f32 : vec4<f32>,
+  vec4_i32 : vec4<i32>,
+  vec4_u32 : vec4<u32>,
+  vec4_f16 : vec4<f16>,
+  mat2x2_f32 : mat2x2<f32>,
+  mat2x3_f32 : mat2x3<f32>,
+  mat2x4_f32 : mat2x4<f32>,
+  mat3x2_f32 : mat3x2<f32>,
+  mat3x3_f32 : mat3x3<f32>,
+  mat3x4_f32 : mat3x4<f32>,
+  mat4x2_f32 : mat4x2<f32>,
+  mat4x3_f32 : mat4x3<f32>,
+  mat4x4_f32 : mat4x4<f32>,
+  mat2x2_f16 : mat2x2<f16>,
+  mat2x3_f16 : mat2x3<f16>,
+  mat2x4_f16 : mat2x4<f16>,
+  mat3x2_f16 : mat3x2<f16>,
+  mat3x3_f16 : mat3x3<f16>,
+  mat3x4_f16 : mat3x4<f16>,
+  mat4x2_f16 : mat4x2<f16>,
+  mat4x3_f16 : mat4x3<f16>,
+  mat4x4_f16 : mat4x4<f16>,
+  arr2_vec3_f32 : array<vec3<f32>, 2>,
+  arr2_mat4x2_f16 : array<mat4x2<f16>, 2>,
 }
 
 @group(0) @binding(0) var<storage, read_write> sb : SB;
 
+@internal(intrinsic_store_storage_f32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_1(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : f32)
+
 @internal(intrinsic_store_storage_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_1(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : i32)
+fn tint_symbol_2(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : i32)
 
 @internal(intrinsic_store_storage_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_2(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : u32)
+fn tint_symbol_3(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : u32)
 
-@internal(intrinsic_store_storage_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_3(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : f32)
-
-@internal(intrinsic_store_storage_vec2_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_4(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec2<i32>)
-
-@internal(intrinsic_store_storage_vec2_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_5(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec2<u32>)
+@internal(intrinsic_store_storage_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_4(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : f16)
 
 @internal(intrinsic_store_storage_vec2_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_6(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec2<f32>)
+fn tint_symbol_5(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec2<f32>)
 
-@internal(intrinsic_store_storage_vec3_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_7(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec3<i32>)
+@internal(intrinsic_store_storage_vec2_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_6(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec2<i32>)
 
-@internal(intrinsic_store_storage_vec3_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_8(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec3<u32>)
+@internal(intrinsic_store_storage_vec2_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_7(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec2<u32>)
+
+@internal(intrinsic_store_storage_vec2_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_8(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec2<f16>)
 
 @internal(intrinsic_store_storage_vec3_f32) @internal(disable_validation__function_has_no_body)
 fn tint_symbol_9(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec3<f32>)
 
-@internal(intrinsic_store_storage_vec4_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_10(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec4<i32>)
+@internal(intrinsic_store_storage_vec3_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_10(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec3<i32>)
 
-@internal(intrinsic_store_storage_vec4_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_11(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec4<u32>)
+@internal(intrinsic_store_storage_vec3_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_11(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec3<u32>)
+
+@internal(intrinsic_store_storage_vec3_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_12(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec3<f16>)
 
 @internal(intrinsic_store_storage_vec4_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_12(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec4<f32>)
+fn tint_symbol_13(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec4<f32>)
 
-fn tint_symbol_13(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat2x2<f32>) {
-  tint_symbol_6(buffer, (offset + 0u), value[0u]);
-  tint_symbol_6(buffer, (offset + 8u), value[1u]);
+@internal(intrinsic_store_storage_vec4_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_14(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec4<i32>)
+
+@internal(intrinsic_store_storage_vec4_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_15(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec4<u32>)
+
+@internal(intrinsic_store_storage_vec4_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_16(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec4<f16>)
+
+fn tint_symbol_17(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat2x2<f32>) {
+  tint_symbol_5(buffer, (offset + 0u), value[0u]);
+  tint_symbol_5(buffer, (offset + 8u), value[1u]);
 }
 
-fn tint_symbol_14(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat2x3<f32>) {
+fn tint_symbol_18(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat2x3<f32>) {
   tint_symbol_9(buffer, (offset + 0u), value[0u]);
   tint_symbol_9(buffer, (offset + 16u), value[1u]);
 }
 
-fn tint_symbol_15(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat2x4<f32>) {
-  tint_symbol_12(buffer, (offset + 0u), value[0u]);
-  tint_symbol_12(buffer, (offset + 16u), value[1u]);
+fn tint_symbol_19(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat2x4<f32>) {
+  tint_symbol_13(buffer, (offset + 0u), value[0u]);
+  tint_symbol_13(buffer, (offset + 16u), value[1u]);
 }
 
-fn tint_symbol_16(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat3x2<f32>) {
-  tint_symbol_6(buffer, (offset + 0u), value[0u]);
-  tint_symbol_6(buffer, (offset + 8u), value[1u]);
-  tint_symbol_6(buffer, (offset + 16u), value[2u]);
+fn tint_symbol_20(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat3x2<f32>) {
+  tint_symbol_5(buffer, (offset + 0u), value[0u]);
+  tint_symbol_5(buffer, (offset + 8u), value[1u]);
+  tint_symbol_5(buffer, (offset + 16u), value[2u]);
 }
 
-fn tint_symbol_17(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat3x3<f32>) {
+fn tint_symbol_21(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat3x3<f32>) {
   tint_symbol_9(buffer, (offset + 0u), value[0u]);
   tint_symbol_9(buffer, (offset + 16u), value[1u]);
   tint_symbol_9(buffer, (offset + 32u), value[2u]);
 }
 
-fn tint_symbol_18(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat3x4<f32>) {
-  tint_symbol_12(buffer, (offset + 0u), value[0u]);
-  tint_symbol_12(buffer, (offset + 16u), value[1u]);
-  tint_symbol_12(buffer, (offset + 32u), value[2u]);
+fn tint_symbol_22(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat3x4<f32>) {
+  tint_symbol_13(buffer, (offset + 0u), value[0u]);
+  tint_symbol_13(buffer, (offset + 16u), value[1u]);
+  tint_symbol_13(buffer, (offset + 32u), value[2u]);
 }
 
-fn tint_symbol_19(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat4x2<f32>) {
-  tint_symbol_6(buffer, (offset + 0u), value[0u]);
-  tint_symbol_6(buffer, (offset + 8u), value[1u]);
-  tint_symbol_6(buffer, (offset + 16u), value[2u]);
-  tint_symbol_6(buffer, (offset + 24u), value[3u]);
+fn tint_symbol_23(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat4x2<f32>) {
+  tint_symbol_5(buffer, (offset + 0u), value[0u]);
+  tint_symbol_5(buffer, (offset + 8u), value[1u]);
+  tint_symbol_5(buffer, (offset + 16u), value[2u]);
+  tint_symbol_5(buffer, (offset + 24u), value[3u]);
 }
 
-fn tint_symbol_20(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat4x3<f32>) {
+fn tint_symbol_24(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat4x3<f32>) {
   tint_symbol_9(buffer, (offset + 0u), value[0u]);
   tint_symbol_9(buffer, (offset + 16u), value[1u]);
   tint_symbol_9(buffer, (offset + 32u), value[2u]);
   tint_symbol_9(buffer, (offset + 48u), value[3u]);
 }
 
-fn tint_symbol_21(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat4x4<f32>) {
-  tint_symbol_12(buffer, (offset + 0u), value[0u]);
-  tint_symbol_12(buffer, (offset + 16u), value[1u]);
-  tint_symbol_12(buffer, (offset + 32u), value[2u]);
-  tint_symbol_12(buffer, (offset + 48u), value[3u]);
+fn tint_symbol_25(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat4x4<f32>) {
+  tint_symbol_13(buffer, (offset + 0u), value[0u]);
+  tint_symbol_13(buffer, (offset + 16u), value[1u]);
+  tint_symbol_13(buffer, (offset + 32u), value[2u]);
+  tint_symbol_13(buffer, (offset + 48u), value[3u]);
 }
 
-fn tint_symbol_22(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : array<vec3<f32>, 2u>) {
+fn tint_symbol_26(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat2x2<f16>) {
+  tint_symbol_8(buffer, (offset + 0u), value[0u]);
+  tint_symbol_8(buffer, (offset + 4u), value[1u]);
+}
+
+fn tint_symbol_27(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat2x3<f16>) {
+  tint_symbol_12(buffer, (offset + 0u), value[0u]);
+  tint_symbol_12(buffer, (offset + 8u), value[1u]);
+}
+
+fn tint_symbol_28(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat2x4<f16>) {
+  tint_symbol_16(buffer, (offset + 0u), value[0u]);
+  tint_symbol_16(buffer, (offset + 8u), value[1u]);
+}
+
+fn tint_symbol_29(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat3x2<f16>) {
+  tint_symbol_8(buffer, (offset + 0u), value[0u]);
+  tint_symbol_8(buffer, (offset + 4u), value[1u]);
+  tint_symbol_8(buffer, (offset + 8u), value[2u]);
+}
+
+fn tint_symbol_30(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat3x3<f16>) {
+  tint_symbol_12(buffer, (offset + 0u), value[0u]);
+  tint_symbol_12(buffer, (offset + 8u), value[1u]);
+  tint_symbol_12(buffer, (offset + 16u), value[2u]);
+}
+
+fn tint_symbol_31(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat3x4<f16>) {
+  tint_symbol_16(buffer, (offset + 0u), value[0u]);
+  tint_symbol_16(buffer, (offset + 8u), value[1u]);
+  tint_symbol_16(buffer, (offset + 16u), value[2u]);
+}
+
+fn tint_symbol_32(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat4x2<f16>) {
+  tint_symbol_8(buffer, (offset + 0u), value[0u]);
+  tint_symbol_8(buffer, (offset + 4u), value[1u]);
+  tint_symbol_8(buffer, (offset + 8u), value[2u]);
+  tint_symbol_8(buffer, (offset + 12u), value[3u]);
+}
+
+fn tint_symbol_33(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat4x3<f16>) {
+  tint_symbol_12(buffer, (offset + 0u), value[0u]);
+  tint_symbol_12(buffer, (offset + 8u), value[1u]);
+  tint_symbol_12(buffer, (offset + 16u), value[2u]);
+  tint_symbol_12(buffer, (offset + 24u), value[3u]);
+}
+
+fn tint_symbol_34(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat4x4<f16>) {
+  tint_symbol_16(buffer, (offset + 0u), value[0u]);
+  tint_symbol_16(buffer, (offset + 8u), value[1u]);
+  tint_symbol_16(buffer, (offset + 16u), value[2u]);
+  tint_symbol_16(buffer, (offset + 24u), value[3u]);
+}
+
+fn tint_symbol_35(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : array<vec3<f32>, 2u>) {
   var array = value;
+  for(var i = 0u; (i < 2u); i = (i + 1u)) {
+    tint_symbol_9(buffer, (offset + (i * 16u)), array[i]);
+  }
+}
+
+fn tint_symbol_36(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : array<mat4x2<f16>, 2u>) {
+  var array_1 = value;
   for(var i_1 = 0u; (i_1 < 2u); i_1 = (i_1 + 1u)) {
-    tint_symbol_9(buffer, (offset + (i_1 * 16u)), array[i_1]);
+    tint_symbol_32(buffer, (offset + (i_1 * 16u)), array_1[i_1]);
   }
 }
 
 fn tint_symbol(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : SB) {
-  tint_symbol_1(buffer, (offset + 0u), value.a);
-  tint_symbol_2(buffer, (offset + 4u), value.b);
-  tint_symbol_3(buffer, (offset + 8u), value.c);
-  tint_symbol_4(buffer, (offset + 16u), value.d);
-  tint_symbol_5(buffer, (offset + 24u), value.e);
-  tint_symbol_6(buffer, (offset + 32u), value.f);
-  tint_symbol_7(buffer, (offset + 48u), value.g);
-  tint_symbol_8(buffer, (offset + 64u), value.h);
-  tint_symbol_9(buffer, (offset + 80u), value.i);
-  tint_symbol_10(buffer, (offset + 96u), value.j);
-  tint_symbol_11(buffer, (offset + 112u), value.k);
-  tint_symbol_12(buffer, (offset + 128u), value.l);
-  tint_symbol_13(buffer, (offset + 144u), value.m);
-  tint_symbol_14(buffer, (offset + 160u), value.n);
-  tint_symbol_15(buffer, (offset + 192u), value.o);
-  tint_symbol_16(buffer, (offset + 224u), value.p);
-  tint_symbol_17(buffer, (offset + 256u), value.q);
-  tint_symbol_18(buffer, (offset + 304u), value.r);
-  tint_symbol_19(buffer, (offset + 352u), value.s);
-  tint_symbol_20(buffer, (offset + 384u), value.t);
-  tint_symbol_21(buffer, (offset + 448u), value.u);
-  tint_symbol_22(buffer, (offset + 512u), value.v);
+  tint_symbol_1(buffer, (offset + 0u), value.scalar_f32);
+  tint_symbol_2(buffer, (offset + 4u), value.scalar_i32);
+  tint_symbol_3(buffer, (offset + 8u), value.scalar_u32);
+  tint_symbol_4(buffer, (offset + 12u), value.scalar_f16);
+  tint_symbol_5(buffer, (offset + 16u), value.vec2_f32);
+  tint_symbol_6(buffer, (offset + 24u), value.vec2_i32);
+  tint_symbol_7(buffer, (offset + 32u), value.vec2_u32);
+  tint_symbol_8(buffer, (offset + 40u), value.vec2_f16);
+  tint_symbol_9(buffer, (offset + 48u), value.vec3_f32);
+  tint_symbol_10(buffer, (offset + 64u), value.vec3_i32);
+  tint_symbol_11(buffer, (offset + 80u), value.vec3_u32);
+  tint_symbol_12(buffer, (offset + 96u), value.vec3_f16);
+  tint_symbol_13(buffer, (offset + 112u), value.vec4_f32);
+  tint_symbol_14(buffer, (offset + 128u), value.vec4_i32);
+  tint_symbol_15(buffer, (offset + 144u), value.vec4_u32);
+  tint_symbol_16(buffer, (offset + 160u), value.vec4_f16);
+  tint_symbol_17(buffer, (offset + 168u), value.mat2x2_f32);
+  tint_symbol_18(buffer, (offset + 192u), value.mat2x3_f32);
+  tint_symbol_19(buffer, (offset + 224u), value.mat2x4_f32);
+  tint_symbol_20(buffer, (offset + 256u), value.mat3x2_f32);
+  tint_symbol_21(buffer, (offset + 288u), value.mat3x3_f32);
+  tint_symbol_22(buffer, (offset + 336u), value.mat3x4_f32);
+  tint_symbol_23(buffer, (offset + 384u), value.mat4x2_f32);
+  tint_symbol_24(buffer, (offset + 416u), value.mat4x3_f32);
+  tint_symbol_25(buffer, (offset + 480u), value.mat4x4_f32);
+  tint_symbol_26(buffer, (offset + 544u), value.mat2x2_f16);
+  tint_symbol_27(buffer, (offset + 552u), value.mat2x3_f16);
+  tint_symbol_28(buffer, (offset + 568u), value.mat2x4_f16);
+  tint_symbol_29(buffer, (offset + 584u), value.mat3x2_f16);
+  tint_symbol_30(buffer, (offset + 600u), value.mat3x3_f16);
+  tint_symbol_31(buffer, (offset + 624u), value.mat3x4_f16);
+  tint_symbol_32(buffer, (offset + 648u), value.mat4x2_f16);
+  tint_symbol_33(buffer, (offset + 664u), value.mat4x3_f16);
+  tint_symbol_34(buffer, (offset + 696u), value.mat4x4_f16);
+  tint_symbol_35(buffer, (offset + 736u), value.arr2_vec3_f32);
+  tint_symbol_36(buffer, (offset + 768u), value.arr2_mat4x2_f16);
 }
 
 @compute @workgroup_size(1)
@@ -1773,6 +2798,8 @@
 
 TEST_F(DecomposeMemoryAccessTest, StoreStructure_OutOfOrder) {
     auto* src = R"(
+enable f16;
+
 @compute @workgroup_size(1)
 fn main() {
   sb = SB();
@@ -1781,152 +2808,255 @@
 @group(0) @binding(0) var<storage, read_write> sb : SB;
 
 struct SB {
-  a : i32,
-  b : u32,
-  c : f32,
-  d : vec2<i32>,
-  e : vec2<u32>,
-  f : vec2<f32>,
-  g : vec3<i32>,
-  h : vec3<u32>,
-  i : vec3<f32>,
-  j : vec4<i32>,
-  k : vec4<u32>,
-  l : vec4<f32>,
-  m : mat2x2<f32>,
-  n : mat2x3<f32>,
-  o : mat2x4<f32>,
-  p : mat3x2<f32>,
-  q : mat3x3<f32>,
-  r : mat3x4<f32>,
-  s : mat4x2<f32>,
-  t : mat4x3<f32>,
-  u : mat4x4<f32>,
-  v : array<vec3<f32>, 2>,
+  scalar_f32 : f32,
+  scalar_i32 : i32,
+  scalar_u32 : u32,
+  scalar_f16 : f16,
+  vec2_f32 : vec2<f32>,
+  vec2_i32 : vec2<i32>,
+  vec2_u32 : vec2<u32>,
+  vec2_f16 : vec2<f16>,
+  vec3_f32 : vec3<f32>,
+  vec3_i32 : vec3<i32>,
+  vec3_u32 : vec3<u32>,
+  vec3_f16 : vec3<f16>,
+  vec4_f32 : vec4<f32>,
+  vec4_i32 : vec4<i32>,
+  vec4_u32 : vec4<u32>,
+  vec4_f16 : vec4<f16>,
+  mat2x2_f32 : mat2x2<f32>,
+  mat2x3_f32 : mat2x3<f32>,
+  mat2x4_f32 : mat2x4<f32>,
+  mat3x2_f32 : mat3x2<f32>,
+  mat3x3_f32 : mat3x3<f32>,
+  mat3x4_f32 : mat3x4<f32>,
+  mat4x2_f32 : mat4x2<f32>,
+  mat4x3_f32 : mat4x3<f32>,
+  mat4x4_f32 : mat4x4<f32>,
+  mat2x2_f16 : mat2x2<f16>,
+  mat2x3_f16 : mat2x3<f16>,
+  mat2x4_f16 : mat2x4<f16>,
+  mat3x2_f16 : mat3x2<f16>,
+  mat3x3_f16 : mat3x3<f16>,
+  mat3x4_f16 : mat3x4<f16>,
+  mat4x2_f16 : mat4x2<f16>,
+  mat4x3_f16 : mat4x3<f16>,
+  mat4x4_f16 : mat4x4<f16>,
+  arr2_vec3_f32 : array<vec3<f32>, 2>,
+  arr2_mat4x2_f16 : array<mat4x2<f16>, 2>,
 };
 )";
 
     auto* expect = R"(
-@internal(intrinsic_store_storage_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_1(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : i32)
-
-@internal(intrinsic_store_storage_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_2(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : u32)
+enable f16;
 
 @internal(intrinsic_store_storage_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_3(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : f32)
+fn tint_symbol_1(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : f32)
 
-@internal(intrinsic_store_storage_vec2_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_4(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec2<i32>)
+@internal(intrinsic_store_storage_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_2(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : i32)
 
-@internal(intrinsic_store_storage_vec2_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_5(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec2<u32>)
+@internal(intrinsic_store_storage_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_3(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : u32)
+
+@internal(intrinsic_store_storage_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_4(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : f16)
 
 @internal(intrinsic_store_storage_vec2_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_6(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec2<f32>)
+fn tint_symbol_5(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec2<f32>)
 
-@internal(intrinsic_store_storage_vec3_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_7(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec3<i32>)
+@internal(intrinsic_store_storage_vec2_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_6(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec2<i32>)
 
-@internal(intrinsic_store_storage_vec3_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_8(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec3<u32>)
+@internal(intrinsic_store_storage_vec2_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_7(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec2<u32>)
+
+@internal(intrinsic_store_storage_vec2_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_8(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec2<f16>)
 
 @internal(intrinsic_store_storage_vec3_f32) @internal(disable_validation__function_has_no_body)
 fn tint_symbol_9(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec3<f32>)
 
-@internal(intrinsic_store_storage_vec4_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_10(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec4<i32>)
+@internal(intrinsic_store_storage_vec3_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_10(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec3<i32>)
 
-@internal(intrinsic_store_storage_vec4_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_11(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec4<u32>)
+@internal(intrinsic_store_storage_vec3_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_11(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec3<u32>)
+
+@internal(intrinsic_store_storage_vec3_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_12(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec3<f16>)
 
 @internal(intrinsic_store_storage_vec4_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_12(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec4<f32>)
+fn tint_symbol_13(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec4<f32>)
 
-fn tint_symbol_13(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat2x2<f32>) {
-  tint_symbol_6(buffer, (offset + 0u), value[0u]);
-  tint_symbol_6(buffer, (offset + 8u), value[1u]);
+@internal(intrinsic_store_storage_vec4_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_14(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec4<i32>)
+
+@internal(intrinsic_store_storage_vec4_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_15(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec4<u32>)
+
+@internal(intrinsic_store_storage_vec4_f16) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_16(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : vec4<f16>)
+
+fn tint_symbol_17(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat2x2<f32>) {
+  tint_symbol_5(buffer, (offset + 0u), value[0u]);
+  tint_symbol_5(buffer, (offset + 8u), value[1u]);
 }
 
-fn tint_symbol_14(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat2x3<f32>) {
+fn tint_symbol_18(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat2x3<f32>) {
   tint_symbol_9(buffer, (offset + 0u), value[0u]);
   tint_symbol_9(buffer, (offset + 16u), value[1u]);
 }
 
-fn tint_symbol_15(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat2x4<f32>) {
-  tint_symbol_12(buffer, (offset + 0u), value[0u]);
-  tint_symbol_12(buffer, (offset + 16u), value[1u]);
+fn tint_symbol_19(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat2x4<f32>) {
+  tint_symbol_13(buffer, (offset + 0u), value[0u]);
+  tint_symbol_13(buffer, (offset + 16u), value[1u]);
 }
 
-fn tint_symbol_16(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat3x2<f32>) {
-  tint_symbol_6(buffer, (offset + 0u), value[0u]);
-  tint_symbol_6(buffer, (offset + 8u), value[1u]);
-  tint_symbol_6(buffer, (offset + 16u), value[2u]);
+fn tint_symbol_20(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat3x2<f32>) {
+  tint_symbol_5(buffer, (offset + 0u), value[0u]);
+  tint_symbol_5(buffer, (offset + 8u), value[1u]);
+  tint_symbol_5(buffer, (offset + 16u), value[2u]);
 }
 
-fn tint_symbol_17(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat3x3<f32>) {
+fn tint_symbol_21(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat3x3<f32>) {
   tint_symbol_9(buffer, (offset + 0u), value[0u]);
   tint_symbol_9(buffer, (offset + 16u), value[1u]);
   tint_symbol_9(buffer, (offset + 32u), value[2u]);
 }
 
-fn tint_symbol_18(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat3x4<f32>) {
-  tint_symbol_12(buffer, (offset + 0u), value[0u]);
-  tint_symbol_12(buffer, (offset + 16u), value[1u]);
-  tint_symbol_12(buffer, (offset + 32u), value[2u]);
+fn tint_symbol_22(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat3x4<f32>) {
+  tint_symbol_13(buffer, (offset + 0u), value[0u]);
+  tint_symbol_13(buffer, (offset + 16u), value[1u]);
+  tint_symbol_13(buffer, (offset + 32u), value[2u]);
 }
 
-fn tint_symbol_19(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat4x2<f32>) {
-  tint_symbol_6(buffer, (offset + 0u), value[0u]);
-  tint_symbol_6(buffer, (offset + 8u), value[1u]);
-  tint_symbol_6(buffer, (offset + 16u), value[2u]);
-  tint_symbol_6(buffer, (offset + 24u), value[3u]);
+fn tint_symbol_23(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat4x2<f32>) {
+  tint_symbol_5(buffer, (offset + 0u), value[0u]);
+  tint_symbol_5(buffer, (offset + 8u), value[1u]);
+  tint_symbol_5(buffer, (offset + 16u), value[2u]);
+  tint_symbol_5(buffer, (offset + 24u), value[3u]);
 }
 
-fn tint_symbol_20(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat4x3<f32>) {
+fn tint_symbol_24(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat4x3<f32>) {
   tint_symbol_9(buffer, (offset + 0u), value[0u]);
   tint_symbol_9(buffer, (offset + 16u), value[1u]);
   tint_symbol_9(buffer, (offset + 32u), value[2u]);
   tint_symbol_9(buffer, (offset + 48u), value[3u]);
 }
 
-fn tint_symbol_21(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat4x4<f32>) {
-  tint_symbol_12(buffer, (offset + 0u), value[0u]);
-  tint_symbol_12(buffer, (offset + 16u), value[1u]);
-  tint_symbol_12(buffer, (offset + 32u), value[2u]);
-  tint_symbol_12(buffer, (offset + 48u), value[3u]);
+fn tint_symbol_25(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat4x4<f32>) {
+  tint_symbol_13(buffer, (offset + 0u), value[0u]);
+  tint_symbol_13(buffer, (offset + 16u), value[1u]);
+  tint_symbol_13(buffer, (offset + 32u), value[2u]);
+  tint_symbol_13(buffer, (offset + 48u), value[3u]);
 }
 
-fn tint_symbol_22(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : array<vec3<f32>, 2u>) {
+fn tint_symbol_26(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat2x2<f16>) {
+  tint_symbol_8(buffer, (offset + 0u), value[0u]);
+  tint_symbol_8(buffer, (offset + 4u), value[1u]);
+}
+
+fn tint_symbol_27(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat2x3<f16>) {
+  tint_symbol_12(buffer, (offset + 0u), value[0u]);
+  tint_symbol_12(buffer, (offset + 8u), value[1u]);
+}
+
+fn tint_symbol_28(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat2x4<f16>) {
+  tint_symbol_16(buffer, (offset + 0u), value[0u]);
+  tint_symbol_16(buffer, (offset + 8u), value[1u]);
+}
+
+fn tint_symbol_29(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat3x2<f16>) {
+  tint_symbol_8(buffer, (offset + 0u), value[0u]);
+  tint_symbol_8(buffer, (offset + 4u), value[1u]);
+  tint_symbol_8(buffer, (offset + 8u), value[2u]);
+}
+
+fn tint_symbol_30(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat3x3<f16>) {
+  tint_symbol_12(buffer, (offset + 0u), value[0u]);
+  tint_symbol_12(buffer, (offset + 8u), value[1u]);
+  tint_symbol_12(buffer, (offset + 16u), value[2u]);
+}
+
+fn tint_symbol_31(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat3x4<f16>) {
+  tint_symbol_16(buffer, (offset + 0u), value[0u]);
+  tint_symbol_16(buffer, (offset + 8u), value[1u]);
+  tint_symbol_16(buffer, (offset + 16u), value[2u]);
+}
+
+fn tint_symbol_32(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat4x2<f16>) {
+  tint_symbol_8(buffer, (offset + 0u), value[0u]);
+  tint_symbol_8(buffer, (offset + 4u), value[1u]);
+  tint_symbol_8(buffer, (offset + 8u), value[2u]);
+  tint_symbol_8(buffer, (offset + 12u), value[3u]);
+}
+
+fn tint_symbol_33(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat4x3<f16>) {
+  tint_symbol_12(buffer, (offset + 0u), value[0u]);
+  tint_symbol_12(buffer, (offset + 8u), value[1u]);
+  tint_symbol_12(buffer, (offset + 16u), value[2u]);
+  tint_symbol_12(buffer, (offset + 24u), value[3u]);
+}
+
+fn tint_symbol_34(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : mat4x4<f16>) {
+  tint_symbol_16(buffer, (offset + 0u), value[0u]);
+  tint_symbol_16(buffer, (offset + 8u), value[1u]);
+  tint_symbol_16(buffer, (offset + 16u), value[2u]);
+  tint_symbol_16(buffer, (offset + 24u), value[3u]);
+}
+
+fn tint_symbol_35(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : array<vec3<f32>, 2u>) {
   var array = value;
+  for(var i = 0u; (i < 2u); i = (i + 1u)) {
+    tint_symbol_9(buffer, (offset + (i * 16u)), array[i]);
+  }
+}
+
+fn tint_symbol_36(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : array<mat4x2<f16>, 2u>) {
+  var array_1 = value;
   for(var i_1 = 0u; (i_1 < 2u); i_1 = (i_1 + 1u)) {
-    tint_symbol_9(buffer, (offset + (i_1 * 16u)), array[i_1]);
+    tint_symbol_32(buffer, (offset + (i_1 * 16u)), array_1[i_1]);
   }
 }
 
 fn tint_symbol(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read_write>, offset : u32, value : SB) {
-  tint_symbol_1(buffer, (offset + 0u), value.a);
-  tint_symbol_2(buffer, (offset + 4u), value.b);
-  tint_symbol_3(buffer, (offset + 8u), value.c);
-  tint_symbol_4(buffer, (offset + 16u), value.d);
-  tint_symbol_5(buffer, (offset + 24u), value.e);
-  tint_symbol_6(buffer, (offset + 32u), value.f);
-  tint_symbol_7(buffer, (offset + 48u), value.g);
-  tint_symbol_8(buffer, (offset + 64u), value.h);
-  tint_symbol_9(buffer, (offset + 80u), value.i);
-  tint_symbol_10(buffer, (offset + 96u), value.j);
-  tint_symbol_11(buffer, (offset + 112u), value.k);
-  tint_symbol_12(buffer, (offset + 128u), value.l);
-  tint_symbol_13(buffer, (offset + 144u), value.m);
-  tint_symbol_14(buffer, (offset + 160u), value.n);
-  tint_symbol_15(buffer, (offset + 192u), value.o);
-  tint_symbol_16(buffer, (offset + 224u), value.p);
-  tint_symbol_17(buffer, (offset + 256u), value.q);
-  tint_symbol_18(buffer, (offset + 304u), value.r);
-  tint_symbol_19(buffer, (offset + 352u), value.s);
-  tint_symbol_20(buffer, (offset + 384u), value.t);
-  tint_symbol_21(buffer, (offset + 448u), value.u);
-  tint_symbol_22(buffer, (offset + 512u), value.v);
+  tint_symbol_1(buffer, (offset + 0u), value.scalar_f32);
+  tint_symbol_2(buffer, (offset + 4u), value.scalar_i32);
+  tint_symbol_3(buffer, (offset + 8u), value.scalar_u32);
+  tint_symbol_4(buffer, (offset + 12u), value.scalar_f16);
+  tint_symbol_5(buffer, (offset + 16u), value.vec2_f32);
+  tint_symbol_6(buffer, (offset + 24u), value.vec2_i32);
+  tint_symbol_7(buffer, (offset + 32u), value.vec2_u32);
+  tint_symbol_8(buffer, (offset + 40u), value.vec2_f16);
+  tint_symbol_9(buffer, (offset + 48u), value.vec3_f32);
+  tint_symbol_10(buffer, (offset + 64u), value.vec3_i32);
+  tint_symbol_11(buffer, (offset + 80u), value.vec3_u32);
+  tint_symbol_12(buffer, (offset + 96u), value.vec3_f16);
+  tint_symbol_13(buffer, (offset + 112u), value.vec4_f32);
+  tint_symbol_14(buffer, (offset + 128u), value.vec4_i32);
+  tint_symbol_15(buffer, (offset + 144u), value.vec4_u32);
+  tint_symbol_16(buffer, (offset + 160u), value.vec4_f16);
+  tint_symbol_17(buffer, (offset + 168u), value.mat2x2_f32);
+  tint_symbol_18(buffer, (offset + 192u), value.mat2x3_f32);
+  tint_symbol_19(buffer, (offset + 224u), value.mat2x4_f32);
+  tint_symbol_20(buffer, (offset + 256u), value.mat3x2_f32);
+  tint_symbol_21(buffer, (offset + 288u), value.mat3x3_f32);
+  tint_symbol_22(buffer, (offset + 336u), value.mat3x4_f32);
+  tint_symbol_23(buffer, (offset + 384u), value.mat4x2_f32);
+  tint_symbol_24(buffer, (offset + 416u), value.mat4x3_f32);
+  tint_symbol_25(buffer, (offset + 480u), value.mat4x4_f32);
+  tint_symbol_26(buffer, (offset + 544u), value.mat2x2_f16);
+  tint_symbol_27(buffer, (offset + 552u), value.mat2x3_f16);
+  tint_symbol_28(buffer, (offset + 568u), value.mat2x4_f16);
+  tint_symbol_29(buffer, (offset + 584u), value.mat3x2_f16);
+  tint_symbol_30(buffer, (offset + 600u), value.mat3x3_f16);
+  tint_symbol_31(buffer, (offset + 624u), value.mat3x4_f16);
+  tint_symbol_32(buffer, (offset + 648u), value.mat4x2_f16);
+  tint_symbol_33(buffer, (offset + 664u), value.mat4x3_f16);
+  tint_symbol_34(buffer, (offset + 696u), value.mat4x4_f16);
+  tint_symbol_35(buffer, (offset + 736u), value.arr2_vec3_f32);
+  tint_symbol_36(buffer, (offset + 768u), value.arr2_mat4x2_f16);
 }
 
 @compute @workgroup_size(1)
@@ -1937,28 +3067,42 @@
 @group(0) @binding(0) var<storage, read_write> sb : SB;
 
 struct SB {
-  a : i32,
-  b : u32,
-  c : f32,
-  d : vec2<i32>,
-  e : vec2<u32>,
-  f : vec2<f32>,
-  g : vec3<i32>,
-  h : vec3<u32>,
-  i : vec3<f32>,
-  j : vec4<i32>,
-  k : vec4<u32>,
-  l : vec4<f32>,
-  m : mat2x2<f32>,
-  n : mat2x3<f32>,
-  o : mat2x4<f32>,
-  p : mat3x2<f32>,
-  q : mat3x3<f32>,
-  r : mat3x4<f32>,
-  s : mat4x2<f32>,
-  t : mat4x3<f32>,
-  u : mat4x4<f32>,
-  v : array<vec3<f32>, 2>,
+  scalar_f32 : f32,
+  scalar_i32 : i32,
+  scalar_u32 : u32,
+  scalar_f16 : f16,
+  vec2_f32 : vec2<f32>,
+  vec2_i32 : vec2<i32>,
+  vec2_u32 : vec2<u32>,
+  vec2_f16 : vec2<f16>,
+  vec3_f32 : vec3<f32>,
+  vec3_i32 : vec3<i32>,
+  vec3_u32 : vec3<u32>,
+  vec3_f16 : vec3<f16>,
+  vec4_f32 : vec4<f32>,
+  vec4_i32 : vec4<i32>,
+  vec4_u32 : vec4<u32>,
+  vec4_f16 : vec4<f16>,
+  mat2x2_f32 : mat2x2<f32>,
+  mat2x3_f32 : mat2x3<f32>,
+  mat2x4_f32 : mat2x4<f32>,
+  mat3x2_f32 : mat3x2<f32>,
+  mat3x3_f32 : mat3x3<f32>,
+  mat3x4_f32 : mat3x4<f32>,
+  mat4x2_f32 : mat4x2<f32>,
+  mat4x3_f32 : mat4x3<f32>,
+  mat4x4_f32 : mat4x4<f32>,
+  mat2x2_f16 : mat2x2<f16>,
+  mat2x3_f16 : mat2x3<f16>,
+  mat2x4_f16 : mat2x4<f16>,
+  mat3x2_f16 : mat3x2<f16>,
+  mat3x3_f16 : mat3x3<f16>,
+  mat3x4_f16 : mat3x4<f16>,
+  mat4x2_f16 : mat4x2<f16>,
+  mat4x3_f16 : mat4x3<f16>,
+  mat4x4_f16 : mat4x4<f16>,
+  arr2_vec3_f32 : array<vec3<f32>, 2>,
+  arr2_mat4x2_f16 : array<mat4x2<f16>, 2>,
 }
 )";
 
diff --git a/src/tint/transform/decompose_strided_matrix.cc b/src/tint/transform/decompose_strided_matrix.cc
index 5494ca2..b7fd7c2 100644
--- a/src/tint/transform/decompose_strided_matrix.cc
+++ b/src/tint/transform/decompose_strided_matrix.cc
@@ -129,7 +129,7 @@
     std::unordered_map<MatrixInfo, Symbol, MatrixInfo::Hasher> mat_to_arr;
     ctx.ReplaceAll([&](const ast::AssignmentStatement* stmt) -> const ast::Statement* {
         if (auto* access = src->Sem().Get<sem::StructMemberAccess>(stmt->lhs)) {
-            if (auto* info = decomposed.Find(access->Member()->Declaration())) {
+            if (auto info = decomposed.Find(access->Member()->Declaration())) {
                 auto fn = utils::GetOrCreate(mat_to_arr, *info, [&] {
                     auto name =
                         b.Symbols().New("mat" + std::to_string(info->matrix->columns()) + "x" +
@@ -168,7 +168,7 @@
     std::unordered_map<MatrixInfo, Symbol, MatrixInfo::Hasher> arr_to_mat;
     ctx.ReplaceAll([&](const ast::MemberAccessorExpression* expr) -> const ast::Expression* {
         if (auto* access = src->Sem().Get<sem::StructMemberAccess>(expr)) {
-            if (auto* info = decomposed.Find(access->Member()->Declaration())) {
+            if (auto info = decomposed.Find(access->Member()->Declaration())) {
                 auto fn = utils::GetOrCreate(arr_to_mat, *info, [&] {
                     auto name =
                         b.Symbols().New("arr_to_mat" + std::to_string(info->matrix->columns()) +
diff --git a/src/tint/transform/promote_initializers_to_let.cc b/src/tint/transform/promote_initializers_to_let.cc
index 9e02c45..7d81989 100644
--- a/src/tint/transform/promote_initializers_to_let.cc
+++ b/src/tint/transform/promote_initializers_to_let.cc
@@ -70,7 +70,8 @@
         }
 
         any_promoted = true;
-        return hoist_to_decl_before.Add(expr, expr->Declaration(), true);
+        return hoist_to_decl_before.Add(expr, expr->Declaration(),
+                                        HoistToDeclBefore::VariableKind::kLet);
     };
 
     for (auto* node : src->ASTNodes().Objects()) {
diff --git a/src/tint/transform/simplify_pointers.cc b/src/tint/transform/simplify_pointers.cc
index b2b99ed..a0855b7 100644
--- a/src/tint/transform/simplify_pointers.cc
+++ b/src/tint/transform/simplify_pointers.cc
@@ -140,7 +140,7 @@
         // variable identifier.
         ctx.ReplaceAll([&](const ast::Expression* expr) -> const ast::Expression* {
             // Look to see if we need to swap this Expression with a saved variable.
-            if (auto* saved_var = saved_vars.Find(expr)) {
+            if (auto saved_var = saved_vars.Find(expr)) {
                 return ctx.dst->Expr(*saved_var);
             }
 
diff --git a/src/tint/transform/std140.cc b/src/tint/transform/std140.cc
index 2116b0f..a371f24 100644
--- a/src/tint/transform/std140.cc
+++ b/src/tint/transform/std140.cc
@@ -265,8 +265,8 @@
     };
 
     /// @returns true if the given matrix needs decomposing to column vectors for std140 layout.
-    /// TODO(crbug.com/tint/1502): This may need adjusting for `f16` matrices.
-    static bool MatrixNeedsDecomposing(const sem::Matrix* mat) { return mat->ColumnStride() == 8; }
+    /// Std140 layout require matrix stride to be 16, otherwise decomposing is needed.
+    static bool MatrixNeedsDecomposing(const sem::Matrix* mat) { return mat->ColumnStride() != 16; }
 
     /// ForkTypes walks the user-declared types in dependency order, forking structures that are
     /// used as uniform buffers which (transitively) use matrices that need std140 decomposition to
@@ -401,7 +401,7 @@
         return Switch(
             ty,  //
             [&](const sem::Struct* str) -> const ast::Type* {
-                if (auto* std140 = std140_structs.Find(str)) {
+                if (auto std140 = std140_structs.Find(str)) {
                     return b.create<ast::TypeName>(*std140);
                 }
                 return nullptr;
@@ -474,7 +474,7 @@
                 // natural size for the matrix. This extra padding needs to be
                 // applied to the last column vector.
                 attributes.Push(
-                    b.MemberSize(AInt(size - mat->ColumnType()->Size() * (num_columns - 1))));
+                    b.MemberSize(AInt(size - mat->ColumnType()->Align() * (num_columns - 1))));
             }
 
             // Build the member
@@ -645,7 +645,8 @@
                 return "mat" + std::to_string(mat->columns()) + "x" + std::to_string(mat->rows()) +
                        "_" + ConvertSuffix(mat->type());
             },
-            [&](const sem::F32*) { return "f32"; },
+            [&](const sem::F32*) { return "f32"; },  //
+            [&](const sem::F16*) { return "f16"; },
             [&](Default) {
                 TINT_ICE(Transform, b.Diagnostics())
                     << "unhandled type for conversion name: " << src->FriendlyName(ty);
@@ -695,7 +696,7 @@
                     // call, or by reassembling a std140 matrix from column vector members.
                     utils::Vector<const ast::Expression*, 8> args;
                     for (auto* member : str->Members()) {
-                        if (auto* col_members = std140_mat_members.Find(member)) {
+                        if (auto col_members = std140_mat_members.Find(member)) {
                             // std140 decomposed matrix. Reassemble.
                             auto* mat_ty = CreateASTTypeFor(ctx, member->Type());
                             auto mat_args =
diff --git a/src/tint/transform/std140.h b/src/tint/transform/std140.h
index 49e663d..769932f 100644
--- a/src/tint/transform/std140.h
+++ b/src/tint/transform/std140.h
@@ -20,11 +20,12 @@
 namespace tint::transform {
 
 /// Std140 is a transform that forks types used in the uniform address space that contain
-/// `matNx2<f32>` matrices into `N`x`vec2<f32>` column vectors. Types that transitively use these
-/// forked types are also forked. `var<uniform>` variables will use these forked types, and
-/// expressions loading from these variables will do appropriate conversions to the regular WGSL
-/// types. As `matNx2<f32>` matrices are the only type that violate std140-layout, this
-/// transformation is sufficient to have any WGSL structure be std140-layout conformant.
+/// `matNx2<f32>` matrices into `N`x`vec2<f32>` column vectors, and `matNxM<f16>` matrices into
+/// `N`x`vecM<f16>` column vectors. Types that transitively use these forked types are also forked.
+/// `var<uniform>` variables will use these forked types, and expressions loading from these
+/// variables will do appropriate conversions to the regular WGSL types. As `matNx2<f32>` and
+/// `matNxM<f16>` matrices are the only type that violate std140-layout, this transformation is
+/// sufficient to have any WGSL structure be std140-layout conformant.
 ///
 /// @note This transform requires the PromoteSideEffectsToDecl transform to have been run first.
 class Std140 final : public Castable<Std140, Transform> {
diff --git a/src/tint/transform/std140_exhaustive_test.cc b/src/tint/transform/std140_exhaustive_test.cc
index 01d2dae..f50e1c4 100644
--- a/src/tint/transform/std140_exhaustive_test.cc
+++ b/src/tint/transform/std140_exhaustive_test.cc
@@ -2838,6 +2838,15 @@
                              {4, 2, MatrixType::f32},
                              {4, 3, MatrixType::f32},
                              {4, 4, MatrixType::f32},
+                             {2, 2, MatrixType::f16},
+                             {2, 3, MatrixType::f16},
+                             {2, 4, MatrixType::f16},
+                             {3, 2, MatrixType::f16},
+                             {3, 3, MatrixType::f16},
+                             {3, 4, MatrixType::f16},
+                             {4, 2, MatrixType::f16},
+                             {4, 3, MatrixType::f16},
+                             {4, 4, MatrixType::f16},
                          }));
 
 using Std140Test_MatrixArray = TransformTestWithParam<MatrixCase>;
@@ -4866,6 +4875,15 @@
                              {4, 2, MatrixType::f32},
                              {4, 3, MatrixType::f32},
                              {4, 4, MatrixType::f32},
+                             {2, 2, MatrixType::f16},
+                             {2, 3, MatrixType::f16},
+                             {2, 4, MatrixType::f16},
+                             {3, 2, MatrixType::f16},
+                             {3, 3, MatrixType::f16},
+                             {3, 4, MatrixType::f16},
+                             {4, 2, MatrixType::f16},
+                             {4, 3, MatrixType::f16},
+                             {4, 4, MatrixType::f16},
                          }));
 
 }  // namespace
diff --git a/src/tint/transform/std140_f16_test.cc b/src/tint/transform/std140_f16_test.cc
new file mode 100644
index 0000000..898bb73
--- /dev/null
+++ b/src/tint/transform/std140_f16_test.cc
@@ -0,0 +1,3596 @@
+// 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/std140.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "src/tint/transform/test_helper.h"
+#include "src/tint/utils/string.h"
+
+namespace tint::transform {
+namespace {
+
+using Std140Test_F16 = TransformTest;
+
+TEST_F(Std140Test_F16, StructMatricesUniform) {
+    auto* src = R"(
+enable f16;
+
+struct S2x2F16 {
+  m : mat2x2<f16>,
+}
+struct S3x2F16 {
+  m : mat3x2<f16>,
+}
+struct S4x2F16 {
+  m : mat4x2<f16>,
+}
+struct S2x3F16 {
+  m : mat2x3<f16>,
+}
+struct S3x3F16 {
+  m : mat3x3<f16>,
+}
+struct S4x3F16 {
+  m : mat4x3<f16>,
+}
+struct S2x4F16 {
+  m : mat2x4<f16>,
+}
+struct S3x4F16 {
+  m : mat3x4<f16>,
+}
+struct S4x4F16 {
+  m : mat4x4<f16>,
+}
+
+@group(2) @binding(2) var<uniform> s2x2f16 : S2x2F16;
+@group(3) @binding(2) var<uniform> s3x2f16 : S3x2F16;
+@group(4) @binding(2) var<uniform> s4x2f16 : S4x2F16;
+@group(2) @binding(3) var<uniform> s2x3f16 : S2x3F16;
+@group(3) @binding(3) var<uniform> s3x3f16 : S3x3F16;
+@group(4) @binding(3) var<uniform> s4x3f16 : S4x3F16;
+@group(2) @binding(4) var<uniform> s2x4f16 : S2x4F16;
+@group(3) @binding(4) var<uniform> s3x4f16 : S3x4F16;
+@group(4) @binding(4) var<uniform> s4x4f16 : S4x4F16;
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct S2x2F16 {
+  m : mat2x2<f16>,
+}
+
+struct S2x2F16_std140 {
+  m_0 : vec2<f16>,
+  m_1 : vec2<f16>,
+}
+
+struct S3x2F16 {
+  m : mat3x2<f16>,
+}
+
+struct S3x2F16_std140 {
+  m_0 : vec2<f16>,
+  m_1 : vec2<f16>,
+  m_2 : vec2<f16>,
+}
+
+struct S4x2F16 {
+  m : mat4x2<f16>,
+}
+
+struct S4x2F16_std140 {
+  m_0 : vec2<f16>,
+  m_1 : vec2<f16>,
+  m_2 : vec2<f16>,
+  m_3 : vec2<f16>,
+}
+
+struct S2x3F16 {
+  m : mat2x3<f16>,
+}
+
+struct S2x3F16_std140 {
+  m_0 : vec3<f16>,
+  m_1 : vec3<f16>,
+}
+
+struct S3x3F16 {
+  m : mat3x3<f16>,
+}
+
+struct S3x3F16_std140 {
+  m_0 : vec3<f16>,
+  m_1 : vec3<f16>,
+  m_2 : vec3<f16>,
+}
+
+struct S4x3F16 {
+  m : mat4x3<f16>,
+}
+
+struct S4x3F16_std140 {
+  m_0 : vec3<f16>,
+  m_1 : vec3<f16>,
+  m_2 : vec3<f16>,
+  m_3 : vec3<f16>,
+}
+
+struct S2x4F16 {
+  m : mat2x4<f16>,
+}
+
+struct S2x4F16_std140 {
+  m_0 : vec4<f16>,
+  m_1 : vec4<f16>,
+}
+
+struct S3x4F16 {
+  m : mat3x4<f16>,
+}
+
+struct S3x4F16_std140 {
+  m_0 : vec4<f16>,
+  m_1 : vec4<f16>,
+  m_2 : vec4<f16>,
+}
+
+struct S4x4F16 {
+  m : mat4x4<f16>,
+}
+
+struct S4x4F16_std140 {
+  m_0 : vec4<f16>,
+  m_1 : vec4<f16>,
+  m_2 : vec4<f16>,
+  m_3 : vec4<f16>,
+}
+
+@group(2) @binding(2) var<uniform> s2x2f16 : S2x2F16_std140;
+
+@group(3) @binding(2) var<uniform> s3x2f16 : S3x2F16_std140;
+
+@group(4) @binding(2) var<uniform> s4x2f16 : S4x2F16_std140;
+
+@group(2) @binding(3) var<uniform> s2x3f16 : S2x3F16_std140;
+
+@group(3) @binding(3) var<uniform> s3x3f16 : S3x3F16_std140;
+
+@group(4) @binding(3) var<uniform> s4x3f16 : S4x3F16_std140;
+
+@group(2) @binding(4) var<uniform> s2x4f16 : S2x4F16_std140;
+
+@group(3) @binding(4) var<uniform> s3x4f16 : S3x4F16_std140;
+
+@group(4) @binding(4) var<uniform> s4x4f16 : S4x4F16_std140;
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+// In the following tests we only test `mat2x3<f16>`, and set all constant column index to 1, row
+// index 0, inner array index 2, and outer array index 3. For exhaustive tests, i.e. tests on all
+// matrix shape and different valid constant index, please refer to std140_exhaustive_test.cc
+
+TEST_F(Std140Test_F16, SingleStructMatUniform_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct S {
+  m : mat2x3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct S {
+  m : mat2x3<f16>,
+}
+
+struct S_std140 {
+  m_0 : vec3<f16>,
+  m_1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, CustomAlign_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct S {
+  before : i32,
+  @align(128)
+  m : mat2x3<f16>,
+  after : i32,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct S {
+  before : i32,
+  @align(128)
+  m : mat2x3<f16>,
+  after : i32,
+}
+
+struct S_std140 {
+  before : i32,
+  @align(128i)
+  m_0 : vec3<f16>,
+  m_1 : vec3<f16>,
+  after : i32,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, CustomSizeMat_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct S {
+  before : i32,
+  @size(128)
+  m : mat2x3<f16>,
+  after : i32,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct S {
+  before : i32,
+  @size(128)
+  m : mat2x3<f16>,
+  after : i32,
+}
+
+struct S_std140 {
+  before : i32,
+  m_0 : vec3<f16>,
+  @size(120)
+  m_1 : vec3<f16>,
+  after : i32,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, CustomAlignAndSize_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct S {
+  before : i32,
+  @align(128) @size(128)
+  m : mat2x3<f16>,
+  after : i32,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct S {
+  before : i32,
+  @align(128) @size(128)
+  m : mat2x3<f16>,
+  after : i32,
+}
+
+struct S_std140 {
+  before : i32,
+  @align(128i)
+  m_0 : vec3<f16>,
+  @size(120)
+  m_1 : vec3<f16>,
+  after : i32,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, MatrixUsageInForLoop_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct S {
+  m : mat2x3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+  for(var i = u32(s.m[0][0]); (i < u32(s.m[i][1])); i += u32(s.m[1][i])) {
+  }
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct S {
+  m : mat2x3<f16>,
+}
+
+struct S_std140 {
+  m_0 : vec3<f16>,
+  m_1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn load_s_m_p0_1(p0 : u32) -> f16 {
+  switch(p0) {
+    case 0u: {
+      return s.m_0[1u];
+    }
+    case 1u: {
+      return s.m_1[1u];
+    }
+    default: {
+      return f16();
+    }
+  }
+}
+
+fn f() {
+  for(var i = u32(s.m_0[0u]); (i < u32(load_s_m_p0_1(u32(i)))); i += u32(s.m_1[i])) {
+  }
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, MatUniform_LoadMatrix_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> m : mat2x3<f16>;
+
+fn f() {
+  let l = m;
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> m : mat2x3_f16;
+
+fn conv_mat2x3_f16(val : mat2x3_f16) -> mat2x3<f16> {
+  return mat2x3<f16>(val.col0, val.col1);
+}
+
+fn f() {
+  let l = conv_mat2x3_f16(m);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, MatUniform_LoadColumn_ConstIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : mat2x3<f16>;
+
+fn f() {
+  let l = a[1];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : mat2x3_f16;
+
+fn f() {
+  let l = a.col1;
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, MatUniform_LoadColumn_VariableIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : mat2x3<f16>;
+
+fn f() {
+  let I = 1;
+  let l = a[I];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : mat2x3_f16;
+
+fn load_a_p0(p0 : u32) -> vec3<f16> {
+  switch(p0) {
+    case 0u: {
+      return a.col0;
+    }
+    case 1u: {
+      return a.col1;
+    }
+    default: {
+      return vec3<f16>();
+    }
+  }
+}
+
+fn f() {
+  let I = 1;
+  let l = load_a_p0(u32(I));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, MatUniform_LoadColumnSwizzle_ConstIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : mat2x3<f16>;
+
+fn f() {
+  let l = a[1].yzx;
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : mat2x3_f16;
+
+fn f() {
+  let l = a.col1.yzx;
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, MatUniform_LoadColumnSwizzle_VariableIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : mat2x3<f16>;
+
+fn f() {
+  let I = 1;
+  let l = a[I].yzx;
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : mat2x3_f16;
+
+fn load_a_p0_yzx(p0 : u32) -> vec3<f16> {
+  switch(p0) {
+    case 0u: {
+      return a.col0.yzx;
+    }
+    case 1u: {
+      return a.col1.yzx;
+    }
+    default: {
+      return vec3<f16>();
+    }
+  }
+}
+
+fn f() {
+  let I = 1;
+  let l = load_a_p0_yzx(u32(I));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, MatUniform_LoadScalar_ConstColumnIndex_ConstRowIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : mat2x3<f16>;
+
+fn f() {
+  let l = a[1][0];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : mat2x3_f16;
+
+fn f() {
+  let l = a.col1[0u];
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, MatUniform_LoadScalar_VariableColumnIndex_ConstRowIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : mat2x3<f16>;
+
+fn f() {
+  let I = 0;
+  let l = a[I][0];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : mat2x3_f16;
+
+fn load_a_p0_0(p0 : u32) -> f16 {
+  switch(p0) {
+    case 0u: {
+      return a.col0[0u];
+    }
+    case 1u: {
+      return a.col1[0u];
+    }
+    default: {
+      return f16();
+    }
+  }
+}
+
+fn f() {
+  let I = 0;
+  let l = load_a_p0_0(u32(I));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, MatUniform_LoadScalar_ConstColumnIndex_VariableRowIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : mat2x3<f16>;
+
+fn f() {
+  let I = 0;
+  let l = a[1][I];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : mat2x3_f16;
+
+fn f() {
+  let I = 0;
+  let l = a.col1[I];
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, MatUniform_LoadScalar_VariableColumnIndex_VariableRowIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : mat2x3<f16>;
+
+fn f() {
+  let I = 0;
+  let l = a[I][I];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : mat2x3_f16;
+
+fn load_a_p0_p1(p0 : u32, p1 : u32) -> f16 {
+  switch(p0) {
+    case 0u: {
+      return a.col0[p1];
+    }
+    case 1u: {
+      return a.col1[p1];
+    }
+    default: {
+      return f16();
+    }
+  }
+}
+
+fn f() {
+  let I = 0;
+  let l = load_a_p0_p1(u32(I), u32(I));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, StructMatUniform_NameCollision_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct S {
+  m_1 : i32,
+  m : mat2x3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct S {
+  m_1 : i32,
+  m : mat2x3<f16>,
+}
+
+struct S_std140 {
+  m_1 : i32,
+  m__0 : vec3<f16>,
+  m__1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, StructMatUniform_LoadStruct_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct S {
+  m : mat2x3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+  let l = s;
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct S {
+  m : mat2x3<f16>,
+}
+
+struct S_std140 {
+  m_0 : vec3<f16>,
+  m_1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn conv_S(val : S_std140) -> S {
+  return S(mat2x3<f16>(val.m_0, val.m_1));
+}
+
+fn f() {
+  let l = conv_S(s);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, StructMatUniform_LoadMatrix_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct S {
+  m : mat2x3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+  let l = s.m;
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct S {
+  m : mat2x3<f16>,
+}
+
+struct S_std140 {
+  m_0 : vec3<f16>,
+  m_1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn load_s_m() -> mat2x3<f16> {
+  let s = &(s);
+  return mat2x3<f16>((*(s)).m_0, (*(s)).m_1);
+}
+
+fn f() {
+  let l = load_s_m();
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, StructMatUniform_LoadColumn_ConstIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct S {
+  m : mat2x3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+  let l = s.m[1];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct S {
+  m : mat2x3<f16>,
+}
+
+struct S_std140 {
+  m_0 : vec3<f16>,
+  m_1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn f() {
+  let l = s.m_1;
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, StructMatUniform_LoadColumn_VariableIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct S {
+  m : mat2x3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+  let I = 0;
+  let l = s.m[I];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct S {
+  m : mat2x3<f16>,
+}
+
+struct S_std140 {
+  m_0 : vec3<f16>,
+  m_1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn load_s_m_p0(p0 : u32) -> vec3<f16> {
+  switch(p0) {
+    case 0u: {
+      return s.m_0;
+    }
+    case 1u: {
+      return s.m_1;
+    }
+    default: {
+      return vec3<f16>();
+    }
+  }
+}
+
+fn f() {
+  let I = 0;
+  let l = load_s_m_p0(u32(I));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, StructMatUniform_LoadScalar_ConstColumnIndex_ConstRowIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct S {
+  m : mat2x3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+  let l = s.m[1][0];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct S {
+  m : mat2x3<f16>,
+}
+
+struct S_std140 {
+  m_0 : vec3<f16>,
+  m_1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn f() {
+  let l = s.m_1[0u];
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, StructMatUniform_LoadScalar_VariableColumnIndex_ConstRowIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct S {
+  m : mat2x3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+  let I = 0;
+  let l = s.m[I][0];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct S {
+  m : mat2x3<f16>,
+}
+
+struct S_std140 {
+  m_0 : vec3<f16>,
+  m_1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn load_s_m_p0_0(p0 : u32) -> f16 {
+  switch(p0) {
+    case 0u: {
+      return s.m_0[0u];
+    }
+    case 1u: {
+      return s.m_1[0u];
+    }
+    default: {
+      return f16();
+    }
+  }
+}
+
+fn f() {
+  let I = 0;
+  let l = load_s_m_p0_0(u32(I));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, StructMatUniform_LoadScalar_ConstColumnIndex_VariableRowIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct S {
+  m : mat2x3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+  let I = 0;
+  let l = s.m[1][I];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct S {
+  m : mat2x3<f16>,
+}
+
+struct S_std140 {
+  m_0 : vec3<f16>,
+  m_1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn f() {
+  let I = 0;
+  let l = s.m_1[I];
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, StructMatUniform_LoadScalar_VariableColumnIndex_VariableRowIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct S {
+  m : mat2x3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+  let I = 0;
+  let l = s.m[I][I];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct S {
+  m : mat2x3<f16>,
+}
+
+struct S_std140 {
+  m_0 : vec3<f16>,
+  m_1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn load_s_m_p0_p1(p0 : u32, p1 : u32) -> f16 {
+  switch(p0) {
+    case 0u: {
+      return s.m_0[p1];
+    }
+    case 1u: {
+      return s.m_1[p1];
+    }
+    default: {
+      return f16();
+    }
+  }
+}
+
+fn f() {
+  let I = 0;
+  let l = load_s_m_p0_p1(u32(I), u32(I));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, ArrayStructMatUniform_LoadArray_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct S {
+  @size(64)
+  m : mat2x3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<S, 3>;
+
+fn f() {
+  let l = a;
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct S {
+  @size(64)
+  m : mat2x3<f16>,
+}
+
+struct S_std140 {
+  m_0 : vec3<f16>,
+  @size(56)
+  m_1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
+
+fn conv_S(val : S_std140) -> S {
+  return S(mat2x3<f16>(val.m_0, val.m_1));
+}
+
+fn conv_arr3_S(val : array<S_std140, 3u>) -> array<S, 3u> {
+  var arr : array<S, 3u>;
+  for(var i : u32; (i < 3u); i = (i + 1)) {
+    arr[i] = conv_S(val[i]);
+  }
+  return arr;
+}
+
+fn f() {
+  let l = conv_arr3_S(a);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, ArrayStructMatUniform_LoadStruct_ConstIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct S {
+  @size(64)
+  m : mat2x3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<S, 3>;
+
+fn f() {
+  let l = a[2];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct S {
+  @size(64)
+  m : mat2x3<f16>,
+}
+
+struct S_std140 {
+  m_0 : vec3<f16>,
+  @size(56)
+  m_1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
+
+fn conv_S(val : S_std140) -> S {
+  return S(mat2x3<f16>(val.m_0, val.m_1));
+}
+
+fn f() {
+  let l = conv_S(a[2u]);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, ArrayStructMatUniform_LoadStruct_VariableIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct S {
+  @size(64)
+  m : mat2x3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<S, 3>;
+
+fn f() {
+  let I = 1;
+  let l = a[I];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct S {
+  @size(64)
+  m : mat2x3<f16>,
+}
+
+struct S_std140 {
+  m_0 : vec3<f16>,
+  @size(56)
+  m_1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
+
+fn conv_S(val : S_std140) -> S {
+  return S(mat2x3<f16>(val.m_0, val.m_1));
+}
+
+fn f() {
+  let I = 1;
+  let l = conv_S(a[I]);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, ArrayStructMatUniform_LoadMatrix_ConstArrayIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct S {
+  @size(64)
+  m : mat2x3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<S, 3>;
+
+fn f() {
+  let l = a[2].m;
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct S {
+  @size(64)
+  m : mat2x3<f16>,
+}
+
+struct S_std140 {
+  m_0 : vec3<f16>,
+  @size(56)
+  m_1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
+
+fn load_a_2_m() -> mat2x3<f16> {
+  let s = &(a[2u]);
+  return mat2x3<f16>((*(s)).m_0, (*(s)).m_1);
+}
+
+fn f() {
+  let l = load_a_2_m();
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, ArrayStructMatUniform_LoadMatrix_VariableArrayIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct S {
+  @size(64)
+  m : mat2x3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<S, 3>;
+
+fn f() {
+  let I = 1;
+  let l = a[I].m;
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct S {
+  @size(64)
+  m : mat2x3<f16>,
+}
+
+struct S_std140 {
+  m_0 : vec3<f16>,
+  @size(56)
+  m_1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
+
+fn load_a_p0_m(p0 : u32) -> mat2x3<f16> {
+  let s = &(a[p0]);
+  return mat2x3<f16>((*(s)).m_0, (*(s)).m_1);
+}
+
+fn f() {
+  let I = 1;
+  let l = load_a_p0_m(u32(I));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16,
+       ArrayStructMatUniform_LoadColumn_ConstArrayIndex_ConstColumnIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct S {
+  @size(64)
+  m : mat2x3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<S, 3>;
+
+fn f() {
+  let l = a[2].m[1];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct S {
+  @size(64)
+  m : mat2x3<f16>,
+}
+
+struct S_std140 {
+  m_0 : vec3<f16>,
+  @size(56)
+  m_1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
+
+fn f() {
+  let l = a[2u].m_1;
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16,
+       ArrayStructMatUniform_LoadColumn_VariableArrayIndex_ConstColumnIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct S {
+  @size(64)
+  m : mat2x3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<S, 3>;
+
+fn f() {
+  let I = 1;
+  let l = a[I].m[1];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct S {
+  @size(64)
+  m : mat2x3<f16>,
+}
+
+struct S_std140 {
+  m_0 : vec3<f16>,
+  @size(56)
+  m_1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
+
+fn f() {
+  let I = 1;
+  let l = a[I].m_1;
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16,
+       ArrayStructMatUniform_LoadColumn_ConstArrayIndex_VariableColumnIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct S {
+  @size(64)
+  m : mat2x3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<S, 3>;
+
+fn f() {
+  let I = 1;
+  let l = a[2].m[I];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct S {
+  @size(64)
+  m : mat2x3<f16>,
+}
+
+struct S_std140 {
+  m_0 : vec3<f16>,
+  @size(56)
+  m_1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
+
+fn load_a_2_m_p0(p0 : u32) -> vec3<f16> {
+  switch(p0) {
+    case 0u: {
+      return a[2u].m_0;
+    }
+    case 1u: {
+      return a[2u].m_1;
+    }
+    default: {
+      return vec3<f16>();
+    }
+  }
+}
+
+fn f() {
+  let I = 1;
+  let l = load_a_2_m_p0(u32(I));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16,
+       ArrayStructMatUniform_LoadColumn_VariableArrayIndex_VariableColumnIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct S {
+  @size(64)
+  m : mat2x3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<S, 3>;
+
+fn f() {
+  let I = 1;
+  let l = a[I].m[I];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct S {
+  @size(64)
+  m : mat2x3<f16>,
+}
+
+struct S_std140 {
+  m_0 : vec3<f16>,
+  @size(56)
+  m_1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
+
+fn load_a_p0_m_p1(p0 : u32, p1 : u32) -> vec3<f16> {
+  switch(p1) {
+    case 0u: {
+      return a[p0].m_0;
+    }
+    case 1u: {
+      return a[p0].m_1;
+    }
+    default: {
+      return vec3<f16>();
+    }
+  }
+}
+
+fn f() {
+  let I = 1;
+  let l = load_a_p0_m_p1(u32(I), u32(I));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, ArrayStructArrayStructMatUniform_Loads_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct Inner {
+  @size(64)
+  m : mat2x3<f16>,
+}
+
+struct Outer {
+  a : array<Inner, 4>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<Outer, 4>;
+
+fn f() {
+  let I = 1;
+  let J = 2;
+  let K = 0;
+  let l_a : array<Outer, 4> = a;
+  let l_a_1 : Outer = a[1];
+  let l_a_I : Outer = a[I];
+  let l_a_2_a : array<Inner, 4> = a[2].a;
+  let l_a_I_a : array<Inner, 4> = a[I].a;
+  let l_a_3_a_1 : Inner = a[3].a[1];
+  let l_a_3_a_I : Inner = a[3].a[I];
+  let l_a_I_a_1 : Inner = a[I].a[1];
+  let l_a_I_a_J : Inner = a[I].a[J];
+  let l_a_0_a_2_m : mat2x3<f16> = a[0].a[2].m;
+  let l_a_0_a_I_m : mat2x3<f16> = a[0].a[I].m;
+  let l_a_I_a_2_m : mat2x3<f16> = a[I].a[2].m;
+  let l_a_I_a_J_m : mat2x3<f16> = a[I].a[J].m;
+  let l_a_1_a_3_m_0 : vec3<f16> = a[1].a[3].m[0];
+  let l_a_I_a_J_m_K : vec3<f16> = a[I].a[J].m[K];
+  let l_a_2_a_0_m_1_0 : f16 = a[2].a[0].m[1][0];
+  let l_a_I_a_J_m_K_I : f16 = a[I].a[J].m[K][I];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct Inner {
+  @size(64)
+  m : mat2x3<f16>,
+}
+
+struct Inner_std140 {
+  m_0 : vec3<f16>,
+  @size(56)
+  m_1 : vec3<f16>,
+}
+
+struct Outer {
+  a : array<Inner, 4>,
+}
+
+struct Outer_std140 {
+  a : array<Inner_std140, 4u>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<Outer_std140, 4u>;
+
+fn conv_Inner(val : Inner_std140) -> Inner {
+  return Inner(mat2x3<f16>(val.m_0, val.m_1));
+}
+
+fn conv_arr4_Inner(val : array<Inner_std140, 4u>) -> array<Inner, 4u> {
+  var arr : array<Inner, 4u>;
+  for(var i : u32; (i < 4u); i = (i + 1)) {
+    arr[i] = conv_Inner(val[i]);
+  }
+  return arr;
+}
+
+fn conv_Outer(val : Outer_std140) -> Outer {
+  return Outer(conv_arr4_Inner(val.a));
+}
+
+fn conv_arr4_Outer(val : array<Outer_std140, 4u>) -> array<Outer, 4u> {
+  var arr : array<Outer, 4u>;
+  for(var i : u32; (i < 4u); i = (i + 1)) {
+    arr[i] = conv_Outer(val[i]);
+  }
+  return arr;
+}
+
+fn load_a_0_a_2_m() -> mat2x3<f16> {
+  let s = &(a[0u].a[2u]);
+  return mat2x3<f16>((*(s)).m_0, (*(s)).m_1);
+}
+
+fn load_a_0_a_p0_m(p0 : u32) -> mat2x3<f16> {
+  let s = &(a[0u].a[p0]);
+  return mat2x3<f16>((*(s)).m_0, (*(s)).m_1);
+}
+
+fn load_a_p0_a_2_m(p0 : u32) -> mat2x3<f16> {
+  let s = &(a[p0].a[2u]);
+  return mat2x3<f16>((*(s)).m_0, (*(s)).m_1);
+}
+
+fn load_a_p0_a_p1_m(p0 : u32, p1 : u32) -> mat2x3<f16> {
+  let s = &(a[p0].a[p1]);
+  return mat2x3<f16>((*(s)).m_0, (*(s)).m_1);
+}
+
+fn load_a_p0_a_p1_m_p2(p0 : u32, p1 : u32, p2 : u32) -> vec3<f16> {
+  switch(p2) {
+    case 0u: {
+      return a[p0].a[p1].m_0;
+    }
+    case 1u: {
+      return a[p0].a[p1].m_1;
+    }
+    default: {
+      return vec3<f16>();
+    }
+  }
+}
+
+fn load_a_p0_a_p1_m_p2_p3(p0 : u32, p1 : u32, p2 : u32, p3 : u32) -> f16 {
+  switch(p2) {
+    case 0u: {
+      return a[p0].a[p1].m_0[p3];
+    }
+    case 1u: {
+      return a[p0].a[p1].m_1[p3];
+    }
+    default: {
+      return f16();
+    }
+  }
+}
+
+fn f() {
+  let I = 1;
+  let J = 2;
+  let K = 0;
+  let l_a : array<Outer, 4> = conv_arr4_Outer(a);
+  let l_a_1 : Outer = conv_Outer(a[1u]);
+  let l_a_I : Outer = conv_Outer(a[I]);
+  let l_a_2_a : array<Inner, 4> = conv_arr4_Inner(a[2u].a);
+  let l_a_I_a : array<Inner, 4> = conv_arr4_Inner(a[I].a);
+  let l_a_3_a_1 : Inner = conv_Inner(a[3u].a[1u]);
+  let l_a_3_a_I : Inner = conv_Inner(a[3u].a[I]);
+  let l_a_I_a_1 : Inner = conv_Inner(a[I].a[1u]);
+  let l_a_I_a_J : Inner = conv_Inner(a[I].a[J]);
+  let l_a_0_a_2_m : mat2x3<f16> = load_a_0_a_2_m();
+  let l_a_0_a_I_m : mat2x3<f16> = load_a_0_a_p0_m(u32(I));
+  let l_a_I_a_2_m : mat2x3<f16> = load_a_p0_a_2_m(u32(I));
+  let l_a_I_a_J_m : mat2x3<f16> = load_a_p0_a_p1_m(u32(I), u32(J));
+  let l_a_1_a_3_m_0 : vec3<f16> = a[1u].a[3u].m_0;
+  let l_a_I_a_J_m_K : vec3<f16> = load_a_p0_a_p1_m_p2(u32(I), u32(J), u32(K));
+  let l_a_2_a_0_m_1_0 : f16 = a[2u].a[0u].m_1[0u];
+  let l_a_I_a_J_m_K_I : f16 = load_a_p0_a_p1_m_p2_p3(u32(I), u32(J), u32(K), u32(I));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, ArrayStructArrayStructMatUniform_LoadsViaPtrs_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct Inner {
+  @size(64)
+  m : mat2x3<f16>,
+}
+
+struct Outer {
+  a : array<Inner, 4>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<Outer, 4>;
+
+fn f() {
+  let I = 1;
+  let J = 2;
+  let K = 0;
+  let p_a = &(a);
+  let p_a_3 = &((*(p_a))[3]);
+  let p_a_I = &((*(p_a))[I]);
+  let p_a_3_a = &((*(p_a_3)).a);
+  let p_a_I_a = &((*(p_a_I)).a);
+  let p_a_3_a_2 = &((*(p_a_3_a))[2]);
+  let p_a_3_a_I = &((*(p_a_3_a))[I]);
+  let p_a_I_a_2 = &((*(p_a_I_a))[2]);
+  let p_a_I_a_J = &((*(p_a_I_a))[J]);
+  let p_a_3_a_2_m = &((*(p_a_3_a_2)).m);
+  let p_a_3_a_I_m = &((*(p_a_3_a_I)).m);
+  let p_a_I_a_2_m = &((*(p_a_I_a_2)).m);
+  let p_a_I_a_J_m = &((*(p_a_I_a_J)).m);
+  let p_a_3_a_2_m_1 = &((*(p_a_3_a_2_m))[1]);
+  let p_a_I_a_J_m_K = &((*(p_a_I_a_J_m))[K]);
+  let l_a : array<Outer, 4> = *(p_a);
+  let l_a_3 : Outer = *(p_a_3);
+  let l_a_I : Outer = *(p_a_I);
+  let l_a_3_a : array<Inner, 4> = *(p_a_3_a);
+  let l_a_I_a : array<Inner, 4> = *(p_a_I_a);
+  let l_a_3_a_2 : Inner = *(p_a_3_a_2);
+  let l_a_3_a_I : Inner = *(p_a_3_a_I);
+  let l_a_I_a_2 : Inner = *(p_a_I_a_2);
+  let l_a_I_a_J : Inner = *(p_a_I_a_J);
+  let l_a_3_a_2_m : mat2x3<f16> = *(p_a_3_a_2_m);
+  let l_a_3_a_I_m : mat2x3<f16> = *(p_a_3_a_I_m);
+  let l_a_I_a_2_m : mat2x3<f16> = *(p_a_I_a_2_m);
+  let l_a_I_a_J_m : mat2x3<f16> = *(p_a_I_a_J_m);
+  let l_a_3_a_2_m_1 : vec3<f16> = *(p_a_3_a_2_m_1);
+  let l_a_I_a_J_m_K : vec3<f16> = *(p_a_I_a_J_m_K);
+  let l_a_2_a_0_m_1_0 : f16 = (*(p_a_3_a_2_m_1))[0];
+  let l_a_I_a_J_m_K_I : f16 = (*(p_a_I_a_J_m_K))[I];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct Inner {
+  @size(64)
+  m : mat2x3<f16>,
+}
+
+struct Inner_std140 {
+  m_0 : vec3<f16>,
+  @size(56)
+  m_1 : vec3<f16>,
+}
+
+struct Outer {
+  a : array<Inner, 4>,
+}
+
+struct Outer_std140 {
+  a : array<Inner_std140, 4u>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<Outer_std140, 4u>;
+
+fn conv_Inner(val : Inner_std140) -> Inner {
+  return Inner(mat2x3<f16>(val.m_0, val.m_1));
+}
+
+fn conv_arr4_Inner(val : array<Inner_std140, 4u>) -> array<Inner, 4u> {
+  var arr : array<Inner, 4u>;
+  for(var i : u32; (i < 4u); i = (i + 1)) {
+    arr[i] = conv_Inner(val[i]);
+  }
+  return arr;
+}
+
+fn conv_Outer(val : Outer_std140) -> Outer {
+  return Outer(conv_arr4_Inner(val.a));
+}
+
+fn conv_arr4_Outer(val : array<Outer_std140, 4u>) -> array<Outer, 4u> {
+  var arr : array<Outer, 4u>;
+  for(var i : u32; (i < 4u); i = (i + 1)) {
+    arr[i] = conv_Outer(val[i]);
+  }
+  return arr;
+}
+
+fn load_a_3_a_2_m() -> mat2x3<f16> {
+  let s = &(a[3u].a[2u]);
+  return mat2x3<f16>((*(s)).m_0, (*(s)).m_1);
+}
+
+fn load_a_3_a_p0_m(p0 : u32) -> mat2x3<f16> {
+  let s = &(a[3u].a[p0]);
+  return mat2x3<f16>((*(s)).m_0, (*(s)).m_1);
+}
+
+fn load_a_p0_a_2_m(p0 : u32) -> mat2x3<f16> {
+  let s = &(a[p0].a[2u]);
+  return mat2x3<f16>((*(s)).m_0, (*(s)).m_1);
+}
+
+fn load_a_p0_a_p1_m(p0 : u32, p1 : u32) -> mat2x3<f16> {
+  let s = &(a[p0].a[p1]);
+  return mat2x3<f16>((*(s)).m_0, (*(s)).m_1);
+}
+
+fn load_a_p0_a_p1_m_p2(p0 : u32, p1 : u32, p2 : u32) -> vec3<f16> {
+  switch(p2) {
+    case 0u: {
+      return a[p0].a[p1].m_0;
+    }
+    case 1u: {
+      return a[p0].a[p1].m_1;
+    }
+    default: {
+      return vec3<f16>();
+    }
+  }
+}
+
+fn load_a_p0_a_p1_m_p2_p3(p0 : u32, p1 : u32, p2 : u32, p3 : u32) -> f16 {
+  switch(p2) {
+    case 0u: {
+      return a[p0].a[p1].m_0[p3];
+    }
+    case 1u: {
+      return a[p0].a[p1].m_1[p3];
+    }
+    default: {
+      return f16();
+    }
+  }
+}
+
+fn f() {
+  let I = 1;
+  let J = 2;
+  let K = 0;
+  let p_a = conv_arr4_Outer(a);
+  let p_a_3 = conv_Outer(a[3u]);
+  let p_a_I = conv_Outer(a[I]);
+  let p_a_3_a = conv_arr4_Inner(a[3u].a);
+  let p_a_I_a = conv_arr4_Inner(a[I].a);
+  let p_a_3_a_2 = conv_Inner(a[3u].a[2u]);
+  let p_a_3_a_I = conv_Inner(a[3u].a[I]);
+  let p_a_I_a_2 = conv_Inner(a[I].a[2u]);
+  let p_a_I_a_J = conv_Inner(a[I].a[J]);
+  let p_a_3_a_2_m = load_a_3_a_2_m();
+  let p_a_3_a_I_m = load_a_3_a_p0_m(u32(I));
+  let p_a_I_a_2_m = load_a_p0_a_2_m(u32(I));
+  let p_a_I_a_J_m = load_a_p0_a_p1_m(u32(I), u32(J));
+  let p_a_3_a_2_m_1 = a[3u].a[2u].m_1;
+  let p_a_I_a_J_m_K = load_a_p0_a_p1_m_p2(u32(I), u32(J), u32(K));
+  let l_a : array<Outer, 4> = conv_arr4_Outer(a);
+  let l_a_3 : Outer = conv_Outer(a[3u]);
+  let l_a_I : Outer = conv_Outer(a[I]);
+  let l_a_3_a : array<Inner, 4> = conv_arr4_Inner(a[3u].a);
+  let l_a_I_a : array<Inner, 4> = conv_arr4_Inner(a[I].a);
+  let l_a_3_a_2 : Inner = conv_Inner(a[3u].a[2u]);
+  let l_a_3_a_I : Inner = conv_Inner(a[3u].a[I]);
+  let l_a_I_a_2 : Inner = conv_Inner(a[I].a[2u]);
+  let l_a_I_a_J : Inner = conv_Inner(a[I].a[J]);
+  let l_a_3_a_2_m : mat2x3<f16> = load_a_3_a_2_m();
+  let l_a_3_a_I_m : mat2x3<f16> = load_a_3_a_p0_m(u32(I));
+  let l_a_I_a_2_m : mat2x3<f16> = load_a_p0_a_2_m(u32(I));
+  let l_a_I_a_J_m : mat2x3<f16> = load_a_p0_a_p1_m(u32(I), u32(J));
+  let l_a_3_a_2_m_1 : vec3<f16> = a[3u].a[2u].m_1;
+  let l_a_I_a_J_m_K : vec3<f16> = load_a_p0_a_p1_m_p2(u32(I), u32(J), u32(K));
+  let l_a_2_a_0_m_1_0 : f16 = a[3u].a[2u].m_1[0u];
+  let l_a_I_a_J_m_K_I : f16 = load_a_p0_a_p1_m_p2_p3(u32(I), u32(J), u32(K), u32(I));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, ArrayStructMatUniform_CopyArray_UniformToStorage_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct S {
+  @size(64)
+  m : mat2x3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> u : array<S, 4>;
+
+@group(0) @binding(1) var<storage, read_write> s : array<S, 4>;
+
+fn f() {
+  s = u;
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct S {
+  @size(64)
+  m : mat2x3<f16>,
+}
+
+struct S_std140 {
+  m_0 : vec3<f16>,
+  @size(56)
+  m_1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> u : array<S_std140, 4u>;
+
+@group(0) @binding(1) var<storage, read_write> s : array<S, 4>;
+
+fn conv_S(val : S_std140) -> S {
+  return S(mat2x3<f16>(val.m_0, val.m_1));
+}
+
+fn conv_arr4_S(val : array<S_std140, 4u>) -> array<S, 4u> {
+  var arr : array<S, 4u>;
+  for(var i : u32; (i < 4u); i = (i + 1)) {
+    arr[i] = conv_S(val[i]);
+  }
+  return arr;
+}
+
+fn f() {
+  s = conv_arr4_S(u);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, ArrayStructMatUniform_CopyStruct_UniformToWorkgroup_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct S {
+  v : vec4<i32>,
+  @size(64)
+  m : mat2x3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> u : array<S, 4>;
+
+var<workgroup> w : array<S, 4>;
+
+fn f() {
+  w[0] = u[1];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct S {
+  v : vec4<i32>,
+  @size(64)
+  m : mat2x3<f16>,
+}
+
+struct S_std140 {
+  v : vec4<i32>,
+  m_0 : vec3<f16>,
+  @size(56)
+  m_1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> u : array<S_std140, 4u>;
+
+var<workgroup> w : array<S, 4>;
+
+fn conv_S(val : S_std140) -> S {
+  return S(val.v, mat2x3<f16>(val.m_0, val.m_1));
+}
+
+fn f() {
+  w[0] = conv_S(u[1u]);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, ArrayStructMatUniform_CopyMatrix_UniformToPrivate_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct S {
+  v : vec4<i32>,
+  @size(64)
+  m : mat2x3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> u : array<S, 3>;
+
+var<private> p : array<S, 4>;
+
+fn f() {
+  p[2].m = u[1].m;
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct S {
+  v : vec4<i32>,
+  @size(64)
+  m : mat2x3<f16>,
+}
+
+struct S_std140 {
+  v : vec4<i32>,
+  m_0 : vec3<f16>,
+  @size(56)
+  m_1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> u : array<S_std140, 3u>;
+
+var<private> p : array<S, 4>;
+
+fn load_u_1_m() -> mat2x3<f16> {
+  let s = &(u[1u]);
+  return mat2x3<f16>((*(s)).m_0, (*(s)).m_1);
+}
+
+fn f() {
+  p[2].m = load_u_1_m();
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, ArrayStructMatUniform_CopyColumn_UniformToStorage_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct S {
+  @size(64)
+  m : mat2x3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> u : array<S, 3>;
+
+@group(0) @binding(1) var<storage, read_write> s : array<S, 4>;
+
+fn f() {
+  s[3].m[1] = u[2].m[0];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct S {
+  @size(64)
+  m : mat2x3<f16>,
+}
+
+struct S_std140 {
+  m_0 : vec3<f16>,
+  @size(56)
+  m_1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> u : array<S_std140, 3u>;
+
+@group(0) @binding(1) var<storage, read_write> s : array<S, 4>;
+
+fn f() {
+  s[3].m[1] = u[2u].m_0;
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, ArrayStructMatUniform_CopyColumnSwizzle_UniformToWorkgroup_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct S {
+  @size(64)
+  m : mat2x3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> u : array<S, 4>;
+
+var<workgroup> w : array<S, 4>;
+
+fn f() {
+  w[3].m[1] = u[2].m[0].yzx.yzx;
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct S {
+  @size(64)
+  m : mat2x3<f16>,
+}
+
+struct S_std140 {
+  m_0 : vec3<f16>,
+  @size(56)
+  m_1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> u : array<S_std140, 4u>;
+
+var<workgroup> w : array<S, 4>;
+
+fn f() {
+  w[3].m[1] = u[2u].m_0.yzx.yzx;
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, ArrayStructMatUniform_CopyScalar_UniformToPrivate_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct S {
+  v : vec4<i32>,
+  @size(64)
+  m : mat2x3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> u : array<S, 3>;
+
+var<private> p : array<S, 4>;
+
+fn f() {
+  p[3].m[1].x = u[2].m[0].y;
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct S {
+  v : vec4<i32>,
+  @size(64)
+  m : mat2x3<f16>,
+}
+
+struct S_std140 {
+  v : vec4<i32>,
+  m_0 : vec3<f16>,
+  @size(56)
+  m_1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> u : array<S_std140, 3u>;
+
+var<private> p : array<S, 4>;
+
+fn f() {
+  p[3].m[1].x = u[2u].m_0[1u];
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, ArrayMatUniform_LoadArray_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<mat2x3<f16>, 3>;
+
+fn f() {
+  let l = a;
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<mat2x3_f16, 3u>;
+
+fn conv_mat2x3_f16(val : mat2x3_f16) -> mat2x3<f16> {
+  return mat2x3<f16>(val.col0, val.col1);
+}
+
+fn conv_arr3_mat2x3_f16(val : array<mat2x3_f16, 3u>) -> array<mat2x3<f16>, 3u> {
+  var arr : array<mat2x3<f16>, 3u>;
+  for(var i : u32; (i < 3u); i = (i + 1)) {
+    arr[i] = conv_mat2x3_f16(val[i]);
+  }
+  return arr;
+}
+
+fn f() {
+  let l = conv_arr3_mat2x3_f16(a);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, ArrayMatUniform_LoadMatrix_ConstArrayIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<mat2x3<f16>, 3>;
+
+fn f() {
+  let l = a[2];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<mat2x3_f16, 3u>;
+
+fn conv_mat2x3_f16(val : mat2x3_f16) -> mat2x3<f16> {
+  return mat2x3<f16>(val.col0, val.col1);
+}
+
+fn f() {
+  let l = conv_mat2x3_f16(a[2u]);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, ArrayMatUniform_LoadMatrix_VariableArrayIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<mat2x3<f16>, 3>;
+
+fn f() {
+  let I = 1;
+  let l = a[I];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<mat2x3_f16, 3u>;
+
+fn conv_mat2x3_f16(val : mat2x3_f16) -> mat2x3<f16> {
+  return mat2x3<f16>(val.col0, val.col1);
+}
+
+fn f() {
+  let I = 1;
+  let l = conv_mat2x3_f16(a[I]);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, ArrayMatUniform_LoadColumn_ConstArrayIndex_ConstColumnIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<mat2x3<f16>, 3>;
+
+fn f() {
+  let l = a[2][1];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<mat2x3_f16, 3u>;
+
+fn f() {
+  let l = a[2u].col1;
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, ArrayMatUniform_LoadColumn_VariableArrayIndex_ConstColumnIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<mat2x3<f16>, 3>;
+
+fn f() {
+  let I = 1;
+  let l = a[I][1];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<mat2x3_f16, 3u>;
+
+fn f() {
+  let I = 1;
+  let l = a[I].col1;
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, ArrayMatUniform_LoadColumn_ConstArrayIndex_VariableColumnIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<mat2x3<f16>, 3>;
+
+fn f() {
+  let I = 1;
+  let l = a[2][I];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<mat2x3_f16, 3u>;
+
+fn load_a_2_p0(p0 : u32) -> vec3<f16> {
+  switch(p0) {
+    case 0u: {
+      return a[2u].col0;
+    }
+    case 1u: {
+      return a[2u].col1;
+    }
+    default: {
+      return vec3<f16>();
+    }
+  }
+}
+
+fn f() {
+  let I = 1;
+  let l = load_a_2_p0(u32(I));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16,
+       ArrayMatUniform_LoadColumn_VariableArrayIndex_VariableColumnIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<mat2x3<f16>, 3>;
+
+fn f() {
+  let I = 1;
+  let l = a[I][I];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<mat2x3_f16, 3u>;
+
+fn load_a_p0_p1(p0 : u32, p1 : u32) -> vec3<f16> {
+  switch(p1) {
+    case 0u: {
+      return a[p0].col0;
+    }
+    case 1u: {
+      return a[p0].col1;
+    }
+    default: {
+      return vec3<f16>();
+    }
+  }
+}
+
+fn f() {
+  let I = 1;
+  let l = load_a_p0_p1(u32(I), u32(I));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, StructArrayMatUniform_LoadStruct_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct S {
+  a : array<mat2x3<f16>, 3>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+  let l = s;
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+struct S {
+  a : array<mat2x3<f16>, 3>,
+}
+
+struct S_std140 {
+  a : array<mat2x3_f16, 3u>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn conv_mat2x3_f16(val : mat2x3_f16) -> mat2x3<f16> {
+  return mat2x3<f16>(val.col0, val.col1);
+}
+
+fn conv_arr3_mat2x3_f16(val : array<mat2x3_f16, 3u>) -> array<mat2x3<f16>, 3u> {
+  var arr : array<mat2x3<f16>, 3u>;
+  for(var i : u32; (i < 3u); i = (i + 1)) {
+    arr[i] = conv_mat2x3_f16(val[i]);
+  }
+  return arr;
+}
+
+fn conv_S(val : S_std140) -> S {
+  return S(conv_arr3_mat2x3_f16(val.a));
+}
+
+fn f() {
+  let l = conv_S(s);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, StructArrayMatUniform_LoadArray_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct S {
+  a : array<mat2x3<f16>, 3>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+  let l = s.a;
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+struct S {
+  a : array<mat2x3<f16>, 3>,
+}
+
+struct S_std140 {
+  a : array<mat2x3_f16, 3u>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn conv_mat2x3_f16(val : mat2x3_f16) -> mat2x3<f16> {
+  return mat2x3<f16>(val.col0, val.col1);
+}
+
+fn conv_arr3_mat2x3_f16(val : array<mat2x3_f16, 3u>) -> array<mat2x3<f16>, 3u> {
+  var arr : array<mat2x3<f16>, 3u>;
+  for(var i : u32; (i < 3u); i = (i + 1)) {
+    arr[i] = conv_mat2x3_f16(val[i]);
+  }
+  return arr;
+}
+
+fn f() {
+  let l = conv_arr3_mat2x3_f16(s.a);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, StructArrayMatUniform_LoadMatrix_ConstArrayIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct S {
+  a : array<mat2x3<f16>, 3>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+  let l = s.a[2];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+struct S {
+  a : array<mat2x3<f16>, 3>,
+}
+
+struct S_std140 {
+  a : array<mat2x3_f16, 3u>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn conv_mat2x3_f16(val : mat2x3_f16) -> mat2x3<f16> {
+  return mat2x3<f16>(val.col0, val.col1);
+}
+
+fn f() {
+  let l = conv_mat2x3_f16(s.a[2u]);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, StructArrayMatUniform_LoadMatrix_VariableArrayIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct S {
+  a : array<mat2x3<f16>, 3>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+  let I = 1;
+  let l = s.a[I];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+struct S {
+  a : array<mat2x3<f16>, 3>,
+}
+
+struct S_std140 {
+  a : array<mat2x3_f16, 3u>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn conv_mat2x3_f16(val : mat2x3_f16) -> mat2x3<f16> {
+  return mat2x3<f16>(val.col0, val.col1);
+}
+
+fn f() {
+  let I = 1;
+  let l = conv_mat2x3_f16(s.a[I]);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16,
+       StructArrayMatUniform_LoadColumn_ConstArrayIndex_ConstColumnIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct S {
+  a : array<mat2x3<f16>, 3>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+  let l = s.a[2][1];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+struct S {
+  a : array<mat2x3<f16>, 3>,
+}
+
+struct S_std140 {
+  a : array<mat2x3_f16, 3u>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn f() {
+  let l = s.a[2u].col1;
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16,
+       StructArrayMatUniform_LoadColumn_VariableArrayIndex_ConstColumnIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct S {
+  a : array<mat2x3<f16>, 3>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+  let I = 1;
+  let l = s.a[I][1];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+struct S {
+  a : array<mat2x3<f16>, 3>,
+}
+
+struct S_std140 {
+  a : array<mat2x3_f16, 3u>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn f() {
+  let I = 1;
+  let l = s.a[I].col1;
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16,
+       StructArrayMatUniform_LoadColumn_ConstArrayIndex_VariableColumnIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct S {
+  a : array<mat2x3<f16>, 3>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+  let I = 1;
+  let l = s.a[2][I];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+struct S {
+  a : array<mat2x3<f16>, 3>,
+}
+
+struct S_std140 {
+  a : array<mat2x3_f16, 3u>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn load_s_a_2_p0(p0 : u32) -> vec3<f16> {
+  switch(p0) {
+    case 0u: {
+      return s.a[2u].col0;
+    }
+    case 1u: {
+      return s.a[2u].col1;
+    }
+    default: {
+      return vec3<f16>();
+    }
+  }
+}
+
+fn f() {
+  let I = 1;
+  let l = load_s_a_2_p0(u32(I));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16,
+       StructArrayMatUniform_LoadColumn_VariableArrayIndex_VariableColumnIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+struct S {
+  a : array<mat2x3<f16>, 3>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+  let I = 1;
+  let l = s.a[I][I];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+struct S {
+  a : array<mat2x3<f16>, 3>,
+}
+
+struct S_std140 {
+  a : array<mat2x3_f16, 3u>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn load_s_a_p0_p1(p0 : u32, p1 : u32) -> vec3<f16> {
+  switch(p1) {
+    case 0u: {
+      return s.a[p0].col0;
+    }
+    case 1u: {
+      return s.a[p0].col1;
+    }
+    default: {
+      return vec3<f16>();
+    }
+  }
+}
+
+fn f() {
+  let I = 1;
+  let l = load_s_a_p0_p1(u32(I), u32(I));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, ArrayArrayMatUniform_LoadArrays_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x3<f16>, 3>, 4>;
+
+fn f() {
+  let l = a;
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x3_f16, 3u>, 4u>;
+
+fn conv_mat2x3_f16(val : mat2x3_f16) -> mat2x3<f16> {
+  return mat2x3<f16>(val.col0, val.col1);
+}
+
+fn conv_arr3_mat2x3_f16(val : array<mat2x3_f16, 3u>) -> array<mat2x3<f16>, 3u> {
+  var arr : array<mat2x3<f16>, 3u>;
+  for(var i : u32; (i < 3u); i = (i + 1)) {
+    arr[i] = conv_mat2x3_f16(val[i]);
+  }
+  return arr;
+}
+
+fn conv_arr4_arr3_mat2x3_f16(val : array<array<mat2x3_f16, 3u>, 4u>) -> array<array<mat2x3<f16>, 3u>, 4u> {
+  var arr : array<array<mat2x3<f16>, 3u>, 4u>;
+  for(var i : u32; (i < 4u); i = (i + 1)) {
+    arr[i] = conv_arr3_mat2x3_f16(val[i]);
+  }
+  return arr;
+}
+
+fn f() {
+  let l = conv_arr4_arr3_mat2x3_f16(a);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, ArrayArrayMatUniform_LoadArray_ConstOuterArrayIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x3<f16>, 3>, 4>;
+
+fn f() {
+  let l = a[3];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x3_f16, 3u>, 4u>;
+
+fn conv_mat2x3_f16(val : mat2x3_f16) -> mat2x3<f16> {
+  return mat2x3<f16>(val.col0, val.col1);
+}
+
+fn conv_arr3_mat2x3_f16(val : array<mat2x3_f16, 3u>) -> array<mat2x3<f16>, 3u> {
+  var arr : array<mat2x3<f16>, 3u>;
+  for(var i : u32; (i < 3u); i = (i + 1)) {
+    arr[i] = conv_mat2x3_f16(val[i]);
+  }
+  return arr;
+}
+
+fn f() {
+  let l = conv_arr3_mat2x3_f16(a[3u]);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16, ArrayArrayMatUniform_LoadArray_VariableOuterArrayIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x3<f16>, 3>, 4>;
+
+fn f() {
+  let I = 1;
+  let l = a[I];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x3_f16, 3u>, 4u>;
+
+fn conv_mat2x3_f16(val : mat2x3_f16) -> mat2x3<f16> {
+  return mat2x3<f16>(val.col0, val.col1);
+}
+
+fn conv_arr3_mat2x3_f16(val : array<mat2x3_f16, 3u>) -> array<mat2x3<f16>, 3u> {
+  var arr : array<mat2x3<f16>, 3u>;
+  for(var i : u32; (i < 3u); i = (i + 1)) {
+    arr[i] = conv_mat2x3_f16(val[i]);
+  }
+  return arr;
+}
+
+fn f() {
+  let I = 1;
+  let l = conv_arr3_mat2x3_f16(a[I]);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16,
+       ArrayArrayMatUniform_LoadMatrix_ConstOuterArrayIndex_ConstInnerArrayIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x3<f16>, 3>, 4>;
+
+fn f() {
+  let l = a[3][2];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x3_f16, 3u>, 4u>;
+
+fn conv_mat2x3_f16(val : mat2x3_f16) -> mat2x3<f16> {
+  return mat2x3<f16>(val.col0, val.col1);
+}
+
+fn f() {
+  let l = conv_mat2x3_f16(a[3u][2u]);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16,
+       ArrayArrayMatUniform_LoadMatrix_ConstOuterArrayIndex_VariableInnerArrayIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x3<f16>, 3>, 4>;
+
+fn f() {
+  let I = 1;
+  let l = a[3][I];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x3_f16, 3u>, 4u>;
+
+fn conv_mat2x3_f16(val : mat2x3_f16) -> mat2x3<f16> {
+  return mat2x3<f16>(val.col0, val.col1);
+}
+
+fn f() {
+  let I = 1;
+  let l = conv_mat2x3_f16(a[3u][I]);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16,
+       ArrayArrayMatUniform_LoadMatrix_VariableOuterArrayIndex_ConstInnerArrayIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x3<f16>, 3>, 4>;
+
+fn f() {
+  let I = 1;
+  let l = a[I][2];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x3_f16, 3u>, 4u>;
+
+fn conv_mat2x3_f16(val : mat2x3_f16) -> mat2x3<f16> {
+  return mat2x3<f16>(val.col0, val.col1);
+}
+
+fn f() {
+  let I = 1;
+  let l = conv_mat2x3_f16(a[I][2u]);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F16,
+       ArrayArrayMatUniform_LoadMatrix_VariableOuterArrayIndex_VariableInnerArrayIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x3<f16>, 3>, 4>;
+
+fn f() {
+  let I = 1;
+  let l = a[I][I];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x3_f16, 3u>, 4u>;
+
+fn conv_mat2x3_f16(val : mat2x3_f16) -> mat2x3<f16> {
+  return mat2x3<f16>(val.col0, val.col1);
+}
+
+fn f() {
+  let I = 1;
+  let l = conv_mat2x3_f16(a[I][I]);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(
+    Std140Test_F16,
+    ArrayArrayMatUniform_LoadColumn_ConstOuterArrayIndex_ConstInnerArrayIndex_ConstColumnIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x3<f16>, 3>, 4>;
+
+fn f() {
+  let l = a[3][2][1];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x3_f16, 3u>, 4u>;
+
+fn f() {
+  let l = a[3u][2u].col1;
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(
+    Std140Test_F16,
+    ArrayArrayMatUniform_LoadColumn_ConstOuterArrayIndex_ConstInnerArrayIndex_VariableColumnIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x3<f16>, 3>, 4>;
+
+fn f() {
+  let I = 1;
+  let l = a[3][2][I];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x3_f16, 3u>, 4u>;
+
+fn load_a_3_2_p0(p0 : u32) -> vec3<f16> {
+  switch(p0) {
+    case 0u: {
+      return a[3u][2u].col0;
+    }
+    case 1u: {
+      return a[3u][2u].col1;
+    }
+    default: {
+      return vec3<f16>();
+    }
+  }
+}
+
+fn f() {
+  let I = 1;
+  let l = load_a_3_2_p0(u32(I));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(
+    Std140Test_F16,
+    ArrayArrayMatUniform_LoadColumn_ConstOuterArrayIndex_VariableInnerArrayIndex_ConstColumnIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x3<f16>, 3>, 4>;
+
+fn f() {
+  let I = 1;
+  let l = a[3][I][1];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x3_f16, 3u>, 4u>;
+
+fn f() {
+  let I = 1;
+  let l = a[3u][I].col1;
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(
+    Std140Test_F16,
+    ArrayArrayMatUniform_LoadColumn_ConstOuterArrayIndex_VariableInnerArrayIndex_VariableColumnIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x3<f16>, 3>, 4>;
+
+fn f() {
+  let I = 1;
+  let J = 2;
+  let l = a[3][I][J];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x3_f16, 3u>, 4u>;
+
+fn load_a_3_p0_p1(p0 : u32, p1 : u32) -> vec3<f16> {
+  switch(p1) {
+    case 0u: {
+      return a[3u][p0].col0;
+    }
+    case 1u: {
+      return a[3u][p0].col1;
+    }
+    default: {
+      return vec3<f16>();
+    }
+  }
+}
+
+fn f() {
+  let I = 1;
+  let J = 2;
+  let l = load_a_3_p0_p1(u32(I), u32(J));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(
+    Std140Test_F16,
+    ArrayArrayMatUniform_LoadColumn_VariableOuterArrayIndex_ConstInnerArrayIndex_ConstColumnIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x3<f16>, 3>, 4>;
+
+fn f() {
+  let I = 1;
+  let l = a[I][2][1];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x3_f16, 3u>, 4u>;
+
+fn f() {
+  let I = 1;
+  let l = a[I][2u].col1;
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(
+    Std140Test_F16,
+    ArrayArrayMatUniform_LoadColumn_VariableOuterArrayIndex_ConstInnerArrayIndex_VariableColumnIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x3<f16>, 3>, 4>;
+
+fn f() {
+  let I = 1;
+  let J = 2;
+  let l = a[I][2][J];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x3_f16, 3u>, 4u>;
+
+fn load_a_p0_2_p1(p0 : u32, p1 : u32) -> vec3<f16> {
+  switch(p1) {
+    case 0u: {
+      return a[p0][2u].col0;
+    }
+    case 1u: {
+      return a[p0][2u].col1;
+    }
+    default: {
+      return vec3<f16>();
+    }
+  }
+}
+
+fn f() {
+  let I = 1;
+  let J = 2;
+  let l = load_a_p0_2_p1(u32(I), u32(J));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(
+    Std140Test_F16,
+    ArrayArrayMatUniform_LoadColumn_VariableOuterArrayIndex_VariableInnerArrayIndex_ConstColumnIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x3<f16>, 3>, 4>;
+
+fn f() {
+  let I = 1;
+  let J = 2;
+  let l = a[I][J][1];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x3_f16, 3u>, 4u>;
+
+fn f() {
+  let I = 1;
+  let J = 2;
+  let l = a[I][J].col1;
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(
+    Std140Test_F16,
+    ArrayArrayMatUniform_LoadColumn_VariableOuterArrayIndex_VariableInnerArrayIndex_VariableColumnIndex_Mat2x3F16) {
+    auto* src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x3<f16>, 3>, 4>;
+
+fn f() {
+  let I = 0;
+  let J = 1;
+  let K = 2;
+  let l = a[I][J][K];
+}
+)";
+
+    auto* expect = R"(
+enable f16;
+
+struct mat2x3_f16 {
+  col0 : vec3<f16>,
+  col1 : vec3<f16>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x3_f16, 3u>, 4u>;
+
+fn load_a_p0_p1_p2(p0 : u32, p1 : u32, p2 : u32) -> vec3<f16> {
+  switch(p2) {
+    case 0u: {
+      return a[p0][p1].col0;
+    }
+    case 1u: {
+      return a[p0][p1].col1;
+    }
+    default: {
+      return vec3<f16>();
+    }
+  }
+}
+
+fn f() {
+  let I = 0;
+  let J = 1;
+  let K = 2;
+  let l = load_a_p0_p1_p2(u32(I), u32(J), u32(K));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+}  // namespace
+}  // namespace tint::transform
diff --git a/src/tint/transform/std140_f32_test.cc b/src/tint/transform/std140_f32_test.cc
new file mode 100644
index 0000000..b0bd467
--- /dev/null
+++ b/src/tint/transform/std140_f32_test.cc
@@ -0,0 +1,3359 @@
+// 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/std140.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "src/tint/transform/test_helper.h"
+#include "src/tint/utils/string.h"
+
+namespace tint::transform {
+namespace {
+
+using Std140Test_F32 = TransformTest;
+
+TEST_F(Std140Test_F32, StructMatricesUniform) {
+    auto* src = R"(
+struct S2x2F32 {
+  m : mat2x2<f32>,
+}
+struct S3x2F32 {
+  m : mat3x2<f32>,
+}
+struct S4x2F32 {
+  m : mat4x2<f32>,
+}
+struct S2x3F32 {
+  m : mat2x3<f32>,
+}
+struct S3x3F32 {
+  m : mat3x3<f32>,
+}
+struct S4x3F32 {
+  m : mat4x3<f32>,
+}
+struct S2x4F32 {
+  m : mat2x4<f32>,
+}
+struct S3x4F32 {
+  m : mat3x4<f32>,
+}
+struct S4x4F32 {
+  m : mat4x4<f32>,
+}
+
+@group(2) @binding(2) var<uniform> s2x2f32 : S2x2F32;
+@group(3) @binding(2) var<uniform> s3x2f32 : S3x2F32;
+@group(4) @binding(2) var<uniform> s4x2f32 : S4x2F32;
+@group(2) @binding(3) var<uniform> s2x3f32 : S2x3F32;
+@group(3) @binding(3) var<uniform> s3x3f32 : S3x3F32;
+@group(4) @binding(3) var<uniform> s4x3f32 : S4x3F32;
+@group(2) @binding(4) var<uniform> s2x4f32 : S2x4F32;
+@group(3) @binding(4) var<uniform> s3x4f32 : S3x4F32;
+@group(4) @binding(4) var<uniform> s4x4f32 : S4x4F32;
+)";
+
+    auto* expect = R"(
+struct S2x2F32 {
+  m : mat2x2<f32>,
+}
+
+struct S2x2F32_std140 {
+  m_0 : vec2<f32>,
+  m_1 : vec2<f32>,
+}
+
+struct S3x2F32 {
+  m : mat3x2<f32>,
+}
+
+struct S3x2F32_std140 {
+  m_0 : vec2<f32>,
+  m_1 : vec2<f32>,
+  m_2 : vec2<f32>,
+}
+
+struct S4x2F32 {
+  m : mat4x2<f32>,
+}
+
+struct S4x2F32_std140 {
+  m_0 : vec2<f32>,
+  m_1 : vec2<f32>,
+  m_2 : vec2<f32>,
+  m_3 : vec2<f32>,
+}
+
+struct S2x3F32 {
+  m : mat2x3<f32>,
+}
+
+struct S3x3F32 {
+  m : mat3x3<f32>,
+}
+
+struct S4x3F32 {
+  m : mat4x3<f32>,
+}
+
+struct S2x4F32 {
+  m : mat2x4<f32>,
+}
+
+struct S3x4F32 {
+  m : mat3x4<f32>,
+}
+
+struct S4x4F32 {
+  m : mat4x4<f32>,
+}
+
+@group(2) @binding(2) var<uniform> s2x2f32 : S2x2F32_std140;
+
+@group(3) @binding(2) var<uniform> s3x2f32 : S3x2F32_std140;
+
+@group(4) @binding(2) var<uniform> s4x2f32 : S4x2F32_std140;
+
+@group(2) @binding(3) var<uniform> s2x3f32 : S2x3F32;
+
+@group(3) @binding(3) var<uniform> s3x3f32 : S3x3F32;
+
+@group(4) @binding(3) var<uniform> s4x3f32 : S4x3F32;
+
+@group(2) @binding(4) var<uniform> s2x4f32 : S2x4F32;
+
+@group(3) @binding(4) var<uniform> s3x4f32 : S3x4F32;
+
+@group(4) @binding(4) var<uniform> s4x4f32 : S4x4F32;
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+// In the following tests we only test `mat2x2<f32>` for matrix used as array element type and
+// `mat3x2<f32>` otherwise, and set all constant column index to 1, row index 0, inner array index
+// 2, and outer array index 3. For exhaustive tests, i.e. tests on all matrix shape and different
+// valid constant index, please refer to std140_exhaustive_test.cc
+
+TEST_F(Std140Test_F32, SingleStructMatUniform_Mat3x2F32) {
+    auto* src = R"(
+struct S {
+  m : mat3x2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+)";
+
+    auto* expect = R"(
+struct S {
+  m : mat3x2<f32>,
+}
+
+struct S_std140 {
+  m_0 : vec2<f32>,
+  m_1 : vec2<f32>,
+  m_2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, CustomAlign_Mat3x2F32) {
+    auto* src = R"(
+struct S {
+  before : i32,
+  @align(128)
+  m : mat3x2<f32>,
+  after : i32,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+)";
+
+    auto* expect = R"(
+struct S {
+  before : i32,
+  @align(128)
+  m : mat3x2<f32>,
+  after : i32,
+}
+
+struct S_std140 {
+  before : i32,
+  @align(128i)
+  m_0 : vec2<f32>,
+  m_1 : vec2<f32>,
+  m_2 : vec2<f32>,
+  after : i32,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, CustomSizeMat_Mat3x2F32) {
+    auto* src = R"(
+struct S {
+  before : i32,
+  @size(128)
+  m : mat3x2<f32>,
+  after : i32,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+)";
+
+    auto* expect = R"(
+struct S {
+  before : i32,
+  @size(128)
+  m : mat3x2<f32>,
+  after : i32,
+}
+
+struct S_std140 {
+  before : i32,
+  m_0 : vec2<f32>,
+  m_1 : vec2<f32>,
+  @size(112)
+  m_2 : vec2<f32>,
+  after : i32,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, CustomAlignAndSize_Mat3x2F32) {
+    auto* src = R"(
+struct S {
+  before : i32,
+  @align(128) @size(128)
+  m : mat3x2<f32>,
+  after : i32,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+)";
+
+    auto* expect = R"(
+struct S {
+  before : i32,
+  @align(128) @size(128)
+  m : mat3x2<f32>,
+  after : i32,
+}
+
+struct S_std140 {
+  before : i32,
+  @align(128i)
+  m_0 : vec2<f32>,
+  m_1 : vec2<f32>,
+  @size(112)
+  m_2 : vec2<f32>,
+  after : i32,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, MatrixUsageInForLoop_Mat3x2F32) {
+    auto* src = R"(
+struct S {
+  m : mat3x2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+  for(var i = u32(s.m[0][0]); (i < u32(s.m[i][1])); i += u32(s.m[1][i])) {
+  }
+}
+)";
+
+    auto* expect = R"(
+struct S {
+  m : mat3x2<f32>,
+}
+
+struct S_std140 {
+  m_0 : vec2<f32>,
+  m_1 : vec2<f32>,
+  m_2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn load_s_m_p0_1(p0 : u32) -> f32 {
+  switch(p0) {
+    case 0u: {
+      return s.m_0[1u];
+    }
+    case 1u: {
+      return s.m_1[1u];
+    }
+    case 2u: {
+      return s.m_2[1u];
+    }
+    default: {
+      return f32();
+    }
+  }
+}
+
+fn f() {
+  for(var i = u32(s.m_0[0u]); (i < u32(load_s_m_p0_1(u32(i)))); i += u32(s.m_1[i])) {
+  }
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, MatUniform_LoadMatrix_Mat3x2F32) {
+    auto* src = R"(
+@group(0) @binding(0) var<uniform> m : mat3x2<f32>;
+
+fn f() {
+  let l = m;
+}
+)";
+
+    auto* expect = R"(
+struct mat3x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+  col2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> m : mat3x2_f32;
+
+fn conv_mat3x2_f32(val : mat3x2_f32) -> mat3x2<f32> {
+  return mat3x2<f32>(val.col0, val.col1, val.col2);
+}
+
+fn f() {
+  let l = conv_mat3x2_f32(m);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, MatUniform_LoadColumn_ConstIndex_Mat3x2F32) {
+    auto* src = R"(
+@group(0) @binding(0) var<uniform> a : mat3x2<f32>;
+
+fn f() {
+  let l = a[1];
+}
+)";
+
+    auto* expect = R"(
+struct mat3x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+  col2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : mat3x2_f32;
+
+fn f() {
+  let l = a.col1;
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, MatUniform_LoadColumn_VariableIndex_Mat3x2F32) {
+    auto* src = R"(
+@group(0) @binding(0) var<uniform> a : mat3x2<f32>;
+
+fn f() {
+  let I = 1;
+  let l = a[I];
+}
+)";
+
+    auto* expect = R"(
+struct mat3x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+  col2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : mat3x2_f32;
+
+fn load_a_p0(p0 : u32) -> vec2<f32> {
+  switch(p0) {
+    case 0u: {
+      return a.col0;
+    }
+    case 1u: {
+      return a.col1;
+    }
+    case 2u: {
+      return a.col2;
+    }
+    default: {
+      return vec2<f32>();
+    }
+  }
+}
+
+fn f() {
+  let I = 1;
+  let l = load_a_p0(u32(I));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, MatUniform_LoadColumnSwizzle_ConstIndex_Mat3x2F32) {
+    auto* src = R"(
+@group(0) @binding(0) var<uniform> a : mat3x2<f32>;
+
+fn f() {
+  let l = a[1].yx;
+}
+)";
+
+    auto* expect = R"(
+struct mat3x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+  col2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : mat3x2_f32;
+
+fn f() {
+  let l = a.col1.yx;
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, MatUniform_LoadColumnSwizzle_VariableIndex_Mat3x2F32) {
+    auto* src = R"(
+@group(0) @binding(0) var<uniform> a : mat3x2<f32>;
+
+fn f() {
+  let I = 1;
+  let l = a[I].yx;
+}
+)";
+
+    auto* expect = R"(
+struct mat3x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+  col2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : mat3x2_f32;
+
+fn load_a_p0_yx(p0 : u32) -> vec2<f32> {
+  switch(p0) {
+    case 0u: {
+      return a.col0.yx;
+    }
+    case 1u: {
+      return a.col1.yx;
+    }
+    case 2u: {
+      return a.col2.yx;
+    }
+    default: {
+      return vec2<f32>();
+    }
+  }
+}
+
+fn f() {
+  let I = 1;
+  let l = load_a_p0_yx(u32(I));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, MatUniform_LoadScalar_ConstColumnIndex_ConstRowIndex_Mat3x2F32) {
+    auto* src = R"(
+@group(0) @binding(0) var<uniform> a : mat3x2<f32>;
+
+fn f() {
+  let l = a[1][0];
+}
+)";
+
+    auto* expect = R"(
+struct mat3x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+  col2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : mat3x2_f32;
+
+fn f() {
+  let l = a.col1[0u];
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, MatUniform_LoadScalar_VariableColumnIndex_ConstRowIndex_Mat3x2F32) {
+    auto* src = R"(
+@group(0) @binding(0) var<uniform> a : mat3x2<f32>;
+
+fn f() {
+  let I = 0;
+  let l = a[I][0];
+}
+)";
+
+    auto* expect = R"(
+struct mat3x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+  col2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : mat3x2_f32;
+
+fn load_a_p0_0(p0 : u32) -> f32 {
+  switch(p0) {
+    case 0u: {
+      return a.col0[0u];
+    }
+    case 1u: {
+      return a.col1[0u];
+    }
+    case 2u: {
+      return a.col2[0u];
+    }
+    default: {
+      return f32();
+    }
+  }
+}
+
+fn f() {
+  let I = 0;
+  let l = load_a_p0_0(u32(I));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, MatUniform_LoadScalar_ConstColumnIndex_VariableRowIndex_Mat3x2F32) {
+    auto* src = R"(
+@group(0) @binding(0) var<uniform> a : mat3x2<f32>;
+
+fn f() {
+  let I = 0;
+  let l = a[1][I];
+}
+)";
+
+    auto* expect = R"(
+struct mat3x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+  col2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : mat3x2_f32;
+
+fn f() {
+  let I = 0;
+  let l = a.col1[I];
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, MatUniform_LoadScalar_VariableColumnIndex_VariableRowIndex_Mat3x2F32) {
+    auto* src = R"(
+@group(0) @binding(0) var<uniform> a : mat3x2<f32>;
+
+fn f() {
+  let I = 0;
+  let l = a[I][I];
+}
+)";
+
+    auto* expect = R"(
+struct mat3x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+  col2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : mat3x2_f32;
+
+fn load_a_p0_p1(p0 : u32, p1 : u32) -> f32 {
+  switch(p0) {
+    case 0u: {
+      return a.col0[p1];
+    }
+    case 1u: {
+      return a.col1[p1];
+    }
+    case 2u: {
+      return a.col2[p1];
+    }
+    default: {
+      return f32();
+    }
+  }
+}
+
+fn f() {
+  let I = 0;
+  let l = load_a_p0_p1(u32(I), u32(I));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, StructMatUniform_NameCollision_Mat3x2F32) {
+    auto* src = R"(
+struct S {
+  m_1 : i32,
+  m : mat3x2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+)";
+
+    auto* expect = R"(
+struct S {
+  m_1 : i32,
+  m : mat3x2<f32>,
+}
+
+struct S_std140 {
+  m_1 : i32,
+  m__0 : vec2<f32>,
+  m__1 : vec2<f32>,
+  m__2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, StructMatUniform_LoadStruct_Mat3x2F32) {
+    auto* src = R"(
+struct S {
+  m : mat3x2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+  let l = s;
+}
+)";
+
+    auto* expect = R"(
+struct S {
+  m : mat3x2<f32>,
+}
+
+struct S_std140 {
+  m_0 : vec2<f32>,
+  m_1 : vec2<f32>,
+  m_2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn conv_S(val : S_std140) -> S {
+  return S(mat3x2<f32>(val.m_0, val.m_1, val.m_2));
+}
+
+fn f() {
+  let l = conv_S(s);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, StructMatUniform_LoadMatrix_Mat3x2F32) {
+    auto* src = R"(
+struct S {
+  m : mat3x2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+  let l = s.m;
+}
+)";
+
+    auto* expect = R"(
+struct S {
+  m : mat3x2<f32>,
+}
+
+struct S_std140 {
+  m_0 : vec2<f32>,
+  m_1 : vec2<f32>,
+  m_2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn load_s_m() -> mat3x2<f32> {
+  let s = &(s);
+  return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
+}
+
+fn f() {
+  let l = load_s_m();
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, StructMatUniform_LoadColumn_ConstIndex_Mat3x2F32) {
+    auto* src = R"(
+struct S {
+  m : mat3x2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+  let l = s.m[1];
+}
+)";
+
+    auto* expect = R"(
+struct S {
+  m : mat3x2<f32>,
+}
+
+struct S_std140 {
+  m_0 : vec2<f32>,
+  m_1 : vec2<f32>,
+  m_2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn f() {
+  let l = s.m_1;
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, StructMatUniform_LoadColumn_VariableIndex_Mat3x2F32) {
+    auto* src = R"(
+struct S {
+  m : mat3x2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+  let I = 0;
+  let l = s.m[I];
+}
+)";
+
+    auto* expect = R"(
+struct S {
+  m : mat3x2<f32>,
+}
+
+struct S_std140 {
+  m_0 : vec2<f32>,
+  m_1 : vec2<f32>,
+  m_2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn load_s_m_p0(p0 : u32) -> vec2<f32> {
+  switch(p0) {
+    case 0u: {
+      return s.m_0;
+    }
+    case 1u: {
+      return s.m_1;
+    }
+    case 2u: {
+      return s.m_2;
+    }
+    default: {
+      return vec2<f32>();
+    }
+  }
+}
+
+fn f() {
+  let I = 0;
+  let l = load_s_m_p0(u32(I));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, StructMatUniform_LoadScalar_ConstColumnIndex_ConstRowIndex_Mat3x2F32) {
+    auto* src = R"(
+struct S {
+  m : mat3x2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+  let l = s.m[1][0];
+}
+)";
+
+    auto* expect = R"(
+struct S {
+  m : mat3x2<f32>,
+}
+
+struct S_std140 {
+  m_0 : vec2<f32>,
+  m_1 : vec2<f32>,
+  m_2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn f() {
+  let l = s.m_1[0u];
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, StructMatUniform_LoadScalar_VariableColumnIndex_ConstRowIndex_Mat3x2F32) {
+    auto* src = R"(
+struct S {
+  m : mat3x2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+  let I = 0;
+  let l = s.m[I][0];
+}
+)";
+
+    auto* expect = R"(
+struct S {
+  m : mat3x2<f32>,
+}
+
+struct S_std140 {
+  m_0 : vec2<f32>,
+  m_1 : vec2<f32>,
+  m_2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn load_s_m_p0_0(p0 : u32) -> f32 {
+  switch(p0) {
+    case 0u: {
+      return s.m_0[0u];
+    }
+    case 1u: {
+      return s.m_1[0u];
+    }
+    case 2u: {
+      return s.m_2[0u];
+    }
+    default: {
+      return f32();
+    }
+  }
+}
+
+fn f() {
+  let I = 0;
+  let l = load_s_m_p0_0(u32(I));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, StructMatUniform_LoadScalar_ConstColumnIndex_VariableRowIndex_Mat3x2F32) {
+    auto* src = R"(
+struct S {
+  m : mat3x2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+  let I = 0;
+  let l = s.m[1][I];
+}
+)";
+
+    auto* expect = R"(
+struct S {
+  m : mat3x2<f32>,
+}
+
+struct S_std140 {
+  m_0 : vec2<f32>,
+  m_1 : vec2<f32>,
+  m_2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn f() {
+  let I = 0;
+  let l = s.m_1[I];
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, StructMatUniform_LoadScalar_VariableColumnIndex_VariableRowIndex_Mat3x2F32) {
+    auto* src = R"(
+struct S {
+  m : mat3x2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+  let I = 0;
+  let l = s.m[I][I];
+}
+)";
+
+    auto* expect = R"(
+struct S {
+  m : mat3x2<f32>,
+}
+
+struct S_std140 {
+  m_0 : vec2<f32>,
+  m_1 : vec2<f32>,
+  m_2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn load_s_m_p0_p1(p0 : u32, p1 : u32) -> f32 {
+  switch(p0) {
+    case 0u: {
+      return s.m_0[p1];
+    }
+    case 1u: {
+      return s.m_1[p1];
+    }
+    case 2u: {
+      return s.m_2[p1];
+    }
+    default: {
+      return f32();
+    }
+  }
+}
+
+fn f() {
+  let I = 0;
+  let l = load_s_m_p0_p1(u32(I), u32(I));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, ArrayStructMatUniform_LoadArray_Mat3x2F32) {
+    auto* src = R"(
+struct S {
+  @size(64)
+  m : mat3x2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<S, 3>;
+
+fn f() {
+  let l = a;
+}
+)";
+
+    auto* expect = R"(
+struct S {
+  @size(64)
+  m : mat3x2<f32>,
+}
+
+struct S_std140 {
+  m_0 : vec2<f32>,
+  m_1 : vec2<f32>,
+  @size(48)
+  m_2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
+
+fn conv_S(val : S_std140) -> S {
+  return S(mat3x2<f32>(val.m_0, val.m_1, val.m_2));
+}
+
+fn conv_arr3_S(val : array<S_std140, 3u>) -> array<S, 3u> {
+  var arr : array<S, 3u>;
+  for(var i : u32; (i < 3u); i = (i + 1)) {
+    arr[i] = conv_S(val[i]);
+  }
+  return arr;
+}
+
+fn f() {
+  let l = conv_arr3_S(a);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, ArrayStructMatUniform_LoadStruct_ConstIndex_Mat3x2F32) {
+    auto* src = R"(
+struct S {
+  @size(64)
+  m : mat3x2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<S, 3>;
+
+fn f() {
+  let l = a[2];
+}
+)";
+
+    auto* expect = R"(
+struct S {
+  @size(64)
+  m : mat3x2<f32>,
+}
+
+struct S_std140 {
+  m_0 : vec2<f32>,
+  m_1 : vec2<f32>,
+  @size(48)
+  m_2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
+
+fn conv_S(val : S_std140) -> S {
+  return S(mat3x2<f32>(val.m_0, val.m_1, val.m_2));
+}
+
+fn f() {
+  let l = conv_S(a[2u]);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, ArrayStructMatUniform_LoadStruct_VariableIndex_Mat3x2F32) {
+    auto* src = R"(
+struct S {
+  @size(64)
+  m : mat3x2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<S, 3>;
+
+fn f() {
+  let I = 1;
+  let l = a[I];
+}
+)";
+
+    auto* expect = R"(
+struct S {
+  @size(64)
+  m : mat3x2<f32>,
+}
+
+struct S_std140 {
+  m_0 : vec2<f32>,
+  m_1 : vec2<f32>,
+  @size(48)
+  m_2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
+
+fn conv_S(val : S_std140) -> S {
+  return S(mat3x2<f32>(val.m_0, val.m_1, val.m_2));
+}
+
+fn f() {
+  let I = 1;
+  let l = conv_S(a[I]);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, ArrayStructMatUniform_LoadMatrix_ConstArrayIndex_Mat3x2F32) {
+    auto* src = R"(
+struct S {
+  @size(64)
+  m : mat3x2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<S, 3>;
+
+fn f() {
+  let l = a[2].m;
+}
+)";
+
+    auto* expect = R"(
+struct S {
+  @size(64)
+  m : mat3x2<f32>,
+}
+
+struct S_std140 {
+  m_0 : vec2<f32>,
+  m_1 : vec2<f32>,
+  @size(48)
+  m_2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
+
+fn load_a_2_m() -> mat3x2<f32> {
+  let s = &(a[2u]);
+  return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
+}
+
+fn f() {
+  let l = load_a_2_m();
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, ArrayStructMatUniform_LoadMatrix_VariableArrayIndex_Mat3x2F32) {
+    auto* src = R"(
+struct S {
+  @size(64)
+  m : mat3x2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<S, 3>;
+
+fn f() {
+  let I = 1;
+  let l = a[I].m;
+}
+)";
+
+    auto* expect = R"(
+struct S {
+  @size(64)
+  m : mat3x2<f32>,
+}
+
+struct S_std140 {
+  m_0 : vec2<f32>,
+  m_1 : vec2<f32>,
+  @size(48)
+  m_2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
+
+fn load_a_p0_m(p0 : u32) -> mat3x2<f32> {
+  let s = &(a[p0]);
+  return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
+}
+
+fn f() {
+  let I = 1;
+  let l = load_a_p0_m(u32(I));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32,
+       ArrayStructMatUniform_LoadColumn_ConstArrayIndex_ConstColumnIndex_Mat3x2F32) {
+    auto* src = R"(
+struct S {
+  @size(64)
+  m : mat3x2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<S, 3>;
+
+fn f() {
+  let l = a[2].m[1];
+}
+)";
+
+    auto* expect = R"(
+struct S {
+  @size(64)
+  m : mat3x2<f32>,
+}
+
+struct S_std140 {
+  m_0 : vec2<f32>,
+  m_1 : vec2<f32>,
+  @size(48)
+  m_2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
+
+fn f() {
+  let l = a[2u].m_1;
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32,
+       ArrayStructMatUniform_LoadColumn_VariableArrayIndex_ConstColumnIndex_Mat3x2F32) {
+    auto* src = R"(
+struct S {
+  @size(64)
+  m : mat3x2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<S, 3>;
+
+fn f() {
+  let I = 1;
+  let l = a[I].m[1];
+}
+)";
+
+    auto* expect = R"(
+struct S {
+  @size(64)
+  m : mat3x2<f32>,
+}
+
+struct S_std140 {
+  m_0 : vec2<f32>,
+  m_1 : vec2<f32>,
+  @size(48)
+  m_2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
+
+fn f() {
+  let I = 1;
+  let l = a[I].m_1;
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32,
+       ArrayStructMatUniform_LoadColumn_ConstArrayIndex_VariableColumnIndex_Mat3x2F32) {
+    auto* src = R"(
+struct S {
+  @size(64)
+  m : mat3x2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<S, 3>;
+
+fn f() {
+  let I = 1;
+  let l = a[2].m[I];
+}
+)";
+
+    auto* expect = R"(
+struct S {
+  @size(64)
+  m : mat3x2<f32>,
+}
+
+struct S_std140 {
+  m_0 : vec2<f32>,
+  m_1 : vec2<f32>,
+  @size(48)
+  m_2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
+
+fn load_a_2_m_p0(p0 : u32) -> vec2<f32> {
+  switch(p0) {
+    case 0u: {
+      return a[2u].m_0;
+    }
+    case 1u: {
+      return a[2u].m_1;
+    }
+    case 2u: {
+      return a[2u].m_2;
+    }
+    default: {
+      return vec2<f32>();
+    }
+  }
+}
+
+fn f() {
+  let I = 1;
+  let l = load_a_2_m_p0(u32(I));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32,
+       ArrayStructMatUniform_LoadColumn_VariableArrayIndex_VariableColumnIndex_Mat3x2F32) {
+    auto* src = R"(
+struct S {
+  @size(64)
+  m : mat3x2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<S, 3>;
+
+fn f() {
+  let I = 1;
+  let l = a[I].m[I];
+}
+)";
+
+    auto* expect = R"(
+struct S {
+  @size(64)
+  m : mat3x2<f32>,
+}
+
+struct S_std140 {
+  m_0 : vec2<f32>,
+  m_1 : vec2<f32>,
+  @size(48)
+  m_2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
+
+fn load_a_p0_m_p1(p0 : u32, p1 : u32) -> vec2<f32> {
+  switch(p1) {
+    case 0u: {
+      return a[p0].m_0;
+    }
+    case 1u: {
+      return a[p0].m_1;
+    }
+    case 2u: {
+      return a[p0].m_2;
+    }
+    default: {
+      return vec2<f32>();
+    }
+  }
+}
+
+fn f() {
+  let I = 1;
+  let l = load_a_p0_m_p1(u32(I), u32(I));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, ArrayStructArrayStructMatUniform_Loads_Mat3x2F32) {
+    auto* src = R"(
+struct Inner {
+  @size(64)
+  m : mat3x2<f32>,
+}
+
+struct Outer {
+  a : array<Inner, 4>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<Outer, 4>;
+
+fn f() {
+  let I = 1;
+  let J = 2;
+  let K = 0;
+  let l_a : array<Outer, 4> = a;
+  let l_a_1 : Outer = a[1];
+  let l_a_I : Outer = a[I];
+  let l_a_2_a : array<Inner, 4> = a[2].a;
+  let l_a_I_a : array<Inner, 4> = a[I].a;
+  let l_a_3_a_1 : Inner = a[3].a[1];
+  let l_a_3_a_I : Inner = a[3].a[I];
+  let l_a_I_a_1 : Inner = a[I].a[1];
+  let l_a_I_a_J : Inner = a[I].a[J];
+  let l_a_0_a_2_m : mat3x2<f32> = a[0].a[2].m;
+  let l_a_0_a_I_m : mat3x2<f32> = a[0].a[I].m;
+  let l_a_I_a_2_m : mat3x2<f32> = a[I].a[2].m;
+  let l_a_I_a_J_m : mat3x2<f32> = a[I].a[J].m;
+  let l_a_1_a_3_m_0 : vec2<f32> = a[1].a[3].m[0];
+  let l_a_I_a_J_m_K : vec2<f32> = a[I].a[J].m[K];
+  let l_a_2_a_0_m_1_0 : f32 = a[2].a[0].m[1][0];
+  let l_a_I_a_J_m_K_I : f32 = a[I].a[J].m[K][I];
+}
+)";
+
+    auto* expect = R"(
+struct Inner {
+  @size(64)
+  m : mat3x2<f32>,
+}
+
+struct Inner_std140 {
+  m_0 : vec2<f32>,
+  m_1 : vec2<f32>,
+  @size(48)
+  m_2 : vec2<f32>,
+}
+
+struct Outer {
+  a : array<Inner, 4>,
+}
+
+struct Outer_std140 {
+  a : array<Inner_std140, 4u>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<Outer_std140, 4u>;
+
+fn conv_Inner(val : Inner_std140) -> Inner {
+  return Inner(mat3x2<f32>(val.m_0, val.m_1, val.m_2));
+}
+
+fn conv_arr4_Inner(val : array<Inner_std140, 4u>) -> array<Inner, 4u> {
+  var arr : array<Inner, 4u>;
+  for(var i : u32; (i < 4u); i = (i + 1)) {
+    arr[i] = conv_Inner(val[i]);
+  }
+  return arr;
+}
+
+fn conv_Outer(val : Outer_std140) -> Outer {
+  return Outer(conv_arr4_Inner(val.a));
+}
+
+fn conv_arr4_Outer(val : array<Outer_std140, 4u>) -> array<Outer, 4u> {
+  var arr : array<Outer, 4u>;
+  for(var i : u32; (i < 4u); i = (i + 1)) {
+    arr[i] = conv_Outer(val[i]);
+  }
+  return arr;
+}
+
+fn load_a_0_a_2_m() -> mat3x2<f32> {
+  let s = &(a[0u].a[2u]);
+  return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
+}
+
+fn load_a_0_a_p0_m(p0 : u32) -> mat3x2<f32> {
+  let s = &(a[0u].a[p0]);
+  return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
+}
+
+fn load_a_p0_a_2_m(p0 : u32) -> mat3x2<f32> {
+  let s = &(a[p0].a[2u]);
+  return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
+}
+
+fn load_a_p0_a_p1_m(p0 : u32, p1 : u32) -> mat3x2<f32> {
+  let s = &(a[p0].a[p1]);
+  return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
+}
+
+fn load_a_p0_a_p1_m_p2(p0 : u32, p1 : u32, p2 : u32) -> vec2<f32> {
+  switch(p2) {
+    case 0u: {
+      return a[p0].a[p1].m_0;
+    }
+    case 1u: {
+      return a[p0].a[p1].m_1;
+    }
+    case 2u: {
+      return a[p0].a[p1].m_2;
+    }
+    default: {
+      return vec2<f32>();
+    }
+  }
+}
+
+fn load_a_p0_a_p1_m_p2_p3(p0 : u32, p1 : u32, p2 : u32, p3 : u32) -> f32 {
+  switch(p2) {
+    case 0u: {
+      return a[p0].a[p1].m_0[p3];
+    }
+    case 1u: {
+      return a[p0].a[p1].m_1[p3];
+    }
+    case 2u: {
+      return a[p0].a[p1].m_2[p3];
+    }
+    default: {
+      return f32();
+    }
+  }
+}
+
+fn f() {
+  let I = 1;
+  let J = 2;
+  let K = 0;
+  let l_a : array<Outer, 4> = conv_arr4_Outer(a);
+  let l_a_1 : Outer = conv_Outer(a[1u]);
+  let l_a_I : Outer = conv_Outer(a[I]);
+  let l_a_2_a : array<Inner, 4> = conv_arr4_Inner(a[2u].a);
+  let l_a_I_a : array<Inner, 4> = conv_arr4_Inner(a[I].a);
+  let l_a_3_a_1 : Inner = conv_Inner(a[3u].a[1u]);
+  let l_a_3_a_I : Inner = conv_Inner(a[3u].a[I]);
+  let l_a_I_a_1 : Inner = conv_Inner(a[I].a[1u]);
+  let l_a_I_a_J : Inner = conv_Inner(a[I].a[J]);
+  let l_a_0_a_2_m : mat3x2<f32> = load_a_0_a_2_m();
+  let l_a_0_a_I_m : mat3x2<f32> = load_a_0_a_p0_m(u32(I));
+  let l_a_I_a_2_m : mat3x2<f32> = load_a_p0_a_2_m(u32(I));
+  let l_a_I_a_J_m : mat3x2<f32> = load_a_p0_a_p1_m(u32(I), u32(J));
+  let l_a_1_a_3_m_0 : vec2<f32> = a[1u].a[3u].m_0;
+  let l_a_I_a_J_m_K : vec2<f32> = load_a_p0_a_p1_m_p2(u32(I), u32(J), u32(K));
+  let l_a_2_a_0_m_1_0 : f32 = a[2u].a[0u].m_1[0u];
+  let l_a_I_a_J_m_K_I : f32 = load_a_p0_a_p1_m_p2_p3(u32(I), u32(J), u32(K), u32(I));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, ArrayStructArrayStructMatUniform_LoadsViaPtrs_Mat3x2F32) {
+    auto* src = R"(
+struct Inner {
+  @size(64)
+  m : mat3x2<f32>,
+}
+
+struct Outer {
+  a : array<Inner, 4>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<Outer, 4>;
+
+fn f() {
+  let I = 1;
+  let J = 2;
+  let K = 0;
+  let p_a = &(a);
+  let p_a_3 = &((*(p_a))[3]);
+  let p_a_I = &((*(p_a))[I]);
+  let p_a_3_a = &((*(p_a_3)).a);
+  let p_a_I_a = &((*(p_a_I)).a);
+  let p_a_3_a_2 = &((*(p_a_3_a))[2]);
+  let p_a_3_a_I = &((*(p_a_3_a))[I]);
+  let p_a_I_a_2 = &((*(p_a_I_a))[2]);
+  let p_a_I_a_J = &((*(p_a_I_a))[J]);
+  let p_a_3_a_2_m = &((*(p_a_3_a_2)).m);
+  let p_a_3_a_I_m = &((*(p_a_3_a_I)).m);
+  let p_a_I_a_2_m = &((*(p_a_I_a_2)).m);
+  let p_a_I_a_J_m = &((*(p_a_I_a_J)).m);
+  let p_a_3_a_2_m_1 = &((*(p_a_3_a_2_m))[1]);
+  let p_a_I_a_J_m_K = &((*(p_a_I_a_J_m))[K]);
+  let l_a : array<Outer, 4> = *(p_a);
+  let l_a_3 : Outer = *(p_a_3);
+  let l_a_I : Outer = *(p_a_I);
+  let l_a_3_a : array<Inner, 4> = *(p_a_3_a);
+  let l_a_I_a : array<Inner, 4> = *(p_a_I_a);
+  let l_a_3_a_2 : Inner = *(p_a_3_a_2);
+  let l_a_3_a_I : Inner = *(p_a_3_a_I);
+  let l_a_I_a_2 : Inner = *(p_a_I_a_2);
+  let l_a_I_a_J : Inner = *(p_a_I_a_J);
+  let l_a_3_a_2_m : mat3x2<f32> = *(p_a_3_a_2_m);
+  let l_a_3_a_I_m : mat3x2<f32> = *(p_a_3_a_I_m);
+  let l_a_I_a_2_m : mat3x2<f32> = *(p_a_I_a_2_m);
+  let l_a_I_a_J_m : mat3x2<f32> = *(p_a_I_a_J_m);
+  let l_a_3_a_2_m_1 : vec2<f32> = *(p_a_3_a_2_m_1);
+  let l_a_I_a_J_m_K : vec2<f32> = *(p_a_I_a_J_m_K);
+  let l_a_2_a_0_m_1_0 : f32 = (*(p_a_3_a_2_m_1))[0];
+  let l_a_I_a_J_m_K_I : f32 = (*(p_a_I_a_J_m_K))[I];
+}
+)";
+
+    auto* expect = R"(
+struct Inner {
+  @size(64)
+  m : mat3x2<f32>,
+}
+
+struct Inner_std140 {
+  m_0 : vec2<f32>,
+  m_1 : vec2<f32>,
+  @size(48)
+  m_2 : vec2<f32>,
+}
+
+struct Outer {
+  a : array<Inner, 4>,
+}
+
+struct Outer_std140 {
+  a : array<Inner_std140, 4u>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<Outer_std140, 4u>;
+
+fn conv_Inner(val : Inner_std140) -> Inner {
+  return Inner(mat3x2<f32>(val.m_0, val.m_1, val.m_2));
+}
+
+fn conv_arr4_Inner(val : array<Inner_std140, 4u>) -> array<Inner, 4u> {
+  var arr : array<Inner, 4u>;
+  for(var i : u32; (i < 4u); i = (i + 1)) {
+    arr[i] = conv_Inner(val[i]);
+  }
+  return arr;
+}
+
+fn conv_Outer(val : Outer_std140) -> Outer {
+  return Outer(conv_arr4_Inner(val.a));
+}
+
+fn conv_arr4_Outer(val : array<Outer_std140, 4u>) -> array<Outer, 4u> {
+  var arr : array<Outer, 4u>;
+  for(var i : u32; (i < 4u); i = (i + 1)) {
+    arr[i] = conv_Outer(val[i]);
+  }
+  return arr;
+}
+
+fn load_a_3_a_2_m() -> mat3x2<f32> {
+  let s = &(a[3u].a[2u]);
+  return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
+}
+
+fn load_a_3_a_p0_m(p0 : u32) -> mat3x2<f32> {
+  let s = &(a[3u].a[p0]);
+  return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
+}
+
+fn load_a_p0_a_2_m(p0 : u32) -> mat3x2<f32> {
+  let s = &(a[p0].a[2u]);
+  return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
+}
+
+fn load_a_p0_a_p1_m(p0 : u32, p1 : u32) -> mat3x2<f32> {
+  let s = &(a[p0].a[p1]);
+  return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
+}
+
+fn load_a_p0_a_p1_m_p2(p0 : u32, p1 : u32, p2 : u32) -> vec2<f32> {
+  switch(p2) {
+    case 0u: {
+      return a[p0].a[p1].m_0;
+    }
+    case 1u: {
+      return a[p0].a[p1].m_1;
+    }
+    case 2u: {
+      return a[p0].a[p1].m_2;
+    }
+    default: {
+      return vec2<f32>();
+    }
+  }
+}
+
+fn load_a_p0_a_p1_m_p2_p3(p0 : u32, p1 : u32, p2 : u32, p3 : u32) -> f32 {
+  switch(p2) {
+    case 0u: {
+      return a[p0].a[p1].m_0[p3];
+    }
+    case 1u: {
+      return a[p0].a[p1].m_1[p3];
+    }
+    case 2u: {
+      return a[p0].a[p1].m_2[p3];
+    }
+    default: {
+      return f32();
+    }
+  }
+}
+
+fn f() {
+  let I = 1;
+  let J = 2;
+  let K = 0;
+  let p_a = conv_arr4_Outer(a);
+  let p_a_3 = conv_Outer(a[3u]);
+  let p_a_I = conv_Outer(a[I]);
+  let p_a_3_a = conv_arr4_Inner(a[3u].a);
+  let p_a_I_a = conv_arr4_Inner(a[I].a);
+  let p_a_3_a_2 = conv_Inner(a[3u].a[2u]);
+  let p_a_3_a_I = conv_Inner(a[3u].a[I]);
+  let p_a_I_a_2 = conv_Inner(a[I].a[2u]);
+  let p_a_I_a_J = conv_Inner(a[I].a[J]);
+  let p_a_3_a_2_m = load_a_3_a_2_m();
+  let p_a_3_a_I_m = load_a_3_a_p0_m(u32(I));
+  let p_a_I_a_2_m = load_a_p0_a_2_m(u32(I));
+  let p_a_I_a_J_m = load_a_p0_a_p1_m(u32(I), u32(J));
+  let p_a_3_a_2_m_1 = a[3u].a[2u].m_1;
+  let p_a_I_a_J_m_K = load_a_p0_a_p1_m_p2(u32(I), u32(J), u32(K));
+  let l_a : array<Outer, 4> = conv_arr4_Outer(a);
+  let l_a_3 : Outer = conv_Outer(a[3u]);
+  let l_a_I : Outer = conv_Outer(a[I]);
+  let l_a_3_a : array<Inner, 4> = conv_arr4_Inner(a[3u].a);
+  let l_a_I_a : array<Inner, 4> = conv_arr4_Inner(a[I].a);
+  let l_a_3_a_2 : Inner = conv_Inner(a[3u].a[2u]);
+  let l_a_3_a_I : Inner = conv_Inner(a[3u].a[I]);
+  let l_a_I_a_2 : Inner = conv_Inner(a[I].a[2u]);
+  let l_a_I_a_J : Inner = conv_Inner(a[I].a[J]);
+  let l_a_3_a_2_m : mat3x2<f32> = load_a_3_a_2_m();
+  let l_a_3_a_I_m : mat3x2<f32> = load_a_3_a_p0_m(u32(I));
+  let l_a_I_a_2_m : mat3x2<f32> = load_a_p0_a_2_m(u32(I));
+  let l_a_I_a_J_m : mat3x2<f32> = load_a_p0_a_p1_m(u32(I), u32(J));
+  let l_a_3_a_2_m_1 : vec2<f32> = a[3u].a[2u].m_1;
+  let l_a_I_a_J_m_K : vec2<f32> = load_a_p0_a_p1_m_p2(u32(I), u32(J), u32(K));
+  let l_a_2_a_0_m_1_0 : f32 = a[3u].a[2u].m_1[0u];
+  let l_a_I_a_J_m_K_I : f32 = load_a_p0_a_p1_m_p2_p3(u32(I), u32(J), u32(K), u32(I));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, ArrayStructMatUniform_CopyArray_UniformToStorage_Mat3x2F32) {
+    auto* src = R"(
+struct S {
+  @size(64)
+  m : mat3x2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> u : array<S, 4>;
+
+@group(0) @binding(1) var<storage, read_write> s : array<S, 4>;
+
+fn f() {
+  s = u;
+}
+)";
+
+    auto* expect = R"(
+struct S {
+  @size(64)
+  m : mat3x2<f32>,
+}
+
+struct S_std140 {
+  m_0 : vec2<f32>,
+  m_1 : vec2<f32>,
+  @size(48)
+  m_2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> u : array<S_std140, 4u>;
+
+@group(0) @binding(1) var<storage, read_write> s : array<S, 4>;
+
+fn conv_S(val : S_std140) -> S {
+  return S(mat3x2<f32>(val.m_0, val.m_1, val.m_2));
+}
+
+fn conv_arr4_S(val : array<S_std140, 4u>) -> array<S, 4u> {
+  var arr : array<S, 4u>;
+  for(var i : u32; (i < 4u); i = (i + 1)) {
+    arr[i] = conv_S(val[i]);
+  }
+  return arr;
+}
+
+fn f() {
+  s = conv_arr4_S(u);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, ArrayStructMatUniform_CopyStruct_UniformToWorkgroup_Mat3x2F32) {
+    auto* src = R"(
+struct S {
+  v : vec4<i32>,
+  @size(64)
+  m : mat3x2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> u : array<S, 4>;
+
+var<workgroup> w : array<S, 4>;
+
+fn f() {
+  w[0] = u[1];
+}
+)";
+
+    auto* expect = R"(
+struct S {
+  v : vec4<i32>,
+  @size(64)
+  m : mat3x2<f32>,
+}
+
+struct S_std140 {
+  v : vec4<i32>,
+  m_0 : vec2<f32>,
+  m_1 : vec2<f32>,
+  @size(48)
+  m_2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> u : array<S_std140, 4u>;
+
+var<workgroup> w : array<S, 4>;
+
+fn conv_S(val : S_std140) -> S {
+  return S(val.v, mat3x2<f32>(val.m_0, val.m_1, val.m_2));
+}
+
+fn f() {
+  w[0] = conv_S(u[1u]);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, ArrayStructMatUniform_CopyMatrix_UniformToPrivate_Mat3x2F32) {
+    auto* src = R"(
+struct S {
+  v : vec4<i32>,
+  @size(64)
+  m : mat3x2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> u : array<S, 3>;
+
+var<private> p : array<S, 4>;
+
+fn f() {
+  p[2].m = u[1].m;
+}
+)";
+
+    auto* expect = R"(
+struct S {
+  v : vec4<i32>,
+  @size(64)
+  m : mat3x2<f32>,
+}
+
+struct S_std140 {
+  v : vec4<i32>,
+  m_0 : vec2<f32>,
+  m_1 : vec2<f32>,
+  @size(48)
+  m_2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> u : array<S_std140, 3u>;
+
+var<private> p : array<S, 4>;
+
+fn load_u_1_m() -> mat3x2<f32> {
+  let s = &(u[1u]);
+  return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
+}
+
+fn f() {
+  p[2].m = load_u_1_m();
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, ArrayStructMatUniform_CopyColumn_UniformToStorage_Mat3x2F32) {
+    auto* src = R"(
+struct S {
+  @size(64)
+  m : mat3x2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> u : array<S, 3>;
+
+@group(0) @binding(1) var<storage, read_write> s : array<S, 4>;
+
+fn f() {
+  s[3].m[1] = u[2].m[0];
+}
+)";
+
+    auto* expect = R"(
+struct S {
+  @size(64)
+  m : mat3x2<f32>,
+}
+
+struct S_std140 {
+  m_0 : vec2<f32>,
+  m_1 : vec2<f32>,
+  @size(48)
+  m_2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> u : array<S_std140, 3u>;
+
+@group(0) @binding(1) var<storage, read_write> s : array<S, 4>;
+
+fn f() {
+  s[3].m[1] = u[2u].m_0;
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, ArrayStructMatUniform_CopyColumnSwizzle_UniformToWorkgroup_Mat3x2F32) {
+    auto* src = R"(
+struct S {
+  @size(64)
+  m : mat3x2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> u : array<S, 4>;
+
+var<workgroup> w : array<S, 4>;
+
+fn f() {
+  w[3].m[1] = u[2].m[0].yx.yx;
+}
+)";
+
+    auto* expect = R"(
+struct S {
+  @size(64)
+  m : mat3x2<f32>,
+}
+
+struct S_std140 {
+  m_0 : vec2<f32>,
+  m_1 : vec2<f32>,
+  @size(48)
+  m_2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> u : array<S_std140, 4u>;
+
+var<workgroup> w : array<S, 4>;
+
+fn f() {
+  w[3].m[1] = u[2u].m_0.yx.yx;
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, ArrayStructMatUniform_CopyScalar_UniformToPrivate_Mat3x2F32) {
+    auto* src = R"(
+struct S {
+  v : vec4<i32>,
+  @size(64)
+  m : mat3x2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> u : array<S, 3>;
+
+var<private> p : array<S, 4>;
+
+fn f() {
+  p[3].m[1].x = u[2].m[0].y;
+}
+)";
+
+    auto* expect = R"(
+struct S {
+  v : vec4<i32>,
+  @size(64)
+  m : mat3x2<f32>,
+}
+
+struct S_std140 {
+  v : vec4<i32>,
+  m_0 : vec2<f32>,
+  m_1 : vec2<f32>,
+  @size(48)
+  m_2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> u : array<S_std140, 3u>;
+
+var<private> p : array<S, 4>;
+
+fn f() {
+  p[3].m[1].x = u[2u].m_0[1u];
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, ArrayMatUniform_LoadArray_Mat2x2F32) {
+    auto* src = R"(
+@group(0) @binding(0) var<uniform> a : array<mat2x2<f32>, 3>;
+
+fn f() {
+  let l = a;
+}
+)";
+
+    auto* expect = R"(
+struct mat2x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<mat2x2_f32, 3u>;
+
+fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
+  return mat2x2<f32>(val.col0, val.col1);
+}
+
+fn conv_arr3_mat2x2_f32(val : array<mat2x2_f32, 3u>) -> array<mat2x2<f32>, 3u> {
+  var arr : array<mat2x2<f32>, 3u>;
+  for(var i : u32; (i < 3u); i = (i + 1)) {
+    arr[i] = conv_mat2x2_f32(val[i]);
+  }
+  return arr;
+}
+
+fn f() {
+  let l = conv_arr3_mat2x2_f32(a);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, ArrayMatUniform_LoadMatrix_ConstArrayIndex_Mat2x2F32) {
+    auto* src = R"(
+@group(0) @binding(0) var<uniform> a : array<mat2x2<f32>, 3>;
+
+fn f() {
+  let l = a[2];
+}
+)";
+
+    auto* expect = R"(
+struct mat2x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<mat2x2_f32, 3u>;
+
+fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
+  return mat2x2<f32>(val.col0, val.col1);
+}
+
+fn f() {
+  let l = conv_mat2x2_f32(a[2u]);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, ArrayMatUniform_LoadMatrix_VariableArrayIndex_Mat2x2F32) {
+    auto* src = R"(
+@group(0) @binding(0) var<uniform> a : array<mat2x2<f32>, 3>;
+
+fn f() {
+  let I = 1;
+  let l = a[I];
+}
+)";
+
+    auto* expect = R"(
+struct mat2x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<mat2x2_f32, 3u>;
+
+fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
+  return mat2x2<f32>(val.col0, val.col1);
+}
+
+fn f() {
+  let I = 1;
+  let l = conv_mat2x2_f32(a[I]);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, ArrayMatUniform_LoadColumn_ConstArrayIndex_ConstColumnIndex_Mat2x2F32) {
+    auto* src = R"(
+@group(0) @binding(0) var<uniform> a : array<mat2x2<f32>, 3>;
+
+fn f() {
+  let l = a[2][1];
+}
+)";
+
+    auto* expect = R"(
+struct mat2x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<mat2x2_f32, 3u>;
+
+fn f() {
+  let l = a[2u].col1;
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, ArrayMatUniform_LoadColumn_VariableArrayIndex_ConstColumnIndex_Mat2x2F32) {
+    auto* src = R"(
+@group(0) @binding(0) var<uniform> a : array<mat2x2<f32>, 3>;
+
+fn f() {
+  let I = 1;
+  let l = a[I][1];
+}
+)";
+
+    auto* expect = R"(
+struct mat2x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<mat2x2_f32, 3u>;
+
+fn f() {
+  let I = 1;
+  let l = a[I].col1;
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, ArrayMatUniform_LoadColumn_ConstArrayIndex_VariableColumnIndex_Mat2x2F32) {
+    auto* src = R"(
+@group(0) @binding(0) var<uniform> a : array<mat2x2<f32>, 3>;
+
+fn f() {
+  let I = 1;
+  let l = a[2][I];
+}
+)";
+
+    auto* expect = R"(
+struct mat2x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<mat2x2_f32, 3u>;
+
+fn load_a_2_p0(p0 : u32) -> vec2<f32> {
+  switch(p0) {
+    case 0u: {
+      return a[2u].col0;
+    }
+    case 1u: {
+      return a[2u].col1;
+    }
+    default: {
+      return vec2<f32>();
+    }
+  }
+}
+
+fn f() {
+  let I = 1;
+  let l = load_a_2_p0(u32(I));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32,
+       ArrayMatUniform_LoadColumn_VariableArrayIndex_VariableColumnIndex_Mat2x2F32) {
+    auto* src = R"(
+@group(0) @binding(0) var<uniform> a : array<mat2x2<f32>, 3>;
+
+fn f() {
+  let I = 1;
+  let l = a[I][I];
+}
+)";
+
+    auto* expect = R"(
+struct mat2x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<mat2x2_f32, 3u>;
+
+fn load_a_p0_p1(p0 : u32, p1 : u32) -> vec2<f32> {
+  switch(p1) {
+    case 0u: {
+      return a[p0].col0;
+    }
+    case 1u: {
+      return a[p0].col1;
+    }
+    default: {
+      return vec2<f32>();
+    }
+  }
+}
+
+fn f() {
+  let I = 1;
+  let l = load_a_p0_p1(u32(I), u32(I));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, StructArrayMatUniform_LoadStruct_Mat2x2F32) {
+    auto* src = R"(
+struct S {
+  a : array<mat2x2<f32>, 3>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+  let l = s;
+}
+)";
+
+    auto* expect = R"(
+struct mat2x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+}
+
+struct S {
+  a : array<mat2x2<f32>, 3>,
+}
+
+struct S_std140 {
+  a : array<mat2x2_f32, 3u>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
+  return mat2x2<f32>(val.col0, val.col1);
+}
+
+fn conv_arr3_mat2x2_f32(val : array<mat2x2_f32, 3u>) -> array<mat2x2<f32>, 3u> {
+  var arr : array<mat2x2<f32>, 3u>;
+  for(var i : u32; (i < 3u); i = (i + 1)) {
+    arr[i] = conv_mat2x2_f32(val[i]);
+  }
+  return arr;
+}
+
+fn conv_S(val : S_std140) -> S {
+  return S(conv_arr3_mat2x2_f32(val.a));
+}
+
+fn f() {
+  let l = conv_S(s);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, StructArrayMatUniform_LoadArray_Mat2x2F32) {
+    auto* src = R"(
+struct S {
+  a : array<mat2x2<f32>, 3>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+  let l = s.a;
+}
+)";
+
+    auto* expect = R"(
+struct mat2x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+}
+
+struct S {
+  a : array<mat2x2<f32>, 3>,
+}
+
+struct S_std140 {
+  a : array<mat2x2_f32, 3u>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
+  return mat2x2<f32>(val.col0, val.col1);
+}
+
+fn conv_arr3_mat2x2_f32(val : array<mat2x2_f32, 3u>) -> array<mat2x2<f32>, 3u> {
+  var arr : array<mat2x2<f32>, 3u>;
+  for(var i : u32; (i < 3u); i = (i + 1)) {
+    arr[i] = conv_mat2x2_f32(val[i]);
+  }
+  return arr;
+}
+
+fn f() {
+  let l = conv_arr3_mat2x2_f32(s.a);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, StructArrayMatUniform_LoadMatrix_ConstArrayIndex_Mat2x2F32) {
+    auto* src = R"(
+struct S {
+  a : array<mat2x2<f32>, 3>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+  let l = s.a[2];
+}
+)";
+
+    auto* expect = R"(
+struct mat2x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+}
+
+struct S {
+  a : array<mat2x2<f32>, 3>,
+}
+
+struct S_std140 {
+  a : array<mat2x2_f32, 3u>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
+  return mat2x2<f32>(val.col0, val.col1);
+}
+
+fn f() {
+  let l = conv_mat2x2_f32(s.a[2u]);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, StructArrayMatUniform_LoadMatrix_VariableArrayIndex_Mat2x2F32) {
+    auto* src = R"(
+struct S {
+  a : array<mat2x2<f32>, 3>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+  let I = 1;
+  let l = s.a[I];
+}
+)";
+
+    auto* expect = R"(
+struct mat2x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+}
+
+struct S {
+  a : array<mat2x2<f32>, 3>,
+}
+
+struct S_std140 {
+  a : array<mat2x2_f32, 3u>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
+  return mat2x2<f32>(val.col0, val.col1);
+}
+
+fn f() {
+  let I = 1;
+  let l = conv_mat2x2_f32(s.a[I]);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32,
+       StructArrayMatUniform_LoadColumn_ConstArrayIndex_ConstColumnIndex_Mat2x2F32) {
+    auto* src = R"(
+struct S {
+  a : array<mat2x2<f32>, 3>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+  let l = s.a[2][1];
+}
+)";
+
+    auto* expect = R"(
+struct mat2x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+}
+
+struct S {
+  a : array<mat2x2<f32>, 3>,
+}
+
+struct S_std140 {
+  a : array<mat2x2_f32, 3u>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn f() {
+  let l = s.a[2u].col1;
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32,
+       StructArrayMatUniform_LoadColumn_VariableArrayIndex_ConstColumnIndex_Mat2x2F32) {
+    auto* src = R"(
+struct S {
+  a : array<mat2x2<f32>, 3>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+  let I = 1;
+  let l = s.a[I][1];
+}
+)";
+
+    auto* expect = R"(
+struct mat2x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+}
+
+struct S {
+  a : array<mat2x2<f32>, 3>,
+}
+
+struct S_std140 {
+  a : array<mat2x2_f32, 3u>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn f() {
+  let I = 1;
+  let l = s.a[I].col1;
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32,
+       StructArrayMatUniform_LoadColumn_ConstArrayIndex_VariableColumnIndex_Mat2x2F32) {
+    auto* src = R"(
+struct S {
+  a : array<mat2x2<f32>, 3>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+  let I = 1;
+  let l = s.a[2][I];
+}
+)";
+
+    auto* expect = R"(
+struct mat2x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+}
+
+struct S {
+  a : array<mat2x2<f32>, 3>,
+}
+
+struct S_std140 {
+  a : array<mat2x2_f32, 3u>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn load_s_a_2_p0(p0 : u32) -> vec2<f32> {
+  switch(p0) {
+    case 0u: {
+      return s.a[2u].col0;
+    }
+    case 1u: {
+      return s.a[2u].col1;
+    }
+    default: {
+      return vec2<f32>();
+    }
+  }
+}
+
+fn f() {
+  let I = 1;
+  let l = load_s_a_2_p0(u32(I));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32,
+       StructArrayMatUniform_LoadColumn_VariableArrayIndex_VariableColumnIndex_Mat2x2F32) {
+    auto* src = R"(
+struct S {
+  a : array<mat2x2<f32>, 3>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+  let I = 1;
+  let l = s.a[I][I];
+}
+)";
+
+    auto* expect = R"(
+struct mat2x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+}
+
+struct S {
+  a : array<mat2x2<f32>, 3>,
+}
+
+struct S_std140 {
+  a : array<mat2x2_f32, 3u>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn load_s_a_p0_p1(p0 : u32, p1 : u32) -> vec2<f32> {
+  switch(p1) {
+    case 0u: {
+      return s.a[p0].col0;
+    }
+    case 1u: {
+      return s.a[p0].col1;
+    }
+    default: {
+      return vec2<f32>();
+    }
+  }
+}
+
+fn f() {
+  let I = 1;
+  let l = load_s_a_p0_p1(u32(I), u32(I));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, ArrayArrayMatUniform_LoadArrays_Mat2x2F32) {
+    auto* src = R"(
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
+
+fn f() {
+  let l = a;
+}
+)";
+
+    auto* expect = R"(
+struct mat2x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
+
+fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
+  return mat2x2<f32>(val.col0, val.col1);
+}
+
+fn conv_arr3_mat2x2_f32(val : array<mat2x2_f32, 3u>) -> array<mat2x2<f32>, 3u> {
+  var arr : array<mat2x2<f32>, 3u>;
+  for(var i : u32; (i < 3u); i = (i + 1)) {
+    arr[i] = conv_mat2x2_f32(val[i]);
+  }
+  return arr;
+}
+
+fn conv_arr4_arr3_mat2x2_f32(val : array<array<mat2x2_f32, 3u>, 4u>) -> array<array<mat2x2<f32>, 3u>, 4u> {
+  var arr : array<array<mat2x2<f32>, 3u>, 4u>;
+  for(var i : u32; (i < 4u); i = (i + 1)) {
+    arr[i] = conv_arr3_mat2x2_f32(val[i]);
+  }
+  return arr;
+}
+
+fn f() {
+  let l = conv_arr4_arr3_mat2x2_f32(a);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, ArrayArrayMatUniform_LoadArray_ConstOuterArrayIndex_Mat2x2F32) {
+    auto* src = R"(
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
+
+fn f() {
+  let l = a[3];
+}
+)";
+
+    auto* expect = R"(
+struct mat2x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
+
+fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
+  return mat2x2<f32>(val.col0, val.col1);
+}
+
+fn conv_arr3_mat2x2_f32(val : array<mat2x2_f32, 3u>) -> array<mat2x2<f32>, 3u> {
+  var arr : array<mat2x2<f32>, 3u>;
+  for(var i : u32; (i < 3u); i = (i + 1)) {
+    arr[i] = conv_mat2x2_f32(val[i]);
+  }
+  return arr;
+}
+
+fn f() {
+  let l = conv_arr3_mat2x2_f32(a[3u]);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, ArrayArrayMatUniform_LoadArray_VariableOuterArrayIndex_Mat2x2F32) {
+    auto* src = R"(
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
+
+fn f() {
+  let I = 1;
+  let l = a[I];
+}
+)";
+
+    auto* expect = R"(
+struct mat2x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
+
+fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
+  return mat2x2<f32>(val.col0, val.col1);
+}
+
+fn conv_arr3_mat2x2_f32(val : array<mat2x2_f32, 3u>) -> array<mat2x2<f32>, 3u> {
+  var arr : array<mat2x2<f32>, 3u>;
+  for(var i : u32; (i < 3u); i = (i + 1)) {
+    arr[i] = conv_mat2x2_f32(val[i]);
+  }
+  return arr;
+}
+
+fn f() {
+  let I = 1;
+  let l = conv_arr3_mat2x2_f32(a[I]);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32,
+       ArrayArrayMatUniform_LoadMatrix_ConstOuterArrayIndex_ConstInnerArrayIndex_Mat2x2F32) {
+    auto* src = R"(
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
+
+fn f() {
+  let l = a[3][2];
+}
+)";
+
+    auto* expect = R"(
+struct mat2x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
+
+fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
+  return mat2x2<f32>(val.col0, val.col1);
+}
+
+fn f() {
+  let l = conv_mat2x2_f32(a[3u][2u]);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32,
+       ArrayArrayMatUniform_LoadMatrix_ConstOuterArrayIndex_VariableInnerArrayIndex_Mat2x2F32) {
+    auto* src = R"(
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
+
+fn f() {
+  let I = 1;
+  let l = a[3][I];
+}
+)";
+
+    auto* expect = R"(
+struct mat2x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
+
+fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
+  return mat2x2<f32>(val.col0, val.col1);
+}
+
+fn f() {
+  let I = 1;
+  let l = conv_mat2x2_f32(a[3u][I]);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32,
+       ArrayArrayMatUniform_LoadMatrix_VariableOuterArrayIndex_ConstInnerArrayIndex_Mat2x2F32) {
+    auto* src = R"(
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
+
+fn f() {
+  let I = 1;
+  let l = a[I][2];
+}
+)";
+
+    auto* expect = R"(
+struct mat2x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
+
+fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
+  return mat2x2<f32>(val.col0, val.col1);
+}
+
+fn f() {
+  let I = 1;
+  let l = conv_mat2x2_f32(a[I][2u]);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32,
+       ArrayArrayMatUniform_LoadMatrix_VariableOuterArrayIndex_VariableInnerArrayIndex_Mat2x2F32) {
+    auto* src = R"(
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
+
+fn f() {
+  let I = 1;
+  let l = a[I][I];
+}
+)";
+
+    auto* expect = R"(
+struct mat2x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
+
+fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
+  return mat2x2<f32>(val.col0, val.col1);
+}
+
+fn f() {
+  let I = 1;
+  let l = conv_mat2x2_f32(a[I][I]);
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(
+    Std140Test_F32,
+    ArrayArrayMatUniform_LoadColumn_ConstOuterArrayIndex_ConstInnerArrayIndex_ConstColumnIndex_Mat2x2F32) {
+    auto* src = R"(
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
+
+fn f() {
+  let l = a[3][2][1];
+}
+)";
+
+    auto* expect = R"(
+struct mat2x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
+
+fn f() {
+  let l = a[3u][2u].col1;
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(
+    Std140Test_F32,
+    ArrayArrayMatUniform_LoadColumn_ConstOuterArrayIndex_ConstInnerArrayIndex_VariableColumnIndex_Mat2x2F32) {
+    auto* src = R"(
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
+
+fn f() {
+  let I = 1;
+  let l = a[3][2][I];
+}
+)";
+
+    auto* expect = R"(
+struct mat2x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
+
+fn load_a_3_2_p0(p0 : u32) -> vec2<f32> {
+  switch(p0) {
+    case 0u: {
+      return a[3u][2u].col0;
+    }
+    case 1u: {
+      return a[3u][2u].col1;
+    }
+    default: {
+      return vec2<f32>();
+    }
+  }
+}
+
+fn f() {
+  let I = 1;
+  let l = load_a_3_2_p0(u32(I));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(
+    Std140Test_F32,
+    ArrayArrayMatUniform_LoadColumn_ConstOuterArrayIndex_VariableInnerArrayIndex_ConstColumnIndex_Mat2x2F32) {
+    auto* src = R"(
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
+
+fn f() {
+  let I = 1;
+  let l = a[3][I][1];
+}
+)";
+
+    auto* expect = R"(
+struct mat2x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
+
+fn f() {
+  let I = 1;
+  let l = a[3u][I].col1;
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(
+    Std140Test_F32,
+    ArrayArrayMatUniform_LoadColumn_ConstOuterArrayIndex_VariableInnerArrayIndex_VariableColumnIndex_Mat2x2F32) {
+    auto* src = R"(
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
+
+fn f() {
+  let I = 1;
+  let J = 2;
+  let l = a[3][I][J];
+}
+)";
+
+    auto* expect = R"(
+struct mat2x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
+
+fn load_a_3_p0_p1(p0 : u32, p1 : u32) -> vec2<f32> {
+  switch(p1) {
+    case 0u: {
+      return a[3u][p0].col0;
+    }
+    case 1u: {
+      return a[3u][p0].col1;
+    }
+    default: {
+      return vec2<f32>();
+    }
+  }
+}
+
+fn f() {
+  let I = 1;
+  let J = 2;
+  let l = load_a_3_p0_p1(u32(I), u32(J));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(
+    Std140Test_F32,
+    ArrayArrayMatUniform_LoadColumn_VariableOuterArrayIndex_ConstInnerArrayIndex_ConstColumnIndex_Mat2x2F32) {
+    auto* src = R"(
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
+
+fn f() {
+  let I = 1;
+  let l = a[I][2][1];
+}
+)";
+
+    auto* expect = R"(
+struct mat2x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
+
+fn f() {
+  let I = 1;
+  let l = a[I][2u].col1;
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(
+    Std140Test_F32,
+    ArrayArrayMatUniform_LoadColumn_VariableOuterArrayIndex_ConstInnerArrayIndex_VariableColumnIndex_Mat2x2F32) {
+    auto* src = R"(
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
+
+fn f() {
+  let I = 1;
+  let J = 2;
+  let l = a[I][2][J];
+}
+)";
+
+    auto* expect = R"(
+struct mat2x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
+
+fn load_a_p0_2_p1(p0 : u32, p1 : u32) -> vec2<f32> {
+  switch(p1) {
+    case 0u: {
+      return a[p0][2u].col0;
+    }
+    case 1u: {
+      return a[p0][2u].col1;
+    }
+    default: {
+      return vec2<f32>();
+    }
+  }
+}
+
+fn f() {
+  let I = 1;
+  let J = 2;
+  let l = load_a_p0_2_p1(u32(I), u32(J));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(
+    Std140Test_F32,
+    ArrayArrayMatUniform_LoadColumn_VariableOuterArrayIndex_VariableInnerArrayIndex_ConstColumnIndex_Mat2x2F32) {
+    auto* src = R"(
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
+
+fn f() {
+  let I = 1;
+  let J = 2;
+  let l = a[I][J][1];
+}
+)";
+
+    auto* expect = R"(
+struct mat2x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
+
+fn f() {
+  let I = 1;
+  let J = 2;
+  let l = a[I][J].col1;
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(
+    Std140Test_F32,
+    ArrayArrayMatUniform_LoadColumn_VariableOuterArrayIndex_VariableInnerArrayIndex_VariableColumnIndex_Mat2x2F32) {
+    auto* src = R"(
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
+
+fn f() {
+  let I = 0;
+  let J = 1;
+  let K = 2;
+  let l = a[I][J][K];
+}
+)";
+
+    auto* expect = R"(
+struct mat2x2_f32 {
+  col0 : vec2<f32>,
+  col1 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
+
+fn load_a_p0_p1_p2(p0 : u32, p1 : u32, p2 : u32) -> vec2<f32> {
+  switch(p2) {
+    case 0u: {
+      return a[p0][p1].col0;
+    }
+    case 1u: {
+      return a[p0][p1].col1;
+    }
+    default: {
+      return vec2<f32>();
+    }
+  }
+}
+
+fn f() {
+  let I = 0;
+  let J = 1;
+  let K = 2;
+  let l = load_a_p0_p1_p2(u32(I), u32(J), u32(K));
+}
+)";
+
+    auto got = Run<Std140>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+}  // namespace
+}  // namespace tint::transform
diff --git a/src/tint/transform/std140_test.cc b/src/tint/transform/std140_test.cc
index 1ec2e09..73221bc 100644
--- a/src/tint/transform/std140_test.cc
+++ b/src/tint/transform/std140_test.cc
@@ -21,6 +21,12 @@
 #include "src/tint/transform/test_helper.h"
 #include "src/tint/utils/string.h"
 
+// This file contains the should-run tests and a trival empty module test for Std140 transform.
+// For testing transform results with clear readability, please refer to std140_f32_test.cc for f32
+// matricies and std140_f16_test.cc for f16 matricies. For exhaustive tests that run Std140
+// transform on all shape of both f32 and f16 matricies and loop on all valid literal index when
+// required, please refer to std140_exhaustive_test.cc.
+
 namespace tint::transform {
 namespace {
 
@@ -96,6 +102,8 @@
 
 TEST_P(Std140TestShouldRun, StructStorage) {
     std::string src = R"(
+enable f16;
+
 struct S {
   m : ${mat},
 }
@@ -110,6 +118,8 @@
 
 TEST_P(Std140TestShouldRun, StructUniform) {
     std::string src = R"(
+enable f16;
+
 struct S {
   m : ${mat},
 }
@@ -124,6 +134,8 @@
 
 TEST_P(Std140TestShouldRun, ArrayStorage) {
     std::string src = R"(
+enable f16;
+
 @group(0) @binding(0) var<storage> s : array<${mat}, 2>;
 )";
 
@@ -141,6 +153,8 @@
     }
 
     std::string src = R"(
+enable f16;
+
 @group(0) @binding(0) var<uniform> s : array<${mat}, 2>;
 )";
 
@@ -161,6 +175,15 @@
                              {4, 2, MatrixType::f32},
                              {4, 3, MatrixType::f32},
                              {4, 4, MatrixType::f32},
+                             {2, 2, MatrixType::f16},
+                             {2, 3, MatrixType::f16},
+                             {2, 4, MatrixType::f16},
+                             {3, 2, MatrixType::f16},
+                             {3, 3, MatrixType::f16},
+                             {3, 4, MatrixType::f16},
+                             {4, 2, MatrixType::f16},
+                             {4, 3, MatrixType::f16},
+                             {4, 4, MatrixType::f16},
                          }));
 
 TEST_F(Std140Test, EmptyModule) {
@@ -173,3336 +196,5 @@
     EXPECT_EQ(expect, str(got));
 }
 
-using Std140Test_F32 = Std140Test;
-
-TEST_F(Std140Test_F32, StructMatricesUniform) {
-    auto* src = R"(
-struct S2x2F32 {
-  m : mat2x2<f32>,
-}
-struct S3x2F32 {
-  m : mat3x2<f32>,
-}
-struct S4x2F32 {
-  m : mat4x2<f32>,
-}
-struct S2x3F32 {
-  m : mat2x3<f32>,
-}
-struct S3x3F32 {
-  m : mat3x3<f32>,
-}
-struct S4x3F32 {
-  m : mat4x3<f32>,
-}
-struct S2x4F32 {
-  m : mat2x4<f32>,
-}
-struct S3x4F32 {
-  m : mat3x4<f32>,
-}
-struct S4x4F32 {
-  m : mat4x4<f32>,
-}
-
-@group(2) @binding(2) var<uniform> s2x2f32 : S2x2F32;
-@group(3) @binding(2) var<uniform> s3x2f32 : S3x2F32;
-@group(4) @binding(2) var<uniform> s4x2f32 : S4x2F32;
-@group(2) @binding(3) var<uniform> s2x3f32 : S2x3F32;
-@group(3) @binding(3) var<uniform> s3x3f32 : S3x3F32;
-@group(4) @binding(3) var<uniform> s4x3f32 : S4x3F32;
-@group(2) @binding(4) var<uniform> s2x4f32 : S2x4F32;
-@group(3) @binding(4) var<uniform> s3x4f32 : S3x4F32;
-@group(4) @binding(4) var<uniform> s4x4f32 : S4x4F32;
-)";
-
-    auto* expect = R"(
-struct S2x2F32 {
-  m : mat2x2<f32>,
-}
-
-struct S2x2F32_std140 {
-  m_0 : vec2<f32>,
-  m_1 : vec2<f32>,
-}
-
-struct S3x2F32 {
-  m : mat3x2<f32>,
-}
-
-struct S3x2F32_std140 {
-  m_0 : vec2<f32>,
-  m_1 : vec2<f32>,
-  m_2 : vec2<f32>,
-}
-
-struct S4x2F32 {
-  m : mat4x2<f32>,
-}
-
-struct S4x2F32_std140 {
-  m_0 : vec2<f32>,
-  m_1 : vec2<f32>,
-  m_2 : vec2<f32>,
-  m_3 : vec2<f32>,
-}
-
-struct S2x3F32 {
-  m : mat2x3<f32>,
-}
-
-struct S3x3F32 {
-  m : mat3x3<f32>,
-}
-
-struct S4x3F32 {
-  m : mat4x3<f32>,
-}
-
-struct S2x4F32 {
-  m : mat2x4<f32>,
-}
-
-struct S3x4F32 {
-  m : mat3x4<f32>,
-}
-
-struct S4x4F32 {
-  m : mat4x4<f32>,
-}
-
-@group(2) @binding(2) var<uniform> s2x2f32 : S2x2F32_std140;
-
-@group(3) @binding(2) var<uniform> s3x2f32 : S3x2F32_std140;
-
-@group(4) @binding(2) var<uniform> s4x2f32 : S4x2F32_std140;
-
-@group(2) @binding(3) var<uniform> s2x3f32 : S2x3F32;
-
-@group(3) @binding(3) var<uniform> s3x3f32 : S3x3F32;
-
-@group(4) @binding(3) var<uniform> s4x3f32 : S4x3F32;
-
-@group(2) @binding(4) var<uniform> s2x4f32 : S2x4F32;
-
-@group(3) @binding(4) var<uniform> s3x4f32 : S3x4F32;
-
-@group(4) @binding(4) var<uniform> s4x4f32 : S4x4F32;
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-// In the following tests we only test `mat2x2<f32>` for matrix used as array element type and
-// `mat3x2<f32>` otherwise, and set all constant column index to 1, row index 0, inner array index
-// 2, and outer array index 3. For exhaustive tests, i.e. tests on all matrix shape and different
-// valid constant index, please refer to std140_exhaustive_test.cc
-
-TEST_F(Std140Test_F32, SingleStructMatUniform_Mat3x2F32) {
-    auto* src = R"(
-struct S {
-  m : mat3x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> s : S;
-)";
-
-    auto* expect = R"(
-struct S {
-  m : mat3x2<f32>,
-}
-
-struct S_std140 {
-  m_0 : vec2<f32>,
-  m_1 : vec2<f32>,
-  m_2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> s : S_std140;
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, CustomAlign_Mat3x2F32) {
-    auto* src = R"(
-struct S {
-  before : i32,
-  @align(128)
-  m : mat3x2<f32>,
-  after : i32,
-}
-
-@group(0) @binding(0) var<uniform> s : S;
-)";
-
-    auto* expect = R"(
-struct S {
-  before : i32,
-  @align(128)
-  m : mat3x2<f32>,
-  after : i32,
-}
-
-struct S_std140 {
-  before : i32,
-  @align(128i)
-  m_0 : vec2<f32>,
-  m_1 : vec2<f32>,
-  m_2 : vec2<f32>,
-  after : i32,
-}
-
-@group(0) @binding(0) var<uniform> s : S_std140;
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, CustomSizeMat_Mat3x2F32) {
-    auto* src = R"(
-struct S {
-  before : i32,
-  @size(128)
-  m : mat3x2<f32>,
-  after : i32,
-}
-
-@group(0) @binding(0) var<uniform> s : S;
-)";
-
-    auto* expect = R"(
-struct S {
-  before : i32,
-  @size(128)
-  m : mat3x2<f32>,
-  after : i32,
-}
-
-struct S_std140 {
-  before : i32,
-  m_0 : vec2<f32>,
-  m_1 : vec2<f32>,
-  @size(112)
-  m_2 : vec2<f32>,
-  after : i32,
-}
-
-@group(0) @binding(0) var<uniform> s : S_std140;
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, CustomAlignAndSize_Mat3x2F32) {
-    auto* src = R"(
-struct S {
-  before : i32,
-  @align(128) @size(128)
-  m : mat3x2<f32>,
-  after : i32,
-}
-
-@group(0) @binding(0) var<uniform> s : S;
-)";
-
-    auto* expect = R"(
-struct S {
-  before : i32,
-  @align(128) @size(128)
-  m : mat3x2<f32>,
-  after : i32,
-}
-
-struct S_std140 {
-  before : i32,
-  @align(128i)
-  m_0 : vec2<f32>,
-  m_1 : vec2<f32>,
-  @size(112)
-  m_2 : vec2<f32>,
-  after : i32,
-}
-
-@group(0) @binding(0) var<uniform> s : S_std140;
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, MatrixUsageInForLoop_Mat3x2F32) {
-    auto* src = R"(
-struct S {
-  m : mat3x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> s : S;
-
-fn f() {
-  for(var i = u32(s.m[0][0]); (i < u32(s.m[i][1])); i += u32(s.m[1][i])) {
-  }
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  m : mat3x2<f32>,
-}
-
-struct S_std140 {
-  m_0 : vec2<f32>,
-  m_1 : vec2<f32>,
-  m_2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> s : S_std140;
-
-fn load_s_m_p0_1(p0 : u32) -> f32 {
-  switch(p0) {
-    case 0u: {
-      return s.m_0[1u];
-    }
-    case 1u: {
-      return s.m_1[1u];
-    }
-    case 2u: {
-      return s.m_2[1u];
-    }
-    default: {
-      return f32();
-    }
-  }
-}
-
-fn f() {
-  for(var i = u32(s.m_0[0u]); (i < u32(load_s_m_p0_1(u32(i)))); i += u32(s.m_1[i])) {
-  }
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, MatUniform_LoadMatrix_Mat3x2F32) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> m : mat3x2<f32>;
-
-fn f() {
-  let l = m;
-}
-)";
-
-    auto* expect = R"(
-struct mat3x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-  col2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> m : mat3x2_f32;
-
-fn conv_mat3x2_f32(val : mat3x2_f32) -> mat3x2<f32> {
-  return mat3x2<f32>(val.col0, val.col1, val.col2);
-}
-
-fn f() {
-  let l = conv_mat3x2_f32(m);
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, MatUniform_LoadColumn_ConstIndex_Mat3x2F32) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> a : mat3x2<f32>;
-
-fn f() {
-  let l = a[1];
-}
-)";
-
-    auto* expect = R"(
-struct mat3x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-  col2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : mat3x2_f32;
-
-fn f() {
-  let l = a.col1;
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, MatUniform_LoadColumn_VariableIndex_Mat3x2F32) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> a : mat3x2<f32>;
-
-fn f() {
-  let I = 1;
-  let l = a[I];
-}
-)";
-
-    auto* expect = R"(
-struct mat3x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-  col2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : mat3x2_f32;
-
-fn load_a_p0(p0 : u32) -> vec2<f32> {
-  switch(p0) {
-    case 0u: {
-      return a.col0;
-    }
-    case 1u: {
-      return a.col1;
-    }
-    case 2u: {
-      return a.col2;
-    }
-    default: {
-      return vec2<f32>();
-    }
-  }
-}
-
-fn f() {
-  let I = 1;
-  let l = load_a_p0(u32(I));
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, MatUniform_LoadColumnSwizzle_ConstIndex_Mat3x2F32) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> a : mat3x2<f32>;
-
-fn f() {
-  let l = a[1].yx;
-}
-)";
-
-    auto* expect = R"(
-struct mat3x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-  col2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : mat3x2_f32;
-
-fn f() {
-  let l = a.col1.yx;
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, MatUniform_LoadColumnSwizzle_VariableIndex_Mat3x2F32) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> a : mat3x2<f32>;
-
-fn f() {
-  let I = 1;
-  let l = a[I].yx;
-}
-)";
-
-    auto* expect = R"(
-struct mat3x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-  col2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : mat3x2_f32;
-
-fn load_a_p0_yx(p0 : u32) -> vec2<f32> {
-  switch(p0) {
-    case 0u: {
-      return a.col0.yx;
-    }
-    case 1u: {
-      return a.col1.yx;
-    }
-    case 2u: {
-      return a.col2.yx;
-    }
-    default: {
-      return vec2<f32>();
-    }
-  }
-}
-
-fn f() {
-  let I = 1;
-  let l = load_a_p0_yx(u32(I));
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, MatUniform_LoadScalar_ConstColumnIndex_ConstRowIndex_Mat3x2F32) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> a : mat3x2<f32>;
-
-fn f() {
-  let l = a[1][0];
-}
-)";
-
-    auto* expect = R"(
-struct mat3x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-  col2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : mat3x2_f32;
-
-fn f() {
-  let l = a.col1[0u];
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, MatUniform_LoadScalar_VariableColumnIndex_ConstRowIndex_Mat3x2F32) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> a : mat3x2<f32>;
-
-fn f() {
-  let I = 0;
-  let l = a[I][0];
-}
-)";
-
-    auto* expect = R"(
-struct mat3x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-  col2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : mat3x2_f32;
-
-fn load_a_p0_0(p0 : u32) -> f32 {
-  switch(p0) {
-    case 0u: {
-      return a.col0[0u];
-    }
-    case 1u: {
-      return a.col1[0u];
-    }
-    case 2u: {
-      return a.col2[0u];
-    }
-    default: {
-      return f32();
-    }
-  }
-}
-
-fn f() {
-  let I = 0;
-  let l = load_a_p0_0(u32(I));
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, MatUniform_LoadScalar_ConstColumnIndex_VariableRowIndex_Mat3x2F32) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> a : mat3x2<f32>;
-
-fn f() {
-  let I = 0;
-  let l = a[1][I];
-}
-)";
-
-    auto* expect = R"(
-struct mat3x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-  col2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : mat3x2_f32;
-
-fn f() {
-  let I = 0;
-  let l = a.col1[I];
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, MatUniform_LoadScalar_VariableColumnIndex_VariableRowIndex_Mat3x2F32) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> a : mat3x2<f32>;
-
-fn f() {
-  let I = 0;
-  let l = a[I][I];
-}
-)";
-
-    auto* expect = R"(
-struct mat3x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-  col2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : mat3x2_f32;
-
-fn load_a_p0_p1(p0 : u32, p1 : u32) -> f32 {
-  switch(p0) {
-    case 0u: {
-      return a.col0[p1];
-    }
-    case 1u: {
-      return a.col1[p1];
-    }
-    case 2u: {
-      return a.col2[p1];
-    }
-    default: {
-      return f32();
-    }
-  }
-}
-
-fn f() {
-  let I = 0;
-  let l = load_a_p0_p1(u32(I), u32(I));
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, StructMatUniform_NameCollision_Mat3x2F32) {
-    auto* src = R"(
-struct S {
-  m_1 : i32,
-  m : mat3x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> s : S;
-)";
-
-    auto* expect = R"(
-struct S {
-  m_1 : i32,
-  m : mat3x2<f32>,
-}
-
-struct S_std140 {
-  m_1 : i32,
-  m__0 : vec2<f32>,
-  m__1 : vec2<f32>,
-  m__2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> s : S_std140;
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, StructMatUniform_LoadStruct_Mat3x2F32) {
-    auto* src = R"(
-struct S {
-  m : mat3x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> s : S;
-
-fn f() {
-  let l = s;
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  m : mat3x2<f32>,
-}
-
-struct S_std140 {
-  m_0 : vec2<f32>,
-  m_1 : vec2<f32>,
-  m_2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> s : S_std140;
-
-fn conv_S(val : S_std140) -> S {
-  return S(mat3x2<f32>(val.m_0, val.m_1, val.m_2));
-}
-
-fn f() {
-  let l = conv_S(s);
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, StructMatUniform_LoadMatrix_Mat3x2F32) {
-    auto* src = R"(
-struct S {
-  m : mat3x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> s : S;
-
-fn f() {
-  let l = s.m;
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  m : mat3x2<f32>,
-}
-
-struct S_std140 {
-  m_0 : vec2<f32>,
-  m_1 : vec2<f32>,
-  m_2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> s : S_std140;
-
-fn load_s_m() -> mat3x2<f32> {
-  let s = &(s);
-  return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
-}
-
-fn f() {
-  let l = load_s_m();
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, StructMatUniform_LoadColumn_ConstIndex_Mat3x2F32) {
-    auto* src = R"(
-struct S {
-  m : mat3x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> s : S;
-
-fn f() {
-  let l = s.m[1];
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  m : mat3x2<f32>,
-}
-
-struct S_std140 {
-  m_0 : vec2<f32>,
-  m_1 : vec2<f32>,
-  m_2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> s : S_std140;
-
-fn f() {
-  let l = s.m_1;
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, StructMatUniform_LoadColumn_VariableIndex_Mat3x2F32) {
-    auto* src = R"(
-struct S {
-  m : mat3x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> s : S;
-
-fn f() {
-  let I = 0;
-  let l = s.m[I];
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  m : mat3x2<f32>,
-}
-
-struct S_std140 {
-  m_0 : vec2<f32>,
-  m_1 : vec2<f32>,
-  m_2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> s : S_std140;
-
-fn load_s_m_p0(p0 : u32) -> vec2<f32> {
-  switch(p0) {
-    case 0u: {
-      return s.m_0;
-    }
-    case 1u: {
-      return s.m_1;
-    }
-    case 2u: {
-      return s.m_2;
-    }
-    default: {
-      return vec2<f32>();
-    }
-  }
-}
-
-fn f() {
-  let I = 0;
-  let l = load_s_m_p0(u32(I));
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, StructMatUniform_LoadScalar_ConstColumnIndex_ConstRowIndex_Mat3x2F32) {
-    auto* src = R"(
-struct S {
-  m : mat3x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> s : S;
-
-fn f() {
-  let l = s.m[1][0];
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  m : mat3x2<f32>,
-}
-
-struct S_std140 {
-  m_0 : vec2<f32>,
-  m_1 : vec2<f32>,
-  m_2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> s : S_std140;
-
-fn f() {
-  let l = s.m_1[0u];
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, StructMatUniform_LoadScalar_VariableColumnIndex_ConstRowIndex_Mat3x2F32) {
-    auto* src = R"(
-struct S {
-  m : mat3x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> s : S;
-
-fn f() {
-  let I = 0;
-  let l = s.m[I][0];
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  m : mat3x2<f32>,
-}
-
-struct S_std140 {
-  m_0 : vec2<f32>,
-  m_1 : vec2<f32>,
-  m_2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> s : S_std140;
-
-fn load_s_m_p0_0(p0 : u32) -> f32 {
-  switch(p0) {
-    case 0u: {
-      return s.m_0[0u];
-    }
-    case 1u: {
-      return s.m_1[0u];
-    }
-    case 2u: {
-      return s.m_2[0u];
-    }
-    default: {
-      return f32();
-    }
-  }
-}
-
-fn f() {
-  let I = 0;
-  let l = load_s_m_p0_0(u32(I));
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, StructMatUniform_LoadScalar_ConstColumnIndex_VariableRowIndex_Mat3x2F32) {
-    auto* src = R"(
-struct S {
-  m : mat3x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> s : S;
-
-fn f() {
-  let I = 0;
-  let l = s.m[1][I];
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  m : mat3x2<f32>,
-}
-
-struct S_std140 {
-  m_0 : vec2<f32>,
-  m_1 : vec2<f32>,
-  m_2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> s : S_std140;
-
-fn f() {
-  let I = 0;
-  let l = s.m_1[I];
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, StructMatUniform_LoadScalar_VariableColumnIndex_VariableRowIndex_Mat3x2F32) {
-    auto* src = R"(
-struct S {
-  m : mat3x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> s : S;
-
-fn f() {
-  let I = 0;
-  let l = s.m[I][I];
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  m : mat3x2<f32>,
-}
-
-struct S_std140 {
-  m_0 : vec2<f32>,
-  m_1 : vec2<f32>,
-  m_2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> s : S_std140;
-
-fn load_s_m_p0_p1(p0 : u32, p1 : u32) -> f32 {
-  switch(p0) {
-    case 0u: {
-      return s.m_0[p1];
-    }
-    case 1u: {
-      return s.m_1[p1];
-    }
-    case 2u: {
-      return s.m_2[p1];
-    }
-    default: {
-      return f32();
-    }
-  }
-}
-
-fn f() {
-  let I = 0;
-  let l = load_s_m_p0_p1(u32(I), u32(I));
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, ArrayStructMatUniform_LoadArray_Mat3x2F32) {
-    auto* src = R"(
-struct S {
-  @size(64)
-  m : mat3x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<S, 3>;
-
-fn f() {
-  let l = a;
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  @size(64)
-  m : mat3x2<f32>,
-}
-
-struct S_std140 {
-  m_0 : vec2<f32>,
-  m_1 : vec2<f32>,
-  @size(48)
-  m_2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
-
-fn conv_S(val : S_std140) -> S {
-  return S(mat3x2<f32>(val.m_0, val.m_1, val.m_2));
-}
-
-fn conv_arr3_S(val : array<S_std140, 3u>) -> array<S, 3u> {
-  var arr : array<S, 3u>;
-  for(var i : u32; (i < 3u); i = (i + 1)) {
-    arr[i] = conv_S(val[i]);
-  }
-  return arr;
-}
-
-fn f() {
-  let l = conv_arr3_S(a);
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, ArrayStructMatUniform_LoadStruct_ConstIndex_Mat3x2F32) {
-    auto* src = R"(
-struct S {
-  @size(64)
-  m : mat3x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<S, 3>;
-
-fn f() {
-  let l = a[2];
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  @size(64)
-  m : mat3x2<f32>,
-}
-
-struct S_std140 {
-  m_0 : vec2<f32>,
-  m_1 : vec2<f32>,
-  @size(48)
-  m_2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
-
-fn conv_S(val : S_std140) -> S {
-  return S(mat3x2<f32>(val.m_0, val.m_1, val.m_2));
-}
-
-fn f() {
-  let l = conv_S(a[2u]);
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, ArrayStructMatUniform_LoadStruct_VariableIndex_Mat3x2F32) {
-    auto* src = R"(
-struct S {
-  @size(64)
-  m : mat3x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<S, 3>;
-
-fn f() {
-  let I = 1;
-  let l = a[I];
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  @size(64)
-  m : mat3x2<f32>,
-}
-
-struct S_std140 {
-  m_0 : vec2<f32>,
-  m_1 : vec2<f32>,
-  @size(48)
-  m_2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
-
-fn conv_S(val : S_std140) -> S {
-  return S(mat3x2<f32>(val.m_0, val.m_1, val.m_2));
-}
-
-fn f() {
-  let I = 1;
-  let l = conv_S(a[I]);
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, ArrayStructMatUniform_LoadMatrix_ConstArrayIndex_Mat3x2F32) {
-    auto* src = R"(
-struct S {
-  @size(64)
-  m : mat3x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<S, 3>;
-
-fn f() {
-  let l = a[2].m;
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  @size(64)
-  m : mat3x2<f32>,
-}
-
-struct S_std140 {
-  m_0 : vec2<f32>,
-  m_1 : vec2<f32>,
-  @size(48)
-  m_2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
-
-fn load_a_2_m() -> mat3x2<f32> {
-  let s = &(a[2u]);
-  return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
-}
-
-fn f() {
-  let l = load_a_2_m();
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, ArrayStructMatUniform_LoadMatrix_VariableArrayIndex_Mat3x2F32) {
-    auto* src = R"(
-struct S {
-  @size(64)
-  m : mat3x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<S, 3>;
-
-fn f() {
-  let I = 1;
-  let l = a[I].m;
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  @size(64)
-  m : mat3x2<f32>,
-}
-
-struct S_std140 {
-  m_0 : vec2<f32>,
-  m_1 : vec2<f32>,
-  @size(48)
-  m_2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
-
-fn load_a_p0_m(p0 : u32) -> mat3x2<f32> {
-  let s = &(a[p0]);
-  return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
-}
-
-fn f() {
-  let I = 1;
-  let l = load_a_p0_m(u32(I));
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32,
-       ArrayStructMatUniform_LoadColumn_ConstArrayIndex_ConstColumnIndex_Mat3x2F32) {
-    auto* src = R"(
-struct S {
-  @size(64)
-  m : mat3x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<S, 3>;
-
-fn f() {
-  let l = a[2].m[1];
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  @size(64)
-  m : mat3x2<f32>,
-}
-
-struct S_std140 {
-  m_0 : vec2<f32>,
-  m_1 : vec2<f32>,
-  @size(48)
-  m_2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
-
-fn f() {
-  let l = a[2u].m_1;
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32,
-       ArrayStructMatUniform_LoadColumn_VariableArrayIndex_ConstColumnIndex_Mat3x2F32) {
-    auto* src = R"(
-struct S {
-  @size(64)
-  m : mat3x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<S, 3>;
-
-fn f() {
-  let I = 1;
-  let l = a[I].m[1];
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  @size(64)
-  m : mat3x2<f32>,
-}
-
-struct S_std140 {
-  m_0 : vec2<f32>,
-  m_1 : vec2<f32>,
-  @size(48)
-  m_2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
-
-fn f() {
-  let I = 1;
-  let l = a[I].m_1;
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32,
-       ArrayStructMatUniform_LoadColumn_ConstArrayIndex_VariableColumnIndex_Mat3x2F32) {
-    auto* src = R"(
-struct S {
-  @size(64)
-  m : mat3x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<S, 3>;
-
-fn f() {
-  let I = 1;
-  let l = a[2].m[I];
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  @size(64)
-  m : mat3x2<f32>,
-}
-
-struct S_std140 {
-  m_0 : vec2<f32>,
-  m_1 : vec2<f32>,
-  @size(48)
-  m_2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
-
-fn load_a_2_m_p0(p0 : u32) -> vec2<f32> {
-  switch(p0) {
-    case 0u: {
-      return a[2u].m_0;
-    }
-    case 1u: {
-      return a[2u].m_1;
-    }
-    case 2u: {
-      return a[2u].m_2;
-    }
-    default: {
-      return vec2<f32>();
-    }
-  }
-}
-
-fn f() {
-  let I = 1;
-  let l = load_a_2_m_p0(u32(I));
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32,
-       ArrayStructMatUniform_LoadColumn_VariableArrayIndex_VariableColumnIndex_Mat3x2F32) {
-    auto* src = R"(
-struct S {
-  @size(64)
-  m : mat3x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<S, 3>;
-
-fn f() {
-  let I = 1;
-  let l = a[I].m[I];
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  @size(64)
-  m : mat3x2<f32>,
-}
-
-struct S_std140 {
-  m_0 : vec2<f32>,
-  m_1 : vec2<f32>,
-  @size(48)
-  m_2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
-
-fn load_a_p0_m_p1(p0 : u32, p1 : u32) -> vec2<f32> {
-  switch(p1) {
-    case 0u: {
-      return a[p0].m_0;
-    }
-    case 1u: {
-      return a[p0].m_1;
-    }
-    case 2u: {
-      return a[p0].m_2;
-    }
-    default: {
-      return vec2<f32>();
-    }
-  }
-}
-
-fn f() {
-  let I = 1;
-  let l = load_a_p0_m_p1(u32(I), u32(I));
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, ArrayStructArrayStructMatUniform_Loads_Mat3x2F32) {
-    auto* src = R"(
-struct Inner {
-  @size(64)
-  m : mat3x2<f32>,
-}
-
-struct Outer {
-  a : array<Inner, 4>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<Outer, 4>;
-
-fn f() {
-  let I = 1;
-  let J = 2;
-  let K = 0;
-  let l_a : array<Outer, 4> = a;
-  let l_a_1 : Outer = a[1];
-  let l_a_I : Outer = a[I];
-  let l_a_2_a : array<Inner, 4> = a[2].a;
-  let l_a_I_a : array<Inner, 4> = a[I].a;
-  let l_a_3_a_1 : Inner = a[3].a[1];
-  let l_a_3_a_I : Inner = a[3].a[I];
-  let l_a_I_a_1 : Inner = a[I].a[1];
-  let l_a_I_a_J : Inner = a[I].a[J];
-  let l_a_0_a_2_m : mat3x2<f32> = a[0].a[2].m;
-  let l_a_0_a_I_m : mat3x2<f32> = a[0].a[I].m;
-  let l_a_I_a_2_m : mat3x2<f32> = a[I].a[2].m;
-  let l_a_I_a_J_m : mat3x2<f32> = a[I].a[J].m;
-  let l_a_1_a_3_m_0 : vec2<f32> = a[1].a[3].m[0];
-  let l_a_I_a_J_m_K : vec2<f32> = a[I].a[J].m[K];
-  let l_a_2_a_0_m_1_0 : f32 = a[2].a[0].m[1][0];
-  let l_a_I_a_J_m_K_I : f32 = a[I].a[J].m[K][I];
-}
-)";
-
-    auto* expect = R"(
-struct Inner {
-  @size(64)
-  m : mat3x2<f32>,
-}
-
-struct Inner_std140 {
-  m_0 : vec2<f32>,
-  m_1 : vec2<f32>,
-  @size(48)
-  m_2 : vec2<f32>,
-}
-
-struct Outer {
-  a : array<Inner, 4>,
-}
-
-struct Outer_std140 {
-  a : array<Inner_std140, 4u>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<Outer_std140, 4u>;
-
-fn conv_Inner(val : Inner_std140) -> Inner {
-  return Inner(mat3x2<f32>(val.m_0, val.m_1, val.m_2));
-}
-
-fn conv_arr4_Inner(val : array<Inner_std140, 4u>) -> array<Inner, 4u> {
-  var arr : array<Inner, 4u>;
-  for(var i : u32; (i < 4u); i = (i + 1)) {
-    arr[i] = conv_Inner(val[i]);
-  }
-  return arr;
-}
-
-fn conv_Outer(val : Outer_std140) -> Outer {
-  return Outer(conv_arr4_Inner(val.a));
-}
-
-fn conv_arr4_Outer(val : array<Outer_std140, 4u>) -> array<Outer, 4u> {
-  var arr : array<Outer, 4u>;
-  for(var i : u32; (i < 4u); i = (i + 1)) {
-    arr[i] = conv_Outer(val[i]);
-  }
-  return arr;
-}
-
-fn load_a_0_a_2_m() -> mat3x2<f32> {
-  let s = &(a[0u].a[2u]);
-  return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
-}
-
-fn load_a_0_a_p0_m(p0 : u32) -> mat3x2<f32> {
-  let s = &(a[0u].a[p0]);
-  return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
-}
-
-fn load_a_p0_a_2_m(p0 : u32) -> mat3x2<f32> {
-  let s = &(a[p0].a[2u]);
-  return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
-}
-
-fn load_a_p0_a_p1_m(p0 : u32, p1 : u32) -> mat3x2<f32> {
-  let s = &(a[p0].a[p1]);
-  return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
-}
-
-fn load_a_p0_a_p1_m_p2(p0 : u32, p1 : u32, p2 : u32) -> vec2<f32> {
-  switch(p2) {
-    case 0u: {
-      return a[p0].a[p1].m_0;
-    }
-    case 1u: {
-      return a[p0].a[p1].m_1;
-    }
-    case 2u: {
-      return a[p0].a[p1].m_2;
-    }
-    default: {
-      return vec2<f32>();
-    }
-  }
-}
-
-fn load_a_p0_a_p1_m_p2_p3(p0 : u32, p1 : u32, p2 : u32, p3 : u32) -> f32 {
-  switch(p2) {
-    case 0u: {
-      return a[p0].a[p1].m_0[p3];
-    }
-    case 1u: {
-      return a[p0].a[p1].m_1[p3];
-    }
-    case 2u: {
-      return a[p0].a[p1].m_2[p3];
-    }
-    default: {
-      return f32();
-    }
-  }
-}
-
-fn f() {
-  let I = 1;
-  let J = 2;
-  let K = 0;
-  let l_a : array<Outer, 4> = conv_arr4_Outer(a);
-  let l_a_1 : Outer = conv_Outer(a[1u]);
-  let l_a_I : Outer = conv_Outer(a[I]);
-  let l_a_2_a : array<Inner, 4> = conv_arr4_Inner(a[2u].a);
-  let l_a_I_a : array<Inner, 4> = conv_arr4_Inner(a[I].a);
-  let l_a_3_a_1 : Inner = conv_Inner(a[3u].a[1u]);
-  let l_a_3_a_I : Inner = conv_Inner(a[3u].a[I]);
-  let l_a_I_a_1 : Inner = conv_Inner(a[I].a[1u]);
-  let l_a_I_a_J : Inner = conv_Inner(a[I].a[J]);
-  let l_a_0_a_2_m : mat3x2<f32> = load_a_0_a_2_m();
-  let l_a_0_a_I_m : mat3x2<f32> = load_a_0_a_p0_m(u32(I));
-  let l_a_I_a_2_m : mat3x2<f32> = load_a_p0_a_2_m(u32(I));
-  let l_a_I_a_J_m : mat3x2<f32> = load_a_p0_a_p1_m(u32(I), u32(J));
-  let l_a_1_a_3_m_0 : vec2<f32> = a[1u].a[3u].m_0;
-  let l_a_I_a_J_m_K : vec2<f32> = load_a_p0_a_p1_m_p2(u32(I), u32(J), u32(K));
-  let l_a_2_a_0_m_1_0 : f32 = a[2u].a[0u].m_1[0u];
-  let l_a_I_a_J_m_K_I : f32 = load_a_p0_a_p1_m_p2_p3(u32(I), u32(J), u32(K), u32(I));
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, ArrayStructArrayStructMatUniform_LoadsViaPtrs_Mat3x2F32) {
-    auto* src = R"(
-struct Inner {
-  @size(64)
-  m : mat3x2<f32>,
-}
-
-struct Outer {
-  a : array<Inner, 4>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<Outer, 4>;
-
-fn f() {
-  let I = 1;
-  let J = 2;
-  let K = 0;
-  let p_a = &(a);
-  let p_a_3 = &((*(p_a))[3]);
-  let p_a_I = &((*(p_a))[I]);
-  let p_a_3_a = &((*(p_a_3)).a);
-  let p_a_I_a = &((*(p_a_I)).a);
-  let p_a_3_a_2 = &((*(p_a_3_a))[2]);
-  let p_a_3_a_I = &((*(p_a_3_a))[I]);
-  let p_a_I_a_2 = &((*(p_a_I_a))[2]);
-  let p_a_I_a_J = &((*(p_a_I_a))[J]);
-  let p_a_3_a_2_m = &((*(p_a_3_a_2)).m);
-  let p_a_3_a_I_m = &((*(p_a_3_a_I)).m);
-  let p_a_I_a_2_m = &((*(p_a_I_a_2)).m);
-  let p_a_I_a_J_m = &((*(p_a_I_a_J)).m);
-  let p_a_3_a_2_m_1 = &((*(p_a_3_a_2_m))[1]);
-  let p_a_I_a_J_m_K = &((*(p_a_I_a_J_m))[K]);
-  let l_a : array<Outer, 4> = *(p_a);
-  let l_a_3 : Outer = *(p_a_3);
-  let l_a_I : Outer = *(p_a_I);
-  let l_a_3_a : array<Inner, 4> = *(p_a_3_a);
-  let l_a_I_a : array<Inner, 4> = *(p_a_I_a);
-  let l_a_3_a_2 : Inner = *(p_a_3_a_2);
-  let l_a_3_a_I : Inner = *(p_a_3_a_I);
-  let l_a_I_a_2 : Inner = *(p_a_I_a_2);
-  let l_a_I_a_J : Inner = *(p_a_I_a_J);
-  let l_a_3_a_2_m : mat3x2<f32> = *(p_a_3_a_2_m);
-  let l_a_3_a_I_m : mat3x2<f32> = *(p_a_3_a_I_m);
-  let l_a_I_a_2_m : mat3x2<f32> = *(p_a_I_a_2_m);
-  let l_a_I_a_J_m : mat3x2<f32> = *(p_a_I_a_J_m);
-  let l_a_3_a_2_m_1 : vec2<f32> = *(p_a_3_a_2_m_1);
-  let l_a_I_a_J_m_K : vec2<f32> = *(p_a_I_a_J_m_K);
-  let l_a_2_a_0_m_1_0 : f32 = (*(p_a_3_a_2_m_1))[0];
-  let l_a_I_a_J_m_K_I : f32 = (*(p_a_I_a_J_m_K))[I];
-}
-)";
-
-    auto* expect = R"(
-struct Inner {
-  @size(64)
-  m : mat3x2<f32>,
-}
-
-struct Inner_std140 {
-  m_0 : vec2<f32>,
-  m_1 : vec2<f32>,
-  @size(48)
-  m_2 : vec2<f32>,
-}
-
-struct Outer {
-  a : array<Inner, 4>,
-}
-
-struct Outer_std140 {
-  a : array<Inner_std140, 4u>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<Outer_std140, 4u>;
-
-fn conv_Inner(val : Inner_std140) -> Inner {
-  return Inner(mat3x2<f32>(val.m_0, val.m_1, val.m_2));
-}
-
-fn conv_arr4_Inner(val : array<Inner_std140, 4u>) -> array<Inner, 4u> {
-  var arr : array<Inner, 4u>;
-  for(var i : u32; (i < 4u); i = (i + 1)) {
-    arr[i] = conv_Inner(val[i]);
-  }
-  return arr;
-}
-
-fn conv_Outer(val : Outer_std140) -> Outer {
-  return Outer(conv_arr4_Inner(val.a));
-}
-
-fn conv_arr4_Outer(val : array<Outer_std140, 4u>) -> array<Outer, 4u> {
-  var arr : array<Outer, 4u>;
-  for(var i : u32; (i < 4u); i = (i + 1)) {
-    arr[i] = conv_Outer(val[i]);
-  }
-  return arr;
-}
-
-fn load_a_3_a_2_m() -> mat3x2<f32> {
-  let s = &(a[3u].a[2u]);
-  return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
-}
-
-fn load_a_3_a_p0_m(p0 : u32) -> mat3x2<f32> {
-  let s = &(a[3u].a[p0]);
-  return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
-}
-
-fn load_a_p0_a_2_m(p0 : u32) -> mat3x2<f32> {
-  let s = &(a[p0].a[2u]);
-  return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
-}
-
-fn load_a_p0_a_p1_m(p0 : u32, p1 : u32) -> mat3x2<f32> {
-  let s = &(a[p0].a[p1]);
-  return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
-}
-
-fn load_a_p0_a_p1_m_p2(p0 : u32, p1 : u32, p2 : u32) -> vec2<f32> {
-  switch(p2) {
-    case 0u: {
-      return a[p0].a[p1].m_0;
-    }
-    case 1u: {
-      return a[p0].a[p1].m_1;
-    }
-    case 2u: {
-      return a[p0].a[p1].m_2;
-    }
-    default: {
-      return vec2<f32>();
-    }
-  }
-}
-
-fn load_a_p0_a_p1_m_p2_p3(p0 : u32, p1 : u32, p2 : u32, p3 : u32) -> f32 {
-  switch(p2) {
-    case 0u: {
-      return a[p0].a[p1].m_0[p3];
-    }
-    case 1u: {
-      return a[p0].a[p1].m_1[p3];
-    }
-    case 2u: {
-      return a[p0].a[p1].m_2[p3];
-    }
-    default: {
-      return f32();
-    }
-  }
-}
-
-fn f() {
-  let I = 1;
-  let J = 2;
-  let K = 0;
-  let p_a = conv_arr4_Outer(a);
-  let p_a_3 = conv_Outer(a[3u]);
-  let p_a_I = conv_Outer(a[I]);
-  let p_a_3_a = conv_arr4_Inner(a[3u].a);
-  let p_a_I_a = conv_arr4_Inner(a[I].a);
-  let p_a_3_a_2 = conv_Inner(a[3u].a[2u]);
-  let p_a_3_a_I = conv_Inner(a[3u].a[I]);
-  let p_a_I_a_2 = conv_Inner(a[I].a[2u]);
-  let p_a_I_a_J = conv_Inner(a[I].a[J]);
-  let p_a_3_a_2_m = load_a_3_a_2_m();
-  let p_a_3_a_I_m = load_a_3_a_p0_m(u32(I));
-  let p_a_I_a_2_m = load_a_p0_a_2_m(u32(I));
-  let p_a_I_a_J_m = load_a_p0_a_p1_m(u32(I), u32(J));
-  let p_a_3_a_2_m_1 = a[3u].a[2u].m_1;
-  let p_a_I_a_J_m_K = load_a_p0_a_p1_m_p2(u32(I), u32(J), u32(K));
-  let l_a : array<Outer, 4> = conv_arr4_Outer(a);
-  let l_a_3 : Outer = conv_Outer(a[3u]);
-  let l_a_I : Outer = conv_Outer(a[I]);
-  let l_a_3_a : array<Inner, 4> = conv_arr4_Inner(a[3u].a);
-  let l_a_I_a : array<Inner, 4> = conv_arr4_Inner(a[I].a);
-  let l_a_3_a_2 : Inner = conv_Inner(a[3u].a[2u]);
-  let l_a_3_a_I : Inner = conv_Inner(a[3u].a[I]);
-  let l_a_I_a_2 : Inner = conv_Inner(a[I].a[2u]);
-  let l_a_I_a_J : Inner = conv_Inner(a[I].a[J]);
-  let l_a_3_a_2_m : mat3x2<f32> = load_a_3_a_2_m();
-  let l_a_3_a_I_m : mat3x2<f32> = load_a_3_a_p0_m(u32(I));
-  let l_a_I_a_2_m : mat3x2<f32> = load_a_p0_a_2_m(u32(I));
-  let l_a_I_a_J_m : mat3x2<f32> = load_a_p0_a_p1_m(u32(I), u32(J));
-  let l_a_3_a_2_m_1 : vec2<f32> = a[3u].a[2u].m_1;
-  let l_a_I_a_J_m_K : vec2<f32> = load_a_p0_a_p1_m_p2(u32(I), u32(J), u32(K));
-  let l_a_2_a_0_m_1_0 : f32 = a[3u].a[2u].m_1[0u];
-  let l_a_I_a_J_m_K_I : f32 = load_a_p0_a_p1_m_p2_p3(u32(I), u32(J), u32(K), u32(I));
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, ArrayStructMatUniform_CopyArray_UniformToStorage_Mat3x2F32) {
-    auto* src = R"(
-struct S {
-  @size(64)
-  m : mat3x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> u : array<S, 4>;
-
-@group(0) @binding(1) var<storage, read_write> s : array<S, 4>;
-
-fn f() {
-  s = u;
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  @size(64)
-  m : mat3x2<f32>,
-}
-
-struct S_std140 {
-  m_0 : vec2<f32>,
-  m_1 : vec2<f32>,
-  @size(48)
-  m_2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> u : array<S_std140, 4u>;
-
-@group(0) @binding(1) var<storage, read_write> s : array<S, 4>;
-
-fn conv_S(val : S_std140) -> S {
-  return S(mat3x2<f32>(val.m_0, val.m_1, val.m_2));
-}
-
-fn conv_arr4_S(val : array<S_std140, 4u>) -> array<S, 4u> {
-  var arr : array<S, 4u>;
-  for(var i : u32; (i < 4u); i = (i + 1)) {
-    arr[i] = conv_S(val[i]);
-  }
-  return arr;
-}
-
-fn f() {
-  s = conv_arr4_S(u);
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, ArrayStructMatUniform_CopyStruct_UniformToWorkgroup_Mat3x2F32) {
-    auto* src = R"(
-struct S {
-  v : vec4<i32>,
-  @size(64)
-  m : mat3x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> u : array<S, 4>;
-
-var<workgroup> w : array<S, 4>;
-
-fn f() {
-  w[0] = u[1];
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  v : vec4<i32>,
-  @size(64)
-  m : mat3x2<f32>,
-}
-
-struct S_std140 {
-  v : vec4<i32>,
-  m_0 : vec2<f32>,
-  m_1 : vec2<f32>,
-  @size(48)
-  m_2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> u : array<S_std140, 4u>;
-
-var<workgroup> w : array<S, 4>;
-
-fn conv_S(val : S_std140) -> S {
-  return S(val.v, mat3x2<f32>(val.m_0, val.m_1, val.m_2));
-}
-
-fn f() {
-  w[0] = conv_S(u[1u]);
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, ArrayStructMatUniform_CopyMatrix_UniformToPrivate_Mat3x2F32) {
-    auto* src = R"(
-struct S {
-  v : vec4<i32>,
-  @size(64)
-  m : mat3x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> u : array<S, 3>;
-
-var<private> p : array<S, 4>;
-
-fn f() {
-  p[2].m = u[1].m;
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  v : vec4<i32>,
-  @size(64)
-  m : mat3x2<f32>,
-}
-
-struct S_std140 {
-  v : vec4<i32>,
-  m_0 : vec2<f32>,
-  m_1 : vec2<f32>,
-  @size(48)
-  m_2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> u : array<S_std140, 3u>;
-
-var<private> p : array<S, 4>;
-
-fn load_u_1_m() -> mat3x2<f32> {
-  let s = &(u[1u]);
-  return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
-}
-
-fn f() {
-  p[2].m = load_u_1_m();
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, ArrayStructMatUniform_CopyColumn_UniformToStorage_Mat3x2F32) {
-    auto* src = R"(
-struct S {
-  @size(64)
-  m : mat3x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> u : array<S, 3>;
-
-@group(0) @binding(1) var<storage, read_write> s : array<S, 4>;
-
-fn f() {
-  s[3].m[1] = u[2].m[0];
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  @size(64)
-  m : mat3x2<f32>,
-}
-
-struct S_std140 {
-  m_0 : vec2<f32>,
-  m_1 : vec2<f32>,
-  @size(48)
-  m_2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> u : array<S_std140, 3u>;
-
-@group(0) @binding(1) var<storage, read_write> s : array<S, 4>;
-
-fn f() {
-  s[3].m[1] = u[2u].m_0;
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, ArrayStructMatUniform_CopyColumnSwizzle_UniformToWorkgroup_Mat3x2F32) {
-    auto* src = R"(
-struct S {
-  @size(64)
-  m : mat3x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> u : array<S, 4>;
-
-var<workgroup> w : array<S, 4>;
-
-fn f() {
-  w[3].m[1] = u[2].m[0].yx.yx;
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  @size(64)
-  m : mat3x2<f32>,
-}
-
-struct S_std140 {
-  m_0 : vec2<f32>,
-  m_1 : vec2<f32>,
-  @size(48)
-  m_2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> u : array<S_std140, 4u>;
-
-var<workgroup> w : array<S, 4>;
-
-fn f() {
-  w[3].m[1] = u[2u].m_0.yx.yx;
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, ArrayStructMatUniform_CopyScalar_UniformToPrivate_Mat3x2F32) {
-    auto* src = R"(
-struct S {
-  v : vec4<i32>,
-  @size(64)
-  m : mat3x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> u : array<S, 3>;
-
-var<private> p : array<S, 4>;
-
-fn f() {
-  p[3].m[1].x = u[2].m[0].y;
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  v : vec4<i32>,
-  @size(64)
-  m : mat3x2<f32>,
-}
-
-struct S_std140 {
-  v : vec4<i32>,
-  m_0 : vec2<f32>,
-  m_1 : vec2<f32>,
-  @size(48)
-  m_2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> u : array<S_std140, 3u>;
-
-var<private> p : array<S, 4>;
-
-fn f() {
-  p[3].m[1].x = u[2u].m_0[1u];
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, ArrayMatUniform_LoadArray_Mat2x2F32) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<mat2x2<f32>, 3>;
-
-fn f() {
-  let l = a;
-}
-)";
-
-    auto* expect = R"(
-struct mat2x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<mat2x2_f32, 3u>;
-
-fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
-  return mat2x2<f32>(val.col0, val.col1);
-}
-
-fn conv_arr3_mat2x2_f32(val : array<mat2x2_f32, 3u>) -> array<mat2x2<f32>, 3u> {
-  var arr : array<mat2x2<f32>, 3u>;
-  for(var i : u32; (i < 3u); i = (i + 1)) {
-    arr[i] = conv_mat2x2_f32(val[i]);
-  }
-  return arr;
-}
-
-fn f() {
-  let l = conv_arr3_mat2x2_f32(a);
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, ArrayMatUniform_LoadMatrix_ConstArrayIndex_Mat2x2F32) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<mat2x2<f32>, 3>;
-
-fn f() {
-  let l = a[2];
-}
-)";
-
-    auto* expect = R"(
-struct mat2x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<mat2x2_f32, 3u>;
-
-fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
-  return mat2x2<f32>(val.col0, val.col1);
-}
-
-fn f() {
-  let l = conv_mat2x2_f32(a[2u]);
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, ArrayMatUniform_LoadMatrix_VariableArrayIndex_Mat2x2F32) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<mat2x2<f32>, 3>;
-
-fn f() {
-  let I = 1;
-  let l = a[I];
-}
-)";
-
-    auto* expect = R"(
-struct mat2x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<mat2x2_f32, 3u>;
-
-fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
-  return mat2x2<f32>(val.col0, val.col1);
-}
-
-fn f() {
-  let I = 1;
-  let l = conv_mat2x2_f32(a[I]);
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, ArrayMatUniform_LoadColumn_ConstArrayIndex_ConstColumnIndex_Mat2x2F32) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<mat2x2<f32>, 3>;
-
-fn f() {
-  let l = a[2][1];
-}
-)";
-
-    auto* expect = R"(
-struct mat2x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<mat2x2_f32, 3u>;
-
-fn f() {
-  let l = a[2u].col1;
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, ArrayMatUniform_LoadColumn_VariableArrayIndex_ConstColumnIndex_Mat2x2F32) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<mat2x2<f32>, 3>;
-
-fn f() {
-  let I = 1;
-  let l = a[I][1];
-}
-)";
-
-    auto* expect = R"(
-struct mat2x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<mat2x2_f32, 3u>;
-
-fn f() {
-  let I = 1;
-  let l = a[I].col1;
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, ArrayMatUniform_LoadColumn_ConstArrayIndex_VariableColumnIndex_Mat2x2F32) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<mat2x2<f32>, 3>;
-
-fn f() {
-  let I = 1;
-  let l = a[2][I];
-}
-)";
-
-    auto* expect = R"(
-struct mat2x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<mat2x2_f32, 3u>;
-
-fn load_a_2_p0(p0 : u32) -> vec2<f32> {
-  switch(p0) {
-    case 0u: {
-      return a[2u].col0;
-    }
-    case 1u: {
-      return a[2u].col1;
-    }
-    default: {
-      return vec2<f32>();
-    }
-  }
-}
-
-fn f() {
-  let I = 1;
-  let l = load_a_2_p0(u32(I));
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32,
-       ArrayMatUniform_LoadColumn_VariableArrayIndex_VariableColumnIndex_Mat2x2F32) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<mat2x2<f32>, 3>;
-
-fn f() {
-  let I = 1;
-  let l = a[I][I];
-}
-)";
-
-    auto* expect = R"(
-struct mat2x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<mat2x2_f32, 3u>;
-
-fn load_a_p0_p1(p0 : u32, p1 : u32) -> vec2<f32> {
-  switch(p1) {
-    case 0u: {
-      return a[p0].col0;
-    }
-    case 1u: {
-      return a[p0].col1;
-    }
-    default: {
-      return vec2<f32>();
-    }
-  }
-}
-
-fn f() {
-  let I = 1;
-  let l = load_a_p0_p1(u32(I), u32(I));
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, StructArrayMatUniform_LoadStruct_Mat2x2F32) {
-    auto* src = R"(
-struct S {
-  a : array<mat2x2<f32>, 3>,
-}
-
-@group(0) @binding(0) var<uniform> s : S;
-
-fn f() {
-  let l = s;
-}
-)";
-
-    auto* expect = R"(
-struct mat2x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-}
-
-struct S {
-  a : array<mat2x2<f32>, 3>,
-}
-
-struct S_std140 {
-  a : array<mat2x2_f32, 3u>,
-}
-
-@group(0) @binding(0) var<uniform> s : S_std140;
-
-fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
-  return mat2x2<f32>(val.col0, val.col1);
-}
-
-fn conv_arr3_mat2x2_f32(val : array<mat2x2_f32, 3u>) -> array<mat2x2<f32>, 3u> {
-  var arr : array<mat2x2<f32>, 3u>;
-  for(var i : u32; (i < 3u); i = (i + 1)) {
-    arr[i] = conv_mat2x2_f32(val[i]);
-  }
-  return arr;
-}
-
-fn conv_S(val : S_std140) -> S {
-  return S(conv_arr3_mat2x2_f32(val.a));
-}
-
-fn f() {
-  let l = conv_S(s);
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, StructArrayMatUniform_LoadArray_Mat2x2F32) {
-    auto* src = R"(
-struct S {
-  a : array<mat2x2<f32>, 3>,
-}
-
-@group(0) @binding(0) var<uniform> s : S;
-
-fn f() {
-  let l = s.a;
-}
-)";
-
-    auto* expect = R"(
-struct mat2x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-}
-
-struct S {
-  a : array<mat2x2<f32>, 3>,
-}
-
-struct S_std140 {
-  a : array<mat2x2_f32, 3u>,
-}
-
-@group(0) @binding(0) var<uniform> s : S_std140;
-
-fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
-  return mat2x2<f32>(val.col0, val.col1);
-}
-
-fn conv_arr3_mat2x2_f32(val : array<mat2x2_f32, 3u>) -> array<mat2x2<f32>, 3u> {
-  var arr : array<mat2x2<f32>, 3u>;
-  for(var i : u32; (i < 3u); i = (i + 1)) {
-    arr[i] = conv_mat2x2_f32(val[i]);
-  }
-  return arr;
-}
-
-fn f() {
-  let l = conv_arr3_mat2x2_f32(s.a);
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, StructArrayMatUniform_LoadMatrix_ConstArrayIndex_Mat2x2F32) {
-    auto* src = R"(
-struct S {
-  a : array<mat2x2<f32>, 3>,
-}
-
-@group(0) @binding(0) var<uniform> s : S;
-
-fn f() {
-  let l = s.a[2];
-}
-)";
-
-    auto* expect = R"(
-struct mat2x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-}
-
-struct S {
-  a : array<mat2x2<f32>, 3>,
-}
-
-struct S_std140 {
-  a : array<mat2x2_f32, 3u>,
-}
-
-@group(0) @binding(0) var<uniform> s : S_std140;
-
-fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
-  return mat2x2<f32>(val.col0, val.col1);
-}
-
-fn f() {
-  let l = conv_mat2x2_f32(s.a[2u]);
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, StructArrayMatUniform_LoadMatrix_VariableArrayIndex_Mat2x2F32) {
-    auto* src = R"(
-struct S {
-  a : array<mat2x2<f32>, 3>,
-}
-
-@group(0) @binding(0) var<uniform> s : S;
-
-fn f() {
-  let I = 1;
-  let l = s.a[I];
-}
-)";
-
-    auto* expect = R"(
-struct mat2x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-}
-
-struct S {
-  a : array<mat2x2<f32>, 3>,
-}
-
-struct S_std140 {
-  a : array<mat2x2_f32, 3u>,
-}
-
-@group(0) @binding(0) var<uniform> s : S_std140;
-
-fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
-  return mat2x2<f32>(val.col0, val.col1);
-}
-
-fn f() {
-  let I = 1;
-  let l = conv_mat2x2_f32(s.a[I]);
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32,
-       StructArrayMatUniform_LoadColumn_ConstArrayIndex_ConstColumnIndex_Mat2x2F32) {
-    auto* src = R"(
-struct S {
-  a : array<mat2x2<f32>, 3>,
-}
-
-@group(0) @binding(0) var<uniform> s : S;
-
-fn f() {
-  let l = s.a[2][1];
-}
-)";
-
-    auto* expect = R"(
-struct mat2x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-}
-
-struct S {
-  a : array<mat2x2<f32>, 3>,
-}
-
-struct S_std140 {
-  a : array<mat2x2_f32, 3u>,
-}
-
-@group(0) @binding(0) var<uniform> s : S_std140;
-
-fn f() {
-  let l = s.a[2u].col1;
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32,
-       StructArrayMatUniform_LoadColumn_VariableArrayIndex_ConstColumnIndex_Mat2x2F32) {
-    auto* src = R"(
-struct S {
-  a : array<mat2x2<f32>, 3>,
-}
-
-@group(0) @binding(0) var<uniform> s : S;
-
-fn f() {
-  let I = 1;
-  let l = s.a[I][1];
-}
-)";
-
-    auto* expect = R"(
-struct mat2x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-}
-
-struct S {
-  a : array<mat2x2<f32>, 3>,
-}
-
-struct S_std140 {
-  a : array<mat2x2_f32, 3u>,
-}
-
-@group(0) @binding(0) var<uniform> s : S_std140;
-
-fn f() {
-  let I = 1;
-  let l = s.a[I].col1;
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32,
-       StructArrayMatUniform_LoadColumn_ConstArrayIndex_VariableColumnIndex_Mat2x2F32) {
-    auto* src = R"(
-struct S {
-  a : array<mat2x2<f32>, 3>,
-}
-
-@group(0) @binding(0) var<uniform> s : S;
-
-fn f() {
-  let I = 1;
-  let l = s.a[2][I];
-}
-)";
-
-    auto* expect = R"(
-struct mat2x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-}
-
-struct S {
-  a : array<mat2x2<f32>, 3>,
-}
-
-struct S_std140 {
-  a : array<mat2x2_f32, 3u>,
-}
-
-@group(0) @binding(0) var<uniform> s : S_std140;
-
-fn load_s_a_2_p0(p0 : u32) -> vec2<f32> {
-  switch(p0) {
-    case 0u: {
-      return s.a[2u].col0;
-    }
-    case 1u: {
-      return s.a[2u].col1;
-    }
-    default: {
-      return vec2<f32>();
-    }
-  }
-}
-
-fn f() {
-  let I = 1;
-  let l = load_s_a_2_p0(u32(I));
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32,
-       StructArrayMatUniform_LoadColumn_VariableArrayIndex_VariableColumnIndex_Mat2x2F32) {
-    auto* src = R"(
-struct S {
-  a : array<mat2x2<f32>, 3>,
-}
-
-@group(0) @binding(0) var<uniform> s : S;
-
-fn f() {
-  let I = 1;
-  let l = s.a[I][I];
-}
-)";
-
-    auto* expect = R"(
-struct mat2x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-}
-
-struct S {
-  a : array<mat2x2<f32>, 3>,
-}
-
-struct S_std140 {
-  a : array<mat2x2_f32, 3u>,
-}
-
-@group(0) @binding(0) var<uniform> s : S_std140;
-
-fn load_s_a_p0_p1(p0 : u32, p1 : u32) -> vec2<f32> {
-  switch(p1) {
-    case 0u: {
-      return s.a[p0].col0;
-    }
-    case 1u: {
-      return s.a[p0].col1;
-    }
-    default: {
-      return vec2<f32>();
-    }
-  }
-}
-
-fn f() {
-  let I = 1;
-  let l = load_s_a_p0_p1(u32(I), u32(I));
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, ArrayArrayMatUniform_LoadArrays_Mat2x2F32) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
-
-fn f() {
-  let l = a;
-}
-)";
-
-    auto* expect = R"(
-struct mat2x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
-
-fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
-  return mat2x2<f32>(val.col0, val.col1);
-}
-
-fn conv_arr3_mat2x2_f32(val : array<mat2x2_f32, 3u>) -> array<mat2x2<f32>, 3u> {
-  var arr : array<mat2x2<f32>, 3u>;
-  for(var i : u32; (i < 3u); i = (i + 1)) {
-    arr[i] = conv_mat2x2_f32(val[i]);
-  }
-  return arr;
-}
-
-fn conv_arr4_arr3_mat2x2_f32(val : array<array<mat2x2_f32, 3u>, 4u>) -> array<array<mat2x2<f32>, 3u>, 4u> {
-  var arr : array<array<mat2x2<f32>, 3u>, 4u>;
-  for(var i : u32; (i < 4u); i = (i + 1)) {
-    arr[i] = conv_arr3_mat2x2_f32(val[i]);
-  }
-  return arr;
-}
-
-fn f() {
-  let l = conv_arr4_arr3_mat2x2_f32(a);
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, ArrayArrayMatUniform_LoadArray_ConstOuterArrayIndex_Mat2x2F32) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
-
-fn f() {
-  let l = a[3];
-}
-)";
-
-    auto* expect = R"(
-struct mat2x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
-
-fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
-  return mat2x2<f32>(val.col0, val.col1);
-}
-
-fn conv_arr3_mat2x2_f32(val : array<mat2x2_f32, 3u>) -> array<mat2x2<f32>, 3u> {
-  var arr : array<mat2x2<f32>, 3u>;
-  for(var i : u32; (i < 3u); i = (i + 1)) {
-    arr[i] = conv_mat2x2_f32(val[i]);
-  }
-  return arr;
-}
-
-fn f() {
-  let l = conv_arr3_mat2x2_f32(a[3u]);
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32, ArrayArrayMatUniform_LoadArray_VariableOuterArrayIndex_Mat2x2F32) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
-
-fn f() {
-  let I = 1;
-  let l = a[I];
-}
-)";
-
-    auto* expect = R"(
-struct mat2x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
-
-fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
-  return mat2x2<f32>(val.col0, val.col1);
-}
-
-fn conv_arr3_mat2x2_f32(val : array<mat2x2_f32, 3u>) -> array<mat2x2<f32>, 3u> {
-  var arr : array<mat2x2<f32>, 3u>;
-  for(var i : u32; (i < 3u); i = (i + 1)) {
-    arr[i] = conv_mat2x2_f32(val[i]);
-  }
-  return arr;
-}
-
-fn f() {
-  let I = 1;
-  let l = conv_arr3_mat2x2_f32(a[I]);
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32,
-       ArrayArrayMatUniform_LoadMatrix_ConstOuterArrayIndex_ConstInnerArrayIndex_Mat2x2F32) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
-
-fn f() {
-  let l = a[3][2];
-}
-)";
-
-    auto* expect = R"(
-struct mat2x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
-
-fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
-  return mat2x2<f32>(val.col0, val.col1);
-}
-
-fn f() {
-  let l = conv_mat2x2_f32(a[3u][2u]);
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32,
-       ArrayArrayMatUniform_LoadMatrix_ConstOuterArrayIndex_VariableInnerArrayIndex_Mat2x2F32) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
-
-fn f() {
-  let I = 1;
-  let l = a[3][I];
-}
-)";
-
-    auto* expect = R"(
-struct mat2x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
-
-fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
-  return mat2x2<f32>(val.col0, val.col1);
-}
-
-fn f() {
-  let I = 1;
-  let l = conv_mat2x2_f32(a[3u][I]);
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32,
-       ArrayArrayMatUniform_LoadMatrix_VariableOuterArrayIndex_ConstInnerArrayIndex_Mat2x2F32) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
-
-fn f() {
-  let I = 1;
-  let l = a[I][2];
-}
-)";
-
-    auto* expect = R"(
-struct mat2x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
-
-fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
-  return mat2x2<f32>(val.col0, val.col1);
-}
-
-fn f() {
-  let I = 1;
-  let l = conv_mat2x2_f32(a[I][2u]);
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test_F32,
-       ArrayArrayMatUniform_LoadMatrix_VariableOuterArrayIndex_VariableInnerArrayIndex_Mat2x2F32) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
-
-fn f() {
-  let I = 1;
-  let l = a[I][I];
-}
-)";
-
-    auto* expect = R"(
-struct mat2x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
-
-fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
-  return mat2x2<f32>(val.col0, val.col1);
-}
-
-fn f() {
-  let I = 1;
-  let l = conv_mat2x2_f32(a[I][I]);
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(
-    Std140Test_F32,
-    ArrayArrayMatUniform_LoadColumn_ConstOuterArrayIndex_ConstInnerArrayIndex_ConstColumnIndex_Mat2x2F32) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
-
-fn f() {
-  let l = a[3][2][1];
-}
-)";
-
-    auto* expect = R"(
-struct mat2x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
-
-fn f() {
-  let l = a[3u][2u].col1;
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(
-    Std140Test_F32,
-    ArrayArrayMatUniform_LoadColumn_ConstOuterArrayIndex_ConstInnerArrayIndex_VariableColumnIndex_Mat2x2F32) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
-
-fn f() {
-  let I = 1;
-  let l = a[3][2][I];
-}
-)";
-
-    auto* expect = R"(
-struct mat2x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
-
-fn load_a_3_2_p0(p0 : u32) -> vec2<f32> {
-  switch(p0) {
-    case 0u: {
-      return a[3u][2u].col0;
-    }
-    case 1u: {
-      return a[3u][2u].col1;
-    }
-    default: {
-      return vec2<f32>();
-    }
-  }
-}
-
-fn f() {
-  let I = 1;
-  let l = load_a_3_2_p0(u32(I));
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(
-    Std140Test_F32,
-    ArrayArrayMatUniform_LoadColumn_ConstOuterArrayIndex_VariableInnerArrayIndex_ConstColumnIndex_Mat2x2F32) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
-
-fn f() {
-  let I = 1;
-  let l = a[3][I][1];
-}
-)";
-
-    auto* expect = R"(
-struct mat2x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
-
-fn f() {
-  let I = 1;
-  let l = a[3u][I].col1;
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(
-    Std140Test_F32,
-    ArrayArrayMatUniform_LoadColumn_ConstOuterArrayIndex_VariableInnerArrayIndex_VariableColumnIndex_Mat2x2F32) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
-
-fn f() {
-  let I = 1;
-  let J = 2;
-  let l = a[3][I][J];
-}
-)";
-
-    auto* expect = R"(
-struct mat2x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
-
-fn load_a_3_p0_p1(p0 : u32, p1 : u32) -> vec2<f32> {
-  switch(p1) {
-    case 0u: {
-      return a[3u][p0].col0;
-    }
-    case 1u: {
-      return a[3u][p0].col1;
-    }
-    default: {
-      return vec2<f32>();
-    }
-  }
-}
-
-fn f() {
-  let I = 1;
-  let J = 2;
-  let l = load_a_3_p0_p1(u32(I), u32(J));
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(
-    Std140Test_F32,
-    ArrayArrayMatUniform_LoadColumn_VariableOuterArrayIndex_ConstInnerArrayIndex_ConstColumnIndex_Mat2x2F32) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
-
-fn f() {
-  let I = 1;
-  let l = a[I][2][1];
-}
-)";
-
-    auto* expect = R"(
-struct mat2x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
-
-fn f() {
-  let I = 1;
-  let l = a[I][2u].col1;
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(
-    Std140Test_F32,
-    ArrayArrayMatUniform_LoadColumn_VariableOuterArrayIndex_ConstInnerArrayIndex_VariableColumnIndex_Mat2x2F32) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
-
-fn f() {
-  let I = 1;
-  let J = 2;
-  let l = a[I][2][J];
-}
-)";
-
-    auto* expect = R"(
-struct mat2x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
-
-fn load_a_p0_2_p1(p0 : u32, p1 : u32) -> vec2<f32> {
-  switch(p1) {
-    case 0u: {
-      return a[p0][2u].col0;
-    }
-    case 1u: {
-      return a[p0][2u].col1;
-    }
-    default: {
-      return vec2<f32>();
-    }
-  }
-}
-
-fn f() {
-  let I = 1;
-  let J = 2;
-  let l = load_a_p0_2_p1(u32(I), u32(J));
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(
-    Std140Test_F32,
-    ArrayArrayMatUniform_LoadColumn_VariableOuterArrayIndex_VariableInnerArrayIndex_ConstColumnIndex_Mat2x2F32) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
-
-fn f() {
-  let I = 1;
-  let J = 2;
-  let l = a[I][J][1];
-}
-)";
-
-    auto* expect = R"(
-struct mat2x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
-
-fn f() {
-  let I = 1;
-  let J = 2;
-  let l = a[I][J].col1;
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(
-    Std140Test_F32,
-    ArrayArrayMatUniform_LoadColumn_VariableOuterArrayIndex_VariableInnerArrayIndex_VariableColumnIndex_Mat2x2F32) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
-
-fn f() {
-  let I = 0;
-  let J = 1;
-  let K = 2;
-  let l = a[I][J][K];
-}
-)";
-
-    auto* expect = R"(
-struct mat2x2_f32 {
-  col0 : vec2<f32>,
-  col1 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
-
-fn load_a_p0_p1_p2(p0 : u32, p1 : u32, p2 : u32) -> vec2<f32> {
-  switch(p2) {
-    case 0u: {
-      return a[p0][p1].col0;
-    }
-    case 1u: {
-      return a[p0][p1].col1;
-    }
-    default: {
-      return vec2<f32>();
-    }
-  }
-}
-
-fn f() {
-  let I = 0;
-  let J = 1;
-  let K = 2;
-  let l = load_a_p0_p1_p2(u32(I), u32(J), u32(K));
-}
-)";
-
-    auto got = Run<Std140>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
 }  // namespace
 }  // namespace tint::transform
diff --git a/src/tint/transform/truncate_interstage_variables.cc b/src/tint/transform/truncate_interstage_variables.cc
index a5e7256..30237bc 100644
--- a/src/tint/transform/truncate_interstage_variables.cc
+++ b/src/tint/transform/truncate_interstage_variables.cc
@@ -161,7 +161,7 @@
     ctx.ReplaceAll(
         [&](const ast::ReturnStatement* return_statement) -> const ast::ReturnStatement* {
             auto* return_sem = sem.Get(return_statement);
-            if (auto* mapping_fn_sym =
+            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)));
diff --git a/src/tint/transform/unshadow.cc b/src/tint/transform/unshadow.cc
index 93ce595..8d2b876 100644
--- a/src/tint/transform/unshadow.cc
+++ b/src/tint/transform/unshadow.cc
@@ -97,7 +97,7 @@
         ctx.ReplaceAll(
             [&](const ast::IdentifierExpression* ident) -> const tint::ast::IdentifierExpression* {
                 if (auto* user = sem.Get<sem::VariableUser>(ident)) {
-                    if (auto* renamed = renamed_to.Find(user->Variable())) {
+                    if (auto renamed = renamed_to.Find(user->Variable())) {
                         return b.Expr(*renamed);
                     }
                 }
diff --git a/src/tint/transform/utils/hoist_to_decl_before.cc b/src/tint/transform/utils/hoist_to_decl_before.cc
index e0b35a1..ede1986 100644
--- a/src/tint/transform/utils/hoist_to_decl_before.cc
+++ b/src/tint/transform/utils/hoist_to_decl_before.cc
@@ -38,23 +38,39 @@
     /// @copydoc HoistToDeclBefore::Add()
     bool Add(const sem::Expression* before_expr,
              const ast::Expression* expr,
-             bool as_let,
+             VariableKind kind,
              const char* decl_name) {
         auto name = b.Symbols().New(decl_name);
 
-        if (as_let) {
-            auto builder = [this, expr, name] {
-                return b.Decl(b.Let(name, ctx.CloneWithoutTransform(expr)));
-            };
-            if (!InsertBeforeImpl(before_expr->Stmt(), std::move(builder))) {
-                return false;
+        switch (kind) {
+            case VariableKind::kLet: {
+                auto builder = [this, expr, name] {
+                    return b.Decl(b.Let(name, ctx.CloneWithoutTransform(expr)));
+                };
+                if (!InsertBeforeImpl(before_expr->Stmt(), std::move(builder))) {
+                    return false;
+                }
+                break;
             }
-        } else {
-            auto builder = [this, expr, name] {
-                return b.Decl(b.Var(name, ctx.CloneWithoutTransform(expr)));
-            };
-            if (!InsertBeforeImpl(before_expr->Stmt(), std::move(builder))) {
-                return false;
+
+            case VariableKind::kVar: {
+                auto builder = [this, expr, name] {
+                    return b.Decl(b.Var(name, ctx.CloneWithoutTransform(expr)));
+                };
+                if (!InsertBeforeImpl(before_expr->Stmt(), std::move(builder))) {
+                    return false;
+                }
+                break;
+            }
+
+            case VariableKind::kConst: {
+                auto builder = [this, expr, name] {
+                    return b.Decl(b.Const(name, ctx.CloneWithoutTransform(expr)));
+                };
+                if (!InsertBeforeImpl(before_expr->Stmt(), std::move(builder))) {
+                    return false;
+                }
+                break;
             }
         }
 
@@ -119,7 +135,7 @@
     /// automatically called.
     /// @warning the returned reference is invalid if this is called a second time, or the
     /// #for_loops map is mutated.
-    LoopInfo& ForLoop(const sem::ForLoopStatement* for_loop) {
+    auto ForLoop(const sem::ForLoopStatement* for_loop) {
         if (for_loops.IsEmpty()) {
             RegisterForLoopTransform();
         }
@@ -131,7 +147,7 @@
     /// automatically called.
     /// @warning the returned reference is invalid if this is called a second time, or the
     /// #for_loops map is mutated.
-    LoopInfo& WhileLoop(const sem::WhileStatement* while_loop) {
+    auto WhileLoop(const sem::WhileStatement* while_loop) {
         if (while_loops.IsEmpty()) {
             RegisterWhileLoopTransform();
         }
@@ -143,7 +159,7 @@
     /// automatically called.
     /// @warning the returned reference is invalid if this is called a second time, or the
     /// #else_ifs map is mutated.
-    ElseIfInfo& ElseIf(const ast::IfStatement* else_if) {
+    auto ElseIf(const ast::IfStatement* else_if) {
         if (else_ifs.IsEmpty()) {
             RegisterElseIfTransform();
         }
@@ -156,7 +172,7 @@
             auto& sem = ctx.src->Sem();
 
             if (auto* fl = sem.Get(stmt)) {
-                if (auto* info = for_loops.Find(fl)) {
+                if (auto info = for_loops.Find(fl)) {
                     auto* for_loop = fl->Declaration();
                     // For-loop needs to be decomposed to a loop.
                     // Build the loop body's statements.
@@ -206,7 +222,7 @@
             auto& sem = ctx.src->Sem();
 
             if (auto* w = sem.Get(stmt)) {
-                if (auto* info = while_loops.Find(w)) {
+                if (auto info = while_loops.Find(w)) {
                     auto* while_loop = w->Declaration();
                     // While needs to be decomposed to a loop.
                     // Build the loop body's statements.
@@ -243,7 +259,7 @@
     void RegisterElseIfTransform() const {
         // Decompose 'else-if' statements into 'else { if }' blocks.
         ctx.ReplaceAll([&](const ast::IfStatement* stmt) -> const ast::Statement* {
-            if (auto* info = else_ifs.Find(stmt)) {
+            if (auto info = else_ifs.Find(stmt)) {
                 // Build the else block's body statements, starting with let decls for the
                 // conditional expression.
                 auto body_stmts = Build(info->cond_decls);
@@ -275,10 +291,10 @@
         if (else_if && else_if->Parent()->Is<sem::IfStatement>()) {
             // Insertion point is an 'else if' condition.
             // Need to convert 'else if' to 'else { if }'.
-            auto& else_if_info = ElseIf(else_if->Declaration());
+            auto else_if_info = ElseIf(else_if->Declaration());
 
             // Index the map to convert this else if, even if `stmt` is nullptr.
-            auto& decls = else_if_info.cond_decls;
+            auto& decls = else_if_info->cond_decls;
             if constexpr (!std::is_same_v<BUILDER, Decompose>) {
                 decls.Push(std::forward<BUILDER>(builder));
             }
@@ -290,7 +306,7 @@
             // For-loop needs to be decomposed to a loop.
 
             // Index the map to convert this for-loop, even if `stmt` is nullptr.
-            auto& decls = ForLoop(fl).cond_decls;
+            auto& decls = ForLoop(fl)->cond_decls;
             if constexpr (!std::is_same_v<BUILDER, Decompose>) {
                 decls.Push(std::forward<BUILDER>(builder));
             }
@@ -302,7 +318,7 @@
             // While needs to be decomposed to a loop.
 
             // Index the map to convert this while, even if `stmt` is nullptr.
-            auto& decls = WhileLoop(w).cond_decls;
+            auto& decls = WhileLoop(w)->cond_decls;
             if constexpr (!std::is_same_v<BUILDER, Decompose>) {
                 decls.Push(std::forward<BUILDER>(builder));
             }
@@ -338,7 +354,7 @@
                 // For-loop needs to be decomposed to a loop.
 
                 // Index the map to convert this for-loop, even if `stmt` is nullptr.
-                auto& decls = ForLoop(fl).cont_decls;
+                auto& decls = ForLoop(fl)->cont_decls;
                 if constexpr (!std::is_same_v<BUILDER, Decompose>) {
                     decls.Push(std::forward<BUILDER>(builder));
                 }
@@ -361,9 +377,9 @@
 
 bool HoistToDeclBefore::Add(const sem::Expression* before_expr,
                             const ast::Expression* expr,
-                            bool as_let,
+                            VariableKind kind,
                             const char* decl_name) {
-    return state_->Add(before_expr, expr, as_let, decl_name);
+    return state_->Add(before_expr, expr, kind, decl_name);
 }
 
 bool HoistToDeclBefore::InsertBefore(const sem::Statement* before_stmt,
diff --git a/src/tint/transform/utils/hoist_to_decl_before.h b/src/tint/transform/utils/hoist_to_decl_before.h
index d9a8a8a..4d6a808 100644
--- a/src/tint/transform/utils/hoist_to_decl_before.h
+++ b/src/tint/transform/utils/hoist_to_decl_before.h
@@ -38,16 +38,23 @@
     /// StmtBuilder is a builder of an AST statement
     using StmtBuilder = std::function<const ast::Statement*()>;
 
+    /// VariableKind is either a var, let or const
+    enum class VariableKind {
+        kVar,
+        kLet,
+        kConst,
+    };
+
     /// Hoists @p expr to a `let` or `var` with optional `decl_name`, inserting it
     /// before @p before_expr.
     /// @param before_expr expression to insert `expr` before
     /// @param expr expression to hoist
-    /// @param as_let hoist to `let` if true, otherwise to `var`
+    /// @param kind variable kind to hoist to
     /// @param decl_name optional name to use for the variable/constant name
     /// @return true on success
     bool Add(const sem::Expression* before_expr,
              const ast::Expression* expr,
-             bool as_let,
+             VariableKind kind,
              const char* decl_name = "");
 
     /// Inserts @p stmt before @p before_stmt, possibly converting 'for-loop's to 'loop's if
diff --git a/src/tint/transform/utils/hoist_to_decl_before_test.cc b/src/tint/transform/utils/hoist_to_decl_before_test.cc
index 7b45e01..c5dca5d 100644
--- a/src/tint/transform/utils/hoist_to_decl_before_test.cc
+++ b/src/tint/transform/utils/hoist_to_decl_before_test.cc
@@ -44,7 +44,7 @@
 
     HoistToDeclBefore hoistToDeclBefore(ctx);
     auto* sem_expr = ctx.src->Sem().Get(expr);
-    hoistToDeclBefore.Add(sem_expr, expr, true);
+    hoistToDeclBefore.Add(sem_expr, expr, HoistToDeclBefore::VariableKind::kLet);
 
     ctx.Clone();
     Program cloned(std::move(cloned_b));
@@ -75,14 +75,14 @@
 
     HoistToDeclBefore hoistToDeclBefore(ctx);
     auto* sem_expr = ctx.src->Sem().Get(expr);
-    hoistToDeclBefore.Add(sem_expr, expr, true);
+    hoistToDeclBefore.Add(sem_expr, expr, HoistToDeclBefore::VariableKind::kVar);
 
     ctx.Clone();
     Program cloned(std::move(cloned_b));
 
     auto* expect = R"(
 fn f() {
-  let tint_symbol = 1i;
+  var tint_symbol = 1i;
   for(var a = tint_symbol; true; ) {
   }
 }
@@ -93,12 +93,12 @@
 
 TEST_F(HoistToDeclBeforeTest, ForLoopCond) {
     // fn f() {
-    //     var a : bool;
+    //     const a = true;
     //     for(; a; ) {
     //     }
     // }
     ProgramBuilder b;
-    auto* var = b.Decl(b.Var("a", b.ty.bool_()));
+    auto* var = b.Decl(b.Const("a", b.Expr(true)));
     auto* expr = b.Expr("a");
     auto* s = b.For(nullptr, expr, nullptr, b.Block());
     b.Func("f", utils::Empty, b.ty.void_(), utils::Vector{var, s});
@@ -109,16 +109,16 @@
 
     HoistToDeclBefore hoistToDeclBefore(ctx);
     auto* sem_expr = ctx.src->Sem().Get(expr);
-    hoistToDeclBefore.Add(sem_expr, expr, true);
+    hoistToDeclBefore.Add(sem_expr, expr, HoistToDeclBefore::VariableKind::kConst);
 
     ctx.Clone();
     Program cloned(std::move(cloned_b));
 
     auto* expect = R"(
 fn f() {
-  var a : bool;
+  const a = true;
   loop {
-    let tint_symbol = a;
+    const tint_symbol = a;
     if (!(tint_symbol)) {
       break;
     }
@@ -147,7 +147,7 @@
 
     HoistToDeclBefore hoistToDeclBefore(ctx);
     auto* sem_expr = ctx.src->Sem().Get(expr);
-    hoistToDeclBefore.Add(sem_expr, expr, true);
+    hoistToDeclBefore.Add(sem_expr, expr, HoistToDeclBefore::VariableKind::kLet);
 
     ctx.Clone();
     Program cloned(std::move(cloned_b));
@@ -190,7 +190,7 @@
 
     HoistToDeclBefore hoistToDeclBefore(ctx);
     auto* sem_expr = ctx.src->Sem().Get(expr);
-    hoistToDeclBefore.Add(sem_expr, expr, true);
+    hoistToDeclBefore.Add(sem_expr, expr, HoistToDeclBefore::VariableKind::kVar);
 
     ctx.Clone();
     Program cloned(std::move(cloned_b));
@@ -199,7 +199,7 @@
 fn f() {
   var a : bool;
   loop {
-    let tint_symbol = a;
+    var tint_symbol = a;
     if (!(tint_symbol)) {
       break;
     }
@@ -214,14 +214,14 @@
 
 TEST_F(HoistToDeclBeforeTest, ElseIf) {
     // fn f() {
-    //     var a : bool;
+    //     const a = true;
     //     if (true) {
     //     } else if (a) {
     //     } else {
     //     }
     // }
     ProgramBuilder b;
-    auto* var = b.Decl(b.Var("a", b.ty.bool_()));
+    auto* var = b.Decl(b.Const("a", b.Expr(true)));
     auto* expr = b.Expr("a");
     auto* s = b.If(b.Expr(true), b.Block(),      //
                    b.Else(b.If(expr, b.Block(),  //
@@ -234,17 +234,17 @@
 
     HoistToDeclBefore hoistToDeclBefore(ctx);
     auto* sem_expr = ctx.src->Sem().Get(expr);
-    hoistToDeclBefore.Add(sem_expr, expr, true);
+    hoistToDeclBefore.Add(sem_expr, expr, HoistToDeclBefore::VariableKind::kConst);
 
     ctx.Clone();
     Program cloned(std::move(cloned_b));
 
     auto* expect = R"(
 fn f() {
-  var a : bool;
+  const a = true;
   if (true) {
   } else {
-    let tint_symbol = a;
+    const tint_symbol = a;
     if (tint_symbol) {
     } else {
     }
@@ -272,7 +272,7 @@
 
     HoistToDeclBefore hoistToDeclBefore(ctx);
     auto* sem_expr = ctx.src->Sem().Get(expr);
-    hoistToDeclBefore.Add(sem_expr, expr, true);
+    hoistToDeclBefore.Add(sem_expr, expr, HoistToDeclBefore::VariableKind::kLet);
 
     ctx.Clone();
     Program cloned(std::move(cloned_b));
@@ -306,7 +306,7 @@
 
     HoistToDeclBefore hoistToDeclBefore(ctx);
     auto* sem_expr = ctx.src->Sem().Get(expr);
-    hoistToDeclBefore.Add(sem_expr, expr, true);
+    hoistToDeclBefore.Add(sem_expr, expr, HoistToDeclBefore::VariableKind::kVar);
 
     ctx.Clone();
     Program cloned(std::move(cloned_b));
@@ -314,7 +314,7 @@
     auto* expect = R"(
 fn f() {
   var a : array<array<i32, 10u>, 10i>;
-  let tint_symbol = a[0i][0i];
+  var tint_symbol = a[0i][0i];
   var b = tint_symbol;
 }
 )";
diff --git a/src/tint/transform/var_for_dynamic_index.cc b/src/tint/transform/var_for_dynamic_index.cc
index 81af013..c2fb0a5 100644
--- a/src/tint/transform/var_for_dynamic_index.cc
+++ b/src/tint/transform/var_for_dynamic_index.cc
@@ -54,7 +54,8 @@
 
         // TODO(bclayton): group multiple accesses in the same object.
         // e.g. arr[i] + arr[i+1] // Don't create two vars for this
-        return hoist_to_decl_before.Add(indexed, object_expr, false, "var_for_index");
+        return hoist_to_decl_before.Add(indexed, object_expr, HoistToDeclBefore::VariableKind::kVar,
+                                        "var_for_index");
     };
 
     bool index_accessor_found = false;
diff --git a/src/tint/utils/hashmap.h b/src/tint/utils/hashmap.h
index 1040e0c..19a5abe 100644
--- a/src/tint/utils/hashmap.h
+++ b/src/tint/utils/hashmap.h
@@ -47,6 +47,67 @@
     /// Result of Add()
     using AddResult = typename Base::PutResult;
 
+    /// Reference is returned by Hashmap::Find(), and performs dynamic Hashmap lookups.
+    /// The value returned by the Reference reflects the current state of the Hashmap, and so the
+    /// referenced value may change, or transition between valid or invalid based on the current
+    /// state of the Hashmap.
+    template <bool IS_CONST>
+    class ReferenceT {
+        /// `const Value` if IS_CONST, or `Value` if !IS_CONST
+        using T = std::conditional_t<IS_CONST, const Value, Value>;
+
+        /// `const Hashmap` if IS_CONST, or `Hashmap` if !IS_CONST
+        using Map = std::conditional_t<IS_CONST, const Hashmap, Hashmap>;
+
+      public:
+        /// @returns true if the reference is valid.
+        operator bool() const { return Get() != nullptr; }
+
+        /// @returns the pointer to the Value, or nullptr if the reference is invalid.
+        operator T*() const { return Get(); }
+
+        /// @returns the pointer to the Value
+        /// @warning if the Hashmap does not contain a value for the reference, then this will
+        /// trigger a TINT_ASSERT, or invalid pointer dereference.
+        T* operator->() const {
+            auto* hashmap_reference_lookup = Get();
+            TINT_ASSERT(Utils, hashmap_reference_lookup != nullptr);
+            return hashmap_reference_lookup;
+        }
+
+        /// @returns the pointer to the Value, or nullptr if the reference is invalid.
+        T* Get() const {
+            auto generation = map_.Generation();
+            if (generation_ != generation) {
+                cached_ = map_.Lookup(key_);
+                generation_ = generation;
+            }
+            return cached_;
+        }
+
+      private:
+        friend Hashmap;
+
+        /// Constructor
+        ReferenceT(Map& map, const Key& key)
+            : map_(map), key_(key), cached_(nullptr), generation_(map.Generation() - 1) {}
+
+        /// Constructor
+        ReferenceT(Map& map, const Key& key, T* value)
+            : map_(map), key_(key), cached_(value), generation_(map.Generation()) {}
+
+        Map& map_;
+        const Key key_;
+        mutable T* cached_ = nullptr;
+        mutable size_t generation_ = 0;
+    };
+
+    /// A mutable reference returned by Find()
+    using Reference = ReferenceT</*IS_CONST*/ false>;
+
+    /// An immutable reference returned by Find()
+    using ConstReference = ReferenceT</*IS_CONST*/ true>;
+
     /// Adds a value to the map, if the map does not already contain an entry with the key @p key.
     /// @param key the entry key.
     /// @param value the value of the entry to add to the map.
@@ -108,25 +169,28 @@
     /// @param key the entry's key value to search for.
     /// @returns the value of the entry.
     template <typename K>
-    Value& GetOrZero(K&& key) {
+    Reference GetOrZero(K&& key) {
         auto res = Add(std::forward<K>(key), Value{});
-        return *res.value;
+        return Reference(*this, key, res.value);
     }
 
     /// @param key the key to search for.
-    /// @returns a pointer to the entry that is equal to the given value, or nullptr if the map does
-    ///          not contain the given value.
-    const Value* Find(const Key& key) const {
+    /// @returns a reference to the entry that is equal to the given value.
+    Reference Find(const Key& key) { return Reference(*this, key); }
+
+    /// @param key the key to search for.
+    /// @returns a reference to the entry that is equal to the given value.
+    ConstReference Find(const Key& key) const { return ConstReference(*this, key); }
+
+  private:
+    Value* Lookup(const Key& key) {
         if (auto [found, index] = this->IndexOf(key); found) {
             return &this->slots_[index].entry->value;
         }
         return nullptr;
     }
 
-    /// @param key the key to search for.
-    /// @returns a pointer to the entry that is equal to the given value, or nullptr if the map does
-    ///          not contain the given value.
-    Value* Find(const Key& key) {
+    const Value* Lookup(const Key& key) const {
         if (auto [found, index] = this->IndexOf(key); found) {
             return &this->slots_[index].entry->value;
         }
diff --git a/src/tint/utils/hashmap_test.cc b/src/tint/utils/hashmap_test.cc
index 77421cf..34a93e6 100644
--- a/src/tint/utils/hashmap_test.cc
+++ b/src/tint/utils/hashmap_test.cc
@@ -90,6 +90,71 @@
     EXPECT_EQ(map.Generation(), 5u);
 }
 
+TEST(Hashmap, Index) {
+    Hashmap<int, std::string, 4> map;
+    auto zero = map.Find(0);
+    EXPECT_FALSE(zero);
+
+    map.Add(3, "three");
+    auto three = map.Find(3);
+    map.Add(2, "two");
+    auto two = map.Find(2);
+    map.Add(4, "four");
+    auto four = map.Find(4);
+    map.Add(8, "eight");
+    auto eight = map.Find(8);
+
+    EXPECT_FALSE(zero);
+    ASSERT_TRUE(three);
+    ASSERT_TRUE(two);
+    ASSERT_TRUE(four);
+    ASSERT_TRUE(eight);
+
+    EXPECT_EQ(*three, "three");
+    EXPECT_EQ(*two, "two");
+    EXPECT_EQ(*four, "four");
+    EXPECT_EQ(*eight, "eight");
+
+    map.Add(0, "zero");  // Note: Find called before Add() is okay!
+
+    map.Add(5, "five");
+    auto five = map.Find(5);
+    map.Add(6, "six");
+    auto six = map.Find(6);
+    map.Add(1, "one");
+    auto one = map.Find(1);
+    map.Add(7, "seven");
+    auto seven = map.Find(7);
+
+    ASSERT_TRUE(zero);
+    ASSERT_TRUE(three);
+    ASSERT_TRUE(two);
+    ASSERT_TRUE(four);
+    ASSERT_TRUE(eight);
+    ASSERT_TRUE(five);
+    ASSERT_TRUE(six);
+    ASSERT_TRUE(one);
+    ASSERT_TRUE(seven);
+
+    EXPECT_EQ(*zero, "zero");
+    EXPECT_EQ(*three, "three");
+    EXPECT_EQ(*two, "two");
+    EXPECT_EQ(*four, "four");
+    EXPECT_EQ(*eight, "eight");
+    EXPECT_EQ(*five, "five");
+    EXPECT_EQ(*six, "six");
+    EXPECT_EQ(*one, "one");
+    EXPECT_EQ(*seven, "seven");
+
+    map.Remove(2);
+    map.Remove(8);
+    map.Remove(1);
+
+    EXPECT_FALSE(two);
+    EXPECT_FALSE(eight);
+    EXPECT_FALSE(one);
+}
+
 TEST(Hashmap, Iterator) {
     using Map = Hashmap<int, std::string, 8>;
     using Entry = typename Map::Entry;
diff --git a/src/tint/writer/glsl/generator_impl_builtin_test.cc b/src/tint/writer/glsl/generator_impl_builtin_test.cc
index 7d0a72e..d4b7053 100644
--- a/src/tint/writer/glsl/generator_impl_builtin_test.cc
+++ b/src/tint/writer/glsl/generator_impl_builtin_test.cc
@@ -425,13 +425,13 @@
     ASSERT_TRUE(gen.Generate()) << gen.error();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
-struct modf_result {
+struct modf_result_f32 {
   float fract;
   float whole;
 };
 
-modf_result tint_modf(float param_0) {
-  modf_result result;
+modf_result_f32 tint_modf(float param_0) {
+  modf_result_f32 result;
   result.fract = modf(param_0, result.whole);
   return result;
 }
@@ -439,7 +439,7 @@
 
 void test_function() {
   float f = 1.5f;
-  modf_result v = tint_modf(f);
+  modf_result_f32 v = tint_modf(f);
 }
 
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
@@ -496,13 +496,13 @@
     ASSERT_TRUE(gen.Generate()) << gen.error();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
-struct modf_result_vec3 {
+struct modf_result_vec3_f32 {
   vec3 fract;
   vec3 whole;
 };
 
-modf_result_vec3 tint_modf(vec3 param_0) {
-  modf_result_vec3 result;
+modf_result_vec3_f32 tint_modf(vec3 param_0) {
+  modf_result_vec3_f32 result;
   result.fract = modf(param_0, result.whole);
   return result;
 }
@@ -510,7 +510,7 @@
 
 void test_function() {
   vec3 f = vec3(1.5f, 2.5f, 3.5f);
-  modf_result_vec3 v = tint_modf(f);
+  modf_result_vec3_f32 v = tint_modf(f);
 }
 
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
@@ -566,14 +566,14 @@
     ASSERT_TRUE(gen.Generate()) << gen.error();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
-struct modf_result {
+struct modf_result_f32 {
   float fract;
   float whole;
 };
 
 
 void test_function() {
-  modf_result v = modf_result(0.5f, 1.0f);
+  modf_result_f32 v = modf_result_f32(0.5f, 1.0f);
 }
 
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
@@ -621,14 +621,14 @@
     ASSERT_TRUE(gen.Generate()) << gen.error();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
-struct modf_result_vec3 {
+struct modf_result_vec3_f32 {
   vec3 fract;
   vec3 whole;
 };
 
 
 void test_function() {
-  modf_result_vec3 v = modf_result_vec3(vec3(0.5f), vec3(1.0f, 2.0f, 3.0f));
+  modf_result_vec3_f32 v = modf_result_vec3_f32(vec3(0.5f), vec3(1.0f, 2.0f, 3.0f));
 }
 
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
@@ -668,29 +668,30 @@
 )");
 }
 
-TEST_F(GlslGeneratorImplTest_Builtin, Frexp_Scalar_f32) {
-    auto* call = Call("frexp", 1_f);
-    WrapInFunction(CallStmt(call));
+TEST_F(GlslGeneratorImplTest_Builtin, Runtime_Frexp_Scalar_f32) {
+    WrapInFunction(Var("f", Expr(1_f)),  //
+                   Var("v", Call("frexp", "f")));
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
     ASSERT_TRUE(gen.Generate()) << gen.error();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
-struct frexp_result {
+struct frexp_result_f32 {
   float fract;
   int exp;
 };
 
-frexp_result tint_frexp(float param_0) {
-  frexp_result result;
+frexp_result_f32 tint_frexp(float param_0) {
+  frexp_result_f32 result;
   result.fract = frexp(param_0, result.exp);
   return result;
 }
 
 
 void test_function() {
-  tint_frexp(1.0f);
+  float f = 1.0f;
+  frexp_result_f32 v = tint_frexp(f);
 }
 
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
@@ -701,11 +702,11 @@
 )");
 }
 
-TEST_F(GlslGeneratorImplTest_Builtin, Frexp_Scalar_f16) {
+TEST_F(GlslGeneratorImplTest_Builtin, Runtime_Frexp_Scalar_f16) {
     Enable(ast::Extension::kF16);
 
-    auto* call = Call("frexp", 1_h);
-    WrapInFunction(CallStmt(call));
+    WrapInFunction(Var("f", Expr(1_h)),  //
+                   Var("v", Call("frexp", "f")));
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
@@ -726,7 +727,8 @@
 
 
 void test_function() {
-  tint_frexp(1.0hf);
+  float16_t f = 1.0hf;
+  frexp_result_f16 v = tint_frexp(f);
 }
 
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
@@ -737,29 +739,30 @@
 )");
 }
 
-TEST_F(GlslGeneratorImplTest_Builtin, Frexp_Vector_f32) {
-    auto* call = Call("frexp", vec3<f32>());
-    WrapInFunction(CallStmt(call));
+TEST_F(GlslGeneratorImplTest_Builtin, Runtime_Frexp_Vector_f32) {
+    WrapInFunction(Var("f", Expr(vec3<f32>())),  //
+                   Var("v", Call("frexp", "f")));
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
     ASSERT_TRUE(gen.Generate()) << gen.error();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
-struct frexp_result_vec3 {
+struct frexp_result_vec3_f32 {
   vec3 fract;
   ivec3 exp;
 };
 
-frexp_result_vec3 tint_frexp(vec3 param_0) {
-  frexp_result_vec3 result;
+frexp_result_vec3_f32 tint_frexp(vec3 param_0) {
+  frexp_result_vec3_f32 result;
   result.fract = frexp(param_0, result.exp);
   return result;
 }
 
 
 void test_function() {
-  tint_frexp(vec3(0.0f));
+  vec3 f = vec3(0.0f);
+  frexp_result_vec3_f32 v = tint_frexp(f);
 }
 
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
@@ -770,11 +773,11 @@
 )");
 }
 
-TEST_F(GlslGeneratorImplTest_Builtin, Frexp_Vector_f16) {
+TEST_F(GlslGeneratorImplTest_Builtin, Runtime_Frexp_Vector_f16) {
     Enable(ast::Extension::kF16);
 
-    auto* call = Call("frexp", vec3<f16>());
-    WrapInFunction(CallStmt(call));
+    WrapInFunction(Var("f", Expr(vec3<f16>())),  //
+                   Var("v", Call("frexp", "f")));
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
@@ -795,7 +798,118 @@
 
 
 void test_function() {
-  tint_frexp(f16vec3(0.0hf));
+  f16vec3 f = f16vec3(0.0hf);
+  frexp_result_vec3_f16 v = tint_frexp(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_Frexp_Scalar_f32) {
+    WrapInFunction(Decl(Let("v", Call("frexp", 1_f))));
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+struct frexp_result_f32 {
+  float fract;
+  int exp;
+};
+
+
+void test_function() {
+  frexp_result_f32 v = frexp_result_f32(0.5f, 1);
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  test_function();
+  return;
+}
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_Builtin, Const_Frexp_Scalar_f16) {
+    Enable(ast::Extension::kF16);
+
+    WrapInFunction(Decl(Let("v", Call("frexp", 1_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 frexp_result_f16 {
+  float16_t fract;
+  int exp;
+};
+
+
+void test_function() {
+  frexp_result_f16 v = frexp_result_f16(0.5hf, 1);
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  test_function();
+  return;
+}
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_Builtin, Const_Frexp_Vector_f32) {
+    WrapInFunction(Decl(Let("v", Call("frexp", vec3<f32>()))));
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+struct frexp_result_vec3_f32 {
+  vec3 fract;
+  ivec3 exp;
+};
+
+
+void test_function() {
+  frexp_result_vec3_f32 v = frexp_result_vec3_f32(vec3(0.0f), ivec3(0));
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  test_function();
+  return;
+}
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_Builtin, Const_Frexp_Vector_f16) {
+    Enable(ast::Extension::kF16);
+
+    WrapInFunction(Decl(Let("v", Call("frexp", vec3<f16>()))));
+
+    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 frexp_result_vec3_f16 {
+  f16vec3 fract;
+  ivec3 exp;
+};
+
+
+void test_function() {
+  frexp_result_vec3_f16 v = frexp_result_vec3_f16(f16vec3(0.0hf), ivec3(0));
 }
 
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
@@ -815,20 +929,8 @@
     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;
+  float tint_symbol = 0.5f;
 }
 
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
diff --git a/src/tint/writer/hlsl/generator_impl.cc b/src/tint/writer/hlsl/generator_impl.cc
index dcad1b5..1e786da 100644
--- a/src/tint/writer/hlsl/generator_impl.cc
+++ b/src/tint/writer/hlsl/generator_impl.cc
@@ -1097,31 +1097,62 @@
     const auto& args = expr->args;
     auto* offset_arg = builder_.Sem().Get(args[1]);
 
-    uint32_t scalar_offset_value = 0;
-    std::string scalar_offset_expr;
+    // offset in bytes
+    uint32_t scalar_offset_bytes = 0;
+    // offset in uint (4 bytes)
+    uint32_t scalar_offset_index = 0;
+    // expression to calculate offset in bytes
+    std::string scalar_offset_bytes_expr;
+    // expression to calculate offset in uint, by dividing scalar_offset_bytes_expr by 4
+    std::string scalar_offset_index_expr;
+    // expression to calculate offset in uint, independently
+    std::string scalar_offset_index_unified_expr;
 
-    // If true, use scalar_offset_value, otherwise use scalar_offset_expr
+    // If true, use scalar_offset_index, otherwise use scalar_offset_index_expr
     bool scalar_offset_constant = false;
 
     if (auto* val = offset_arg->ConstantValue()) {
         TINT_ASSERT(Writer, val->Type()->Is<sem::U32>());
-        scalar_offset_value = static_cast<uint32_t>(std::get<AInt>(val->Value()));
-        scalar_offset_value /= 4;  // bytes -> scalar index
+        scalar_offset_bytes = static_cast<uint32_t>(std::get<AInt>(val->Value()));
+        scalar_offset_index = scalar_offset_bytes / 4;  // bytes -> scalar index
         scalar_offset_constant = true;
     }
 
+    // If true, scalar_offset_bytes or scalar_offset_bytes_expr should be used, otherwise only use
+    // scalar_offset_index or scalar_offset_index_unified_expr. Currently only loading f16 scalar
+    // require using offset in bytes.
+    const bool need_offset_in_bytes =
+        intrinsic->type == transform::DecomposeMemoryAccess::Intrinsic::DataType::kF16;
+
     if (!scalar_offset_constant) {
         // UBO offset not compile-time known.
         // Calculate the scalar offset into a temporary.
-        scalar_offset_expr = UniqueIdentifier("scalar_offset");
-        auto pre = line();
-        pre << "const uint " << scalar_offset_expr << " = (";
-        if (!EmitExpression(pre, args[1])) {  // offset
-            return false;
+        if (need_offset_in_bytes) {
+            scalar_offset_bytes_expr = UniqueIdentifier("scalar_offset_bytes");
+            scalar_offset_index_expr = UniqueIdentifier("scalar_offset_index");
+            {
+                auto pre = line();
+                pre << "const uint " << scalar_offset_bytes_expr << " = (";
+                if (!EmitExpression(pre, args[1])) {  // offset
+                    return false;
+                }
+                pre << ");";
+            }
+            line() << "const uint " << scalar_offset_index_expr << " = " << scalar_offset_bytes_expr
+                   << " / 4;";
+        } else {
+            scalar_offset_index_unified_expr = UniqueIdentifier("scalar_offset");
+            auto pre = line();
+            pre << "const uint " << scalar_offset_index_unified_expr << " = (";
+            if (!EmitExpression(pre, args[1])) {  // offset
+                return false;
+            }
+            pre << ") / 4;";
         }
-        pre << ") / 4;";
     }
 
+    constexpr const char swizzle[] = {'x', 'y', 'z', 'w'};
+
     using Op = transform::DecomposeMemoryAccess::Intrinsic::Op;
     using DataType = transform::DecomposeMemoryAccess::Intrinsic::DataType;
     switch (intrinsic->op) {
@@ -1132,27 +1163,28 @@
                 out << ")";
                 return result;
             };
-            auto load_scalar = [&]() {
-                if (!EmitExpression(out, args[0])) {  // buffer
+            auto load_u32_to = [&](std::ostream& target) {
+                if (!EmitExpression(target, args[0])) {  // buffer
                     return false;
                 }
                 if (scalar_offset_constant) {
-                    char swizzle[] = {'x', 'y', 'z', 'w'};
-                    out << "[" << (scalar_offset_value / 4) << "]."
-                        << swizzle[scalar_offset_value & 3];
+                    target << "[" << (scalar_offset_index / 4) << "]."
+                           << swizzle[scalar_offset_index & 3];
                 } else {
-                    out << "[" << scalar_offset_expr << " / 4][" << scalar_offset_expr << " % 4]";
+                    target << "[" << scalar_offset_index_unified_expr << " / 4]["
+                           << scalar_offset_index_unified_expr << " % 4]";
                 }
                 return true;
             };
+            auto load_u32 = [&] { return load_u32_to(out); };
             // Has a minimum alignment of 8 bytes, so is either .xy or .zw
-            auto load_vec2 = [&] {
+            auto load_vec2_u32_to = [&](std::ostream& target) {
                 if (scalar_offset_constant) {
-                    if (!EmitExpression(out, args[0])) {  // buffer
+                    if (!EmitExpression(target, args[0])) {  // buffer
                         return false;
                     }
-                    out << "[" << (scalar_offset_value / 4) << "]";
-                    out << ((scalar_offset_value & 2) == 0 ? ".xy" : ".zw");
+                    target << "[" << (scalar_offset_index / 4) << "]";
+                    target << ((scalar_offset_index & 2) == 0 ? ".xy" : ".zw");
                 } else {
                     std::string ubo_load = UniqueIdentifier("ubo_load");
                     {
@@ -1161,58 +1193,190 @@
                         if (!EmitExpression(pre, args[0])) {  // buffer
                             return false;
                         }
-                        pre << "[" << scalar_offset_expr << " / 4];";
+                        pre << "[" << scalar_offset_index_unified_expr << " / 4];";
                     }
-                    out << "((" << scalar_offset_expr << " & 2) ? " << ubo_load
-                        << ".zw : " << ubo_load << ".xy)";
+                    target << "((" << scalar_offset_index_unified_expr << " & 2) ? " << ubo_load
+                           << ".zw : " << ubo_load << ".xy)";
                 }
                 return true;
             };
+            auto load_vec2_u32 = [&] { return load_vec2_u32_to(out); };
             // vec4 has a minimum alignment of 16 bytes, easiest case
-            auto load_vec4 = [&] {
+            auto load_vec4_u32 = [&] {
                 if (!EmitExpression(out, args[0])) {  // buffer
                     return false;
                 }
                 if (scalar_offset_constant) {
-                    out << "[" << (scalar_offset_value / 4) << "]";
+                    out << "[" << (scalar_offset_index / 4) << "]";
                 } else {
-                    out << "[" << scalar_offset_expr << " / 4]";
+                    out << "[" << scalar_offset_index_unified_expr << " / 4]";
                 }
                 return true;
             };
             // vec3 has a minimum alignment of 16 bytes, so is just a .xyz swizzle
-            auto load_vec3 = [&] {
-                if (!load_vec4()) {
+            auto load_vec3_u32 = [&] {
+                if (!load_vec4_u32()) {
                     return false;
                 }
                 out << ".xyz";
                 return true;
             };
+            auto load_scalar_f16 = [&] {
+                // offset bytes = 4k,   ((buffer[index].x) & 0xFFFF)
+                // offset bytes = 4k+2, ((buffer[index].x >> 16) & 0xFFFF)
+                out << "float16_t(f16tof32(((";
+                if (!EmitExpression(out, args[0])) {  // buffer
+                    return false;
+                }
+                if (scalar_offset_constant) {
+                    out << "[" << (scalar_offset_index / 4) << "]."
+                        << swizzle[scalar_offset_index & 3];
+                    // WGSL spec ensure little endian memory layout.
+                    if (scalar_offset_bytes % 4 == 0) {
+                        out << ") & 0xFFFF)";
+                    } else {
+                        out << " >> 16) & 0xFFFF)";
+                    }
+                } else {
+                    out << "[" << scalar_offset_index_expr << " / 4][" << scalar_offset_index_expr
+                        << " % 4] >> (" << scalar_offset_bytes_expr
+                        << " % 4 == 0 ? 0 : 16)) & 0xFFFF)";
+                }
+                out << "))";
+                return true;
+            };
+            auto load_vec2_f16 = [&] {
+                // vec2<f16> is aligned to 4 bytes
+                // Preclude code load the vec2<f16> data as a uint:
+                //     uint ubo_load = buffer[id0][id1];
+                // Loading code convert it to vec2<f16>:
+                //     vector<float16_t, 2>(float16_t(f16tof32(ubo_load & 0xFFFF)),
+                //     float16_t(f16tof32(ubo_load >> 16)))
+                std::string ubo_load = UniqueIdentifier("ubo_load");
+                {
+                    auto pre = line();
+                    // Load the 4 bytes f16 vector as an uint
+                    pre << "uint " << ubo_load << " = ";
+                    if (!load_u32_to(pre)) {
+                        return false;
+                    }
+                    pre << ";";
+                }
+                out << "vector<float16_t, 2>(float16_t(f16tof32(" << ubo_load
+                    << " & 0xFFFF)), float16_t(f16tof32(" << ubo_load << " >> 16)))";
+                return true;
+            };
+            auto load_vec3_f16 = [&] {
+                // vec3<f16> is aligned to 8 bytes
+                // Preclude code load the vec3<f16> data as uint2 and convert its elements to
+                // float16_t:
+                //     uint2 ubo_load = buffer[id0].xy;
+                //     /* The low 8 bits of two uint are the x and z elements of vec3<f16> */
+                //     vector<float16_t> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load &
+                //     0xFFFF));
+                //     /* The high 8 bits of first uint is the y element of vec3<f16> */
+                //     float16_t ubo_load_y = f16tof32(ubo_load[0] >> 16);
+                // Loading code convert it to vec3<f16>:
+                //     vector<float16_t, 3>(ubo_load_xz[0], ubo_load_y, ubo_load_xz[1])
+                std::string ubo_load = UniqueIdentifier("ubo_load");
+                std::string ubo_load_xz = UniqueIdentifier(ubo_load + "_xz");
+                std::string ubo_load_y = UniqueIdentifier(ubo_load + "_y");
+                {
+                    auto pre = line();
+                    // Load the 8 bytes uint2 with the f16 vector at lower 6 bytes
+                    pre << "uint2 " << ubo_load << " = ";
+                    if (!load_vec2_u32_to(pre)) {
+                        return false;
+                    }
+                    pre << ";";
+                }
+                {
+                    auto pre = line();
+                    pre << "vector<float16_t, 2> " << ubo_load_xz
+                        << " = vector<float16_t, 2>(f16tof32(" << ubo_load << " & 0xFFFF));";
+                }
+                {
+                    auto pre = line();
+                    pre << "float16_t " << ubo_load_y << " = f16tof32(" << ubo_load
+                        << "[0] >> 16);";
+                }
+                out << "vector<float16_t, 3>(" << ubo_load_xz << "[0], " << ubo_load_y << ", "
+                    << ubo_load_xz << "[1])";
+                return true;
+            };
+            auto load_vec4_f16 = [&] {
+                // vec4<f16> is aligned to 8 bytes
+                // Preclude code load the vec4<f16> data as uint2 and convert its elements to
+                // float16_t:
+                //     uint2 ubo_load = buffer[id0].xy;
+                //     /* The low 8 bits of two uint are the x and z elements of vec4<f16> */
+                //     vector<float16_t> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load &
+                //     0xFFFF));
+                //     /* The high 8 bits of two uint are the y and w elements of vec4<f16> */
+                //     vector<float16_t, 2> ubo_load_yw = vector<float16_t, 2>(f16tof32(ubo_load >>
+                //     16));
+                // Loading code convert it to vec4<f16>:
+                //     vector<float16_t, 4>(ubo_load_xz[0], ubo_load_yw[0], ubo_load_xz[1],
+                //     ubo_load_yw[1])
+                std::string ubo_load = UniqueIdentifier("ubo_load");
+                std::string ubo_load_xz = UniqueIdentifier(ubo_load + "_xz");
+                std::string ubo_load_yw = UniqueIdentifier(ubo_load + "_yw");
+                {
+                    auto pre = line();
+                    // Load the 8 bytes f16 vector as an uint2
+                    pre << "uint2 " << ubo_load << " = ";
+                    if (!load_vec2_u32_to(pre)) {
+                        return false;
+                    }
+                    pre << ";";
+                }
+                {
+                    auto pre = line();
+                    pre << "vector<float16_t, 2> " << ubo_load_xz
+                        << " = vector<float16_t, 2>(f16tof32(" << ubo_load << " & 0xFFFF));";
+                }
+                {
+                    auto pre = line();
+                    pre << "vector<float16_t, 2> " << ubo_load_yw
+                        << " = vector<float16_t, 2>(f16tof32(" << ubo_load << " >> 16));";
+                }
+                out << "vector<float16_t, 4>(" << ubo_load_xz << "[0], " << ubo_load_yw << "[0], "
+                    << ubo_load_xz << "[1], " << ubo_load_yw << "[1])";
+                return true;
+            };
             switch (intrinsic->type) {
                 case DataType::kU32:
-                    return load_scalar();
+                    return load_u32();
                 case DataType::kF32:
-                    return cast("asfloat", load_scalar);
+                    return cast("asfloat", load_u32);
                 case DataType::kI32:
-                    return cast("asint", load_scalar);
+                    return cast("asint", load_u32);
+                case DataType::kF16:
+                    return load_scalar_f16();
                 case DataType::kVec2U32:
-                    return load_vec2();
+                    return load_vec2_u32();
                 case DataType::kVec2F32:
-                    return cast("asfloat", load_vec2);
+                    return cast("asfloat", load_vec2_u32);
                 case DataType::kVec2I32:
-                    return cast("asint", load_vec2);
+                    return cast("asint", load_vec2_u32);
+                case DataType::kVec2F16:
+                    return load_vec2_f16();
                 case DataType::kVec3U32:
-                    return load_vec3();
+                    return load_vec3_u32();
                 case DataType::kVec3F32:
-                    return cast("asfloat", load_vec3);
+                    return cast("asfloat", load_vec3_u32);
                 case DataType::kVec3I32:
-                    return cast("asint", load_vec3);
+                    return cast("asint", load_vec3_u32);
+                case DataType::kVec3F16:
+                    return load_vec3_f16();
                 case DataType::kVec4U32:
-                    return load_vec4();
+                    return load_vec4_u32();
                 case DataType::kVec4F32:
-                    return cast("asfloat", load_vec4);
+                    return cast("asfloat", load_vec4_u32);
                 case DataType::kVec4I32:
-                    return cast("asint", load_vec4);
+                    return cast("asint", load_vec4_u32);
+                case DataType::kVec4F16:
+                    return load_vec4_f16();
             }
             TINT_UNREACHABLE(Writer, diagnostics_)
                 << "unsupported DecomposeMemoryAccess::Intrinsic::DataType: "
@@ -1257,6 +1421,20 @@
                 }
                 return true;
             };
+            // Templated load used for f16 types, requires SM6.2 or higher and DXC
+            // Used by loading f16 types, e.g. for f16 type, set type parameter to "float16_t"
+            // to emit `buffer.Load<float16_t>(offset)`.
+            auto templated_load = [&](const char* type) {
+                if (!EmitExpression(out, args[0])) {  // buffer
+                    return false;
+                }
+                out << ".Load<" << type << ">";  // templated load
+                ScopedParen sp(out);
+                if (!EmitExpression(out, args[1])) {  // offset
+                    return false;
+                }
+                return true;
+            };
             switch (intrinsic->type) {
                 case DataType::kU32:
                     return load(nullptr, 1);
@@ -1264,24 +1442,32 @@
                     return load("asfloat", 1);
                 case DataType::kI32:
                     return load("asint", 1);
+                case DataType::kF16:
+                    return templated_load("float16_t");
                 case DataType::kVec2U32:
                     return load(nullptr, 2);
                 case DataType::kVec2F32:
                     return load("asfloat", 2);
                 case DataType::kVec2I32:
                     return load("asint", 2);
+                case DataType::kVec2F16:
+                    return templated_load("vector<float16_t, 2> ");
                 case DataType::kVec3U32:
                     return load(nullptr, 3);
                 case DataType::kVec3F32:
                     return load("asfloat", 3);
                 case DataType::kVec3I32:
                     return load("asint", 3);
+                case DataType::kVec3F16:
+                    return templated_load("vector<float16_t, 3> ");
                 case DataType::kVec4U32:
                     return load(nullptr, 4);
                 case DataType::kVec4F32:
                     return load("asfloat", 4);
                 case DataType::kVec4I32:
                     return load("asint", 4);
+                case DataType::kVec4F16:
+                    return templated_load("vector<float16_t, 4> ");
             }
             TINT_UNREACHABLE(Writer, diagnostics_)
                 << "unsupported DecomposeMemoryAccess::Intrinsic::DataType: "
@@ -1309,6 +1495,24 @@
                 }
                 return true;
             };
+            // Templated stored used for f16 types, requires SM6.2 or higher and DXC
+            // Used by storing f16 types, e.g. for f16 type, set type parameter to "float16_t"
+            // to emit `buffer.Store<float16_t>(offset)`.
+            auto templated_store = [&](const char* type) {
+                if (!EmitExpression(out, args[0])) {  // buffer
+                    return false;
+                }
+                out << ".Store<" << type << ">";  // templated store
+                ScopedParen sp1(out);
+                if (!EmitExpression(out, args[1])) {  // offset
+                    return false;
+                }
+                out << ", ";
+                if (!EmitExpression(out, args[2])) {  // value
+                    return false;
+                }
+                return true;
+            };
             switch (intrinsic->type) {
                 case DataType::kU32:
                     return store(1);
@@ -1316,24 +1520,32 @@
                     return store(1);
                 case DataType::kI32:
                     return store(1);
+                case DataType::kF16:
+                    return templated_store("float16_t");
                 case DataType::kVec2U32:
                     return store(2);
                 case DataType::kVec2F32:
                     return store(2);
                 case DataType::kVec2I32:
                     return store(2);
+                case DataType::kVec2F16:
+                    return templated_store("vector<float16_t, 2> ");
                 case DataType::kVec3U32:
                     return store(3);
                 case DataType::kVec3F32:
                     return store(3);
                 case DataType::kVec3I32:
                     return store(3);
+                case DataType::kVec3F16:
+                    return templated_store("vector<float16_t, 3> ");
                 case DataType::kVec4U32:
                     return store(4);
                 case DataType::kVec4F32:
                     return store(4);
                 case DataType::kVec4I32:
                     return store(4);
+                case DataType::kVec4F16:
+                    return templated_store("vector<float16_t, 4> ");
             }
             TINT_UNREACHABLE(Writer, diagnostics_)
                 << "unsupported DecomposeMemoryAccess::Intrinsic::DataType: "
diff --git a/src/tint/writer/hlsl/generator_impl_builtin_test.cc b/src/tint/writer/hlsl/generator_impl_builtin_test.cc
index 3e9b169..8b5fe07 100644
--- a/src/tint/writer/hlsl/generator_impl_builtin_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_builtin_test.cc
@@ -388,12 +388,12 @@
     GeneratorImpl& gen = SanitizeAndBuild();
 
     ASSERT_TRUE(gen.Generate()) << gen.error();
-    EXPECT_EQ(gen.result(), R"(struct modf_result {
+    EXPECT_EQ(gen.result(), R"(struct modf_result_f32 {
   float fract;
   float whole;
 };
-modf_result tint_modf(float param_0) {
-  modf_result result;
+modf_result_f32 tint_modf(float param_0) {
+  modf_result_f32 result;
   result.fract = modf(param_0, result.whole);
   return result;
 }
@@ -401,7 +401,7 @@
 [numthreads(1, 1, 1)]
 void test_function() {
   const float f = 1.5f;
-  const modf_result v = tint_modf(f);
+  const modf_result_f32 v = tint_modf(f);
   return;
 }
 )");
@@ -442,12 +442,12 @@
     GeneratorImpl& gen = SanitizeAndBuild();
 
     ASSERT_TRUE(gen.Generate()) << gen.error();
-    EXPECT_EQ(gen.result(), R"(struct modf_result_vec3 {
+    EXPECT_EQ(gen.result(), R"(struct modf_result_vec3_f32 {
   float3 fract;
   float3 whole;
 };
-modf_result_vec3 tint_modf(float3 param_0) {
-  modf_result_vec3 result;
+modf_result_vec3_f32 tint_modf(float3 param_0) {
+  modf_result_vec3_f32 result;
   result.fract = modf(param_0, result.whole);
   return result;
 }
@@ -455,7 +455,7 @@
 [numthreads(1, 1, 1)]
 void test_function() {
   const float3 f = float3(1.5f, 2.5f, 3.5f);
-  const modf_result_vec3 v = tint_modf(f);
+  const modf_result_vec3_f32 v = tint_modf(f);
   return;
 }
 )");
@@ -495,13 +495,13 @@
     GeneratorImpl& gen = SanitizeAndBuild();
 
     ASSERT_TRUE(gen.Generate()) << gen.error();
-    EXPECT_EQ(gen.result(), R"(struct modf_result {
+    EXPECT_EQ(gen.result(), R"(struct modf_result_f32 {
   float fract;
   float whole;
 };
 [numthreads(1, 1, 1)]
 void test_function() {
-  const modf_result v = {0.5f, 1.0f};
+  const modf_result_f32 v = {0.5f, 1.0f};
   return;
 }
 )");
@@ -533,13 +533,13 @@
     GeneratorImpl& gen = SanitizeAndBuild();
 
     ASSERT_TRUE(gen.Generate()) << gen.error();
-    EXPECT_EQ(gen.result(), R"(struct modf_result_vec3 {
+    EXPECT_EQ(gen.result(), R"(struct modf_result_vec3_f32 {
   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)};
+  const modf_result_vec3_f32 v = {(0.5f).xxx, float3(1.0f, 2.0f, 3.0f)};
   return;
 }
 )");
@@ -572,56 +572,57 @@
         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))));
+        Assign("v", Call("modf", vec3<f32>(4.5_a, 5.5_a, 6.5_a))));
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
     ASSERT_TRUE(gen.Generate()) << gen.error();
-    EXPECT_EQ(gen.result(), R"(struct modf_result_vec3 {
+    EXPECT_EQ(gen.result(), R"(struct modf_result_vec3_f32 {
   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)};
+  modf_result_vec3_f32 v = {(0.5f).xxx, float3(1.0f, 2.0f, 3.0f)};
+  const modf_result_vec3_f32 c = {(0.5f).xxx, float3(4.0f, 5.0f, 6.0f)};
   v = c;
   return;
 }
 )");
 }
 
-TEST_F(HlslGeneratorImplTest_Builtin, Frexp_Scalar_f32) {
-    auto* call = Call("frexp", 1_f);
-    WrapInFunction(CallStmt(call));
+TEST_F(HlslGeneratorImplTest_Builtin, Runtime_Frexp_Scalar_f32) {
+    WrapInFunction(Var("f", Expr(1_f)),  //
+                   Var("v", Call("frexp", "f")));
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
     ASSERT_TRUE(gen.Generate()) << gen.error();
-    EXPECT_EQ(gen.result(), R"(struct frexp_result {
+    EXPECT_EQ(gen.result(), R"(struct frexp_result_f32 {
   float fract;
   int exp;
 };
-frexp_result tint_frexp(float param_0) {
+frexp_result_f32 tint_frexp(float param_0) {
   float exp;
   float fract = frexp(param_0, exp);
-  frexp_result result = {fract, int(exp)};
+  frexp_result_f32 result = {fract, int(exp)};
   return result;
 }
 
 [numthreads(1, 1, 1)]
 void test_function() {
-  tint_frexp(1.0f);
+  float f = 1.0f;
+  frexp_result_f32 v = tint_frexp(f);
   return;
 }
 )");
 }
 
-TEST_F(HlslGeneratorImplTest_Builtin, Frexp_Scalar_f16) {
+TEST_F(HlslGeneratorImplTest_Builtin, Runtime_Frexp_Scalar_f16) {
     Enable(ast::Extension::kF16);
 
-    auto* call = Call("frexp", 1_h);
-    WrapInFunction(CallStmt(call));
+    WrapInFunction(Var("f", Expr(1_h)),  //
+                   Var("v", Call("frexp", "f")));
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
@@ -639,43 +640,45 @@
 
 [numthreads(1, 1, 1)]
 void test_function() {
-  tint_frexp(float16_t(1.0h));
+  float16_t f = float16_t(1.0h);
+  frexp_result_f16 v = tint_frexp(f);
   return;
 }
 )");
 }
 
-TEST_F(HlslGeneratorImplTest_Builtin, Frexp_Vector_f32) {
-    auto* call = Call("frexp", vec3<f32>());
-    WrapInFunction(CallStmt(call));
+TEST_F(HlslGeneratorImplTest_Builtin, Runtime_Frexp_Vector_f32) {
+    WrapInFunction(Var("f", Expr(vec3<f32>())),  //
+                   Var("v", Call("frexp", "f")));
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
     ASSERT_TRUE(gen.Generate()) << gen.error();
-    EXPECT_EQ(gen.result(), R"(struct frexp_result_vec3 {
+    EXPECT_EQ(gen.result(), R"(struct frexp_result_vec3_f32 {
   float3 fract;
   int3 exp;
 };
-frexp_result_vec3 tint_frexp(float3 param_0) {
+frexp_result_vec3_f32 tint_frexp(float3 param_0) {
   float3 exp;
   float3 fract = frexp(param_0, exp);
-  frexp_result_vec3 result = {fract, int3(exp)};
+  frexp_result_vec3_f32 result = {fract, int3(exp)};
   return result;
 }
 
 [numthreads(1, 1, 1)]
 void test_function() {
-  tint_frexp((0.0f).xxx);
+  float3 f = (0.0f).xxx;
+  frexp_result_vec3_f32 v = tint_frexp(f);
   return;
 }
 )");
 }
 
-TEST_F(HlslGeneratorImplTest_Builtin, Frexp_Vector_f16) {
+TEST_F(HlslGeneratorImplTest_Builtin, Runtime_Frexp_Vector_f16) {
     Enable(ast::Extension::kF16);
 
-    auto* call = Call("frexp", vec3<f16>());
-    WrapInFunction(CallStmt(call));
+    WrapInFunction(Var("f", Expr(vec3<f16>())),  //
+                   Var("v", Call("frexp", "f")));
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
@@ -693,7 +696,110 @@
 
 [numthreads(1, 1, 1)]
 void test_function() {
-  tint_frexp((float16_t(0.0h)).xxx);
+  vector<float16_t, 3> f = (float16_t(0.0h)).xxx;
+  frexp_result_vec3_f16 v = tint_frexp(f);
+  return;
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_Builtin, Const_Frexp_Scalar_f32) {
+    WrapInFunction(Decl(Let("v", Call("frexp", 1_f))));
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_EQ(gen.result(), R"(struct frexp_result_f32 {
+  float fract;
+  int exp;
+};
+[numthreads(1, 1, 1)]
+void test_function() {
+  const frexp_result_f32 v = {0.5f, 1};
+  return;
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_Builtin, Const_Frexp_Scalar_f16) {
+    Enable(ast::Extension::kF16);
+
+    WrapInFunction(Decl(Let("v", Call("frexp", 1_h))));
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_EQ(gen.result(), R"(struct frexp_result_f16 {
+  float16_t fract;
+  int exp;
+};
+[numthreads(1, 1, 1)]
+void test_function() {
+  const frexp_result_f16 v = {float16_t(0.5h), 1};
+  return;
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_Builtin, Const_Frexp_Vector_f32) {
+    WrapInFunction(Decl(Let("v", Call("frexp", vec3<f32>()))));
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_EQ(gen.result(), R"(struct frexp_result_vec3_f32 {
+  float3 fract;
+  int3 exp;
+};
+[numthreads(1, 1, 1)]
+void test_function() {
+  const frexp_result_vec3_f32 v = (frexp_result_vec3_f32)0;
+  return;
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_Builtin, Const_Frexp_Vector_f16) {
+    Enable(ast::Extension::kF16);
+
+    WrapInFunction(Decl(Let("v", Call("frexp", vec3<f16>()))));
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_EQ(gen.result(), R"(struct frexp_result_vec3_f16 {
+  vector<float16_t, 3> fract;
+  int3 exp;
+};
+[numthreads(1, 1, 1)]
+void test_function() {
+  const frexp_result_vec3_f16 v = (frexp_result_vec3_f16)0;
+  return;
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_Builtin, NonInitializer_Frexp_Vector_f32) {
+    WrapInFunction(
+        // Declare a variable with the result of a frexp call.
+        // This is required to infer the 'var' type.
+        Decl(Var("v", Call("frexp", vec3<f32>(1.5_f, 2.5_f, 3.5_f)))),
+        // Now assign 'v' again with another frexp call.
+        // This requires generating a temporary variable for the struct initializer.
+        Assign("v", Call("frexp", vec3<f32>(4.5_a, 5.5_a, 6.5_a))));
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_EQ(gen.result(), R"(struct frexp_result_vec3_f32 {
+  float3 fract;
+  int3 exp;
+};
+[numthreads(1, 1, 1)]
+void test_function() {
+  frexp_result_vec3_f32 v = {float3(0.75f, 0.625f, 0.875f), int3(1, 2, 2)};
+  const frexp_result_vec3_f32 c = {float3(0.5625f, 0.6875f, 0.8125f), (3).xxx};
+  v = c;
   return;
 }
 )");
@@ -701,25 +807,20 @@
 
 // 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"));
+    WrapInFunction(Var("v", Call("frexp", 1_f)),  //
+                   MemberAccessor("v", "sig"));
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
     ASSERT_TRUE(gen.Generate()) << gen.error();
-    EXPECT_EQ(gen.result(), R"(struct frexp_result {
+    EXPECT_EQ(gen.result(), R"(struct frexp_result_f32 {
   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;
+  frexp_result_f32 v = {0.5f, 1};
+  const float tint_symbol = v.fract;
   return;
 }
 )");
diff --git a/src/tint/writer/hlsl/generator_impl_member_accessor_test.cc b/src/tint/writer/hlsl/generator_impl_member_accessor_test.cc
index 1fbef9b..b3440b8 100644
--- a/src/tint/writer/hlsl/generator_impl_member_accessor_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_member_accessor_test.cc
@@ -34,6 +34,9 @@
 inline const ast::Type* ty_f32(const ProgramBuilder::TypesBuilder& ty) {
     return ty.f32();
 }
+inline const ast::Type* ty_f16(const ProgramBuilder::TypesBuilder& ty) {
+    return ty.f16();
+}
 template <typename T>
 inline const ast::Type* ty_vec2(const ProgramBuilder::TypesBuilder& ty) {
     return ty.vec2<T>();
@@ -94,6 +97,14 @@
                     b.Group(1_a), b.Binding(0_a));
     }
 
+    void SetupUniformBuffer(utils::VectorRef<const ast::StructMember*> members) {
+        ProgramBuilder& b = *this;
+        auto* s = b.Structure("Data", members);
+
+        b.GlobalVar("data", b.ty.Of(s), ast::AddressSpace::kUniform, ast::Access::kUndefined,
+                    b.Group(1_a), b.Binding(1_a));
+    }
+
     void SetupFunction(utils::VectorRef<const ast::Statement*> statements) {
         ProgramBuilder& b = *this;
         utils::Vector attrs{
@@ -144,18 +155,21 @@
     return out;
 }
 
-using HlslGeneratorImplTest_MemberAccessor_StorageBufferLoad =
+using HlslGeneratorImplTest_MemberAccessor_StorageBufferLoad_ConstantOffset =
     HlslGeneratorImplTest_MemberAccessorWithParam<TypeCase>;
-TEST_P(HlslGeneratorImplTest_MemberAccessor_StorageBufferLoad, Test) {
+
+TEST_P(HlslGeneratorImplTest_MemberAccessor_StorageBufferLoad_ConstantOffset, Test) {
     // struct Data {
-    //   a : i32;
-    //   b : <type>;
+    //   a : i32,
+    //   b : <type>,
     // };
     // var<storage> data : Data;
     // data.b;
 
     auto p = GetParam();
 
+    Enable(ast::Extension::kF16);
+
     SetupStorageBuffer(utils::Vector{
         Member("a", ty.i32()),
         Member("b", p.member_type(ty)),
@@ -173,60 +187,813 @@
 
 INSTANTIATE_TEST_SUITE_P(
     HlslGeneratorImplTest_MemberAccessor,
-    HlslGeneratorImplTest_MemberAccessor_StorageBufferLoad,
+    HlslGeneratorImplTest_MemberAccessor_StorageBufferLoad_ConstantOffset,
+    testing::Values(TypeCase{ty_u32, "data.Load(4u)"},
+                    TypeCase{ty_f32, "asfloat(data.Load(4u))"},
+                    TypeCase{ty_i32, "asint(data.Load(4u))"},
+                    TypeCase{ty_f16, "data.Load<float16_t>(4u)"},
+                    TypeCase{ty_vec2<u32>, "data.Load2(8u)"},
+                    TypeCase{ty_vec2<f32>, "asfloat(data.Load2(8u))"},
+                    TypeCase{ty_vec2<i32>, "asint(data.Load2(8u))"},
+                    TypeCase{ty_vec2<f16>, "data.Load<vector<float16_t, 2> >(4u)"},
+                    TypeCase{ty_vec3<u32>, "data.Load3(16u)"},
+                    TypeCase{ty_vec3<f32>, "asfloat(data.Load3(16u))"},
+                    TypeCase{ty_vec3<i32>, "asint(data.Load3(16u))"},
+                    TypeCase{ty_vec3<f16>, "data.Load<vector<float16_t, 3> >(8u)"},
+                    TypeCase{ty_vec4<u32>, "data.Load4(16u)"},
+                    TypeCase{ty_vec4<f32>, "asfloat(data.Load4(16u))"},
+                    TypeCase{ty_vec4<i32>, "asint(data.Load4(16u))"},
+                    TypeCase{ty_vec4<f16>, "data.Load<vector<float16_t, 4> >(8u)"},
+                    TypeCase{ty_mat2x2<f32>,
+                             "return float2x2(asfloat(buffer.Load2((offset + 0u))), "
+                             "asfloat(buffer.Load2((offset + 8u))));"},
+                    TypeCase{ty_mat2x3<f32>,
+                             "return float2x3(asfloat(buffer.Load3((offset + 0u))), "
+                             "asfloat(buffer.Load3((offset + 16u))));"},
+                    TypeCase{ty_mat2x4<f32>,
+                             "return float2x4(asfloat(buffer.Load4((offset + 0u))), "
+                             "asfloat(buffer.Load4((offset + 16u))));"},
+                    TypeCase{ty_mat3x2<f32>,
+                             "return float3x2(asfloat(buffer.Load2((offset + 0u))), "
+                             "asfloat(buffer.Load2((offset + 8u))), "
+                             "asfloat(buffer.Load2((offset + 16u))));"},
+                    TypeCase{ty_mat3x3<f32>,
+                             "return float3x3(asfloat(buffer.Load3((offset + 0u))), "
+                             "asfloat(buffer.Load3((offset + 16u))), "
+                             "asfloat(buffer.Load3((offset + 32u))));"},
+                    TypeCase{ty_mat3x4<f32>,
+                             "return float3x4(asfloat(buffer.Load4((offset + 0u))), "
+                             "asfloat(buffer.Load4((offset + 16u))), "
+                             "asfloat(buffer.Load4((offset + 32u))));"},
+                    TypeCase{ty_mat4x2<f32>,
+                             "return float4x2(asfloat(buffer.Load2((offset + 0u))), "
+                             "asfloat(buffer.Load2((offset + 8u))), "
+                             "asfloat(buffer.Load2((offset + 16u))), "
+                             "asfloat(buffer.Load2((offset + 24u))));"},
+                    TypeCase{ty_mat4x3<f32>,
+                             "return float4x3(asfloat(buffer.Load3((offset + 0u))), "
+                             "asfloat(buffer.Load3((offset + 16u))), "
+                             "asfloat(buffer.Load3((offset + 32u))), "
+                             "asfloat(buffer.Load3((offset + 48u))));"},
+                    TypeCase{ty_mat4x4<f32>,
+                             "return float4x4(asfloat(buffer.Load4((offset + 0u))), "
+                             "asfloat(buffer.Load4((offset + 16u))), "
+                             "asfloat(buffer.Load4((offset + 32u))), "
+                             "asfloat(buffer.Load4((offset + 48u))));"},
+                    TypeCase{ty_mat2x2<f16>,
+                             "return matrix<float16_t, 2, 2>("
+                             "buffer.Load<vector<float16_t, 2> >((offset + 0u)), "
+                             "buffer.Load<vector<float16_t, 2> >((offset + 4u)));"},
+                    TypeCase{ty_mat2x3<f16>,
+                             "return matrix<float16_t, 2, 3>("
+                             "buffer.Load<vector<float16_t, 3> >((offset + 0u)), "
+                             "buffer.Load<vector<float16_t, 3> >((offset + 8u)));"},
+                    TypeCase{ty_mat2x4<f16>,
+                             "return matrix<float16_t, 2, 4>("
+                             "buffer.Load<vector<float16_t, 4> >((offset + 0u)), "
+                             "buffer.Load<vector<float16_t, 4> >((offset + 8u)));"},
+                    TypeCase{ty_mat3x2<f16>,
+                             "return matrix<float16_t, 3, 2>("
+                             "buffer.Load<vector<float16_t, 2> >((offset + 0u)), "
+                             "buffer.Load<vector<float16_t, 2> >((offset + 4u)), "
+                             "buffer.Load<vector<float16_t, 2> >((offset + 8u)));"},
+                    TypeCase{ty_mat3x3<f16>,
+                             "return matrix<float16_t, 3, 3>("
+                             "buffer.Load<vector<float16_t, 3> >((offset + 0u)), "
+                             "buffer.Load<vector<float16_t, 3> >((offset + 8u)), "
+                             "buffer.Load<vector<float16_t, 3> >((offset + 16u)));"},
+                    TypeCase{ty_mat3x4<f16>,
+                             "return matrix<float16_t, 3, 4>("
+                             "buffer.Load<vector<float16_t, 4> >((offset + 0u)), "
+                             "buffer.Load<vector<float16_t, 4> >((offset + 8u)), "
+                             "buffer.Load<vector<float16_t, 4> >((offset + 16u)));"},
+                    TypeCase{ty_mat4x2<f16>,
+                             "return matrix<float16_t, 4, 2>("
+                             "buffer.Load<vector<float16_t, 2> >((offset + 0u)), "
+                             "buffer.Load<vector<float16_t, 2> >((offset + 4u)), "
+                             "buffer.Load<vector<float16_t, 2> >((offset + 8u)), "
+                             "buffer.Load<vector<float16_t, 2> >((offset + 12u)));"},
+                    TypeCase{ty_mat4x3<f16>,
+                             "return matrix<float16_t, 4, 3>("
+                             "buffer.Load<vector<float16_t, 3> >((offset + 0u)), "
+                             "buffer.Load<vector<float16_t, 3> >((offset + 8u)), "
+                             "buffer.Load<vector<float16_t, 3> >((offset + 16u)), "
+                             "buffer.Load<vector<float16_t, 3> >((offset + 24u)));"},
+                    TypeCase{ty_mat4x4<f16>,
+                             "return matrix<float16_t, 4, 4>("
+                             "buffer.Load<vector<float16_t, 4> >((offset + 0u)), "
+                             "buffer.Load<vector<float16_t, 4> >((offset + 8u)), "
+                             "buffer.Load<vector<float16_t, 4> >((offset + 16u)), "
+                             "buffer.Load<vector<float16_t, 4> >((offset + 24u)));"}));
+
+using HlslGeneratorImplTest_MemberAccessor_StorageBufferLoad_DynamicOffset =
+    HlslGeneratorImplTest_MemberAccessorWithParam<TypeCase>;
+
+TEST_P(HlslGeneratorImplTest_MemberAccessor_StorageBufferLoad_DynamicOffset, Test) {
+    // struct Inner {
+    //   a : i32,
+    //   b : <type>,
+    //   c : vec4<i32>,
+    // };
+    // struct Data {
+    //  arr : array<Inner, 4i>,
+    // }
+    // var<storage> data : Data;
+    // data.arr[i].b;
+
+    auto p = GetParam();
+
+    Enable(ast::Extension::kF16);
+
+    auto* inner = Structure("Inner", utils::Vector{
+                                         Member("a", ty.i32()),
+                                         Member("b", p.member_type(ty)),
+                                         Member("c", ty.vec4(ty.i32())),
+                                     });
+
+    SetupStorageBuffer(utils::Vector{
+        Member("arr", ty.array(ty.Of(inner), 4_i)),
+    });
+
+    auto* i = Var("i", Expr(2_i));
+
+    SetupFunction(utils::Vector{
+        Decl(i),
+        Decl(Var("x", MemberAccessor(IndexAccessor(MemberAccessor("data", "arr"), i), "b"))),
+    });
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_THAT(gen.result(), HasSubstr(p.expected));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    HlslGeneratorImplTest_MemberAccessor,
+    HlslGeneratorImplTest_MemberAccessor_StorageBufferLoad_DynamicOffset,
     testing::Values(
-        TypeCase{ty_u32, "data.Load(4u)"},
-        TypeCase{ty_f32, "asfloat(data.Load(4u))"},
-        TypeCase{ty_i32, "asint(data.Load(4u))"},
-        TypeCase{ty_vec2<u32>, "data.Load2(8u)"},
-        TypeCase{ty_vec2<f32>, "asfloat(data.Load2(8u))"},
-        TypeCase{ty_vec2<i32>, "asint(data.Load2(8u))"},
-        TypeCase{ty_vec3<u32>, "data.Load3(16u)"},
-        TypeCase{ty_vec3<f32>, "asfloat(data.Load3(16u))"},
-        TypeCase{ty_vec3<i32>, "asint(data.Load3(16u))"},
-        TypeCase{ty_vec4<u32>, "data.Load4(16u)"},
-        TypeCase{ty_vec4<f32>, "asfloat(data.Load4(16u))"},
-        TypeCase{ty_vec4<i32>, "asint(data.Load4(16u))"},
-        TypeCase{
-            ty_mat2x2<f32>,
-            R"(return float2x2(asfloat(buffer.Load2((offset + 0u))), asfloat(buffer.Load2((offset + 8u))));)"},
-        TypeCase{
-            ty_mat2x3<f32>,
-            R"(return float2x3(asfloat(buffer.Load3((offset + 0u))), asfloat(buffer.Load3((offset + 16u))));)"},
-        TypeCase{
-            ty_mat2x4<f32>,
-            R"(return float2x4(asfloat(buffer.Load4((offset + 0u))), asfloat(buffer.Load4((offset + 16u))));)"},
-        TypeCase{
-            ty_mat3x2<f32>,
-            R"(return float3x2(asfloat(buffer.Load2((offset + 0u))), asfloat(buffer.Load2((offset + 8u))), asfloat(buffer.Load2((offset + 16u))));)"},
-        TypeCase{
-            ty_mat3x3<f32>,
-            R"(return float3x3(asfloat(buffer.Load3((offset + 0u))), asfloat(buffer.Load3((offset + 16u))), asfloat(buffer.Load3((offset + 32u))));)"},
-        TypeCase{
-            ty_mat3x4<f32>,
-            R"(return float3x4(asfloat(buffer.Load4((offset + 0u))), asfloat(buffer.Load4((offset + 16u))), asfloat(buffer.Load4((offset + 32u))));)"},
-        TypeCase{
-            ty_mat4x2<f32>,
-            R"(return float4x2(asfloat(buffer.Load2((offset + 0u))), asfloat(buffer.Load2((offset + 8u))), asfloat(buffer.Load2((offset + 16u))), asfloat(buffer.Load2((offset + 24u))));)"},
-        TypeCase{
-            ty_mat4x3<f32>,
-            R"(return float4x3(asfloat(buffer.Load3((offset + 0u))), asfloat(buffer.Load3((offset + 16u))), asfloat(buffer.Load3((offset + 32u))), asfloat(buffer.Load3((offset + 48u))));)"},
-        TypeCase{
-            ty_mat4x4<f32>,
-            R"(return float4x4(asfloat(buffer.Load4((offset + 0u))), asfloat(buffer.Load4((offset + 16u))), asfloat(buffer.Load4((offset + 32u))), asfloat(buffer.Load4((offset + 48u))));)"}));
+        TypeCase{ty_u32, "data.Load(((32u * uint(i)) + 4u))"},
+        TypeCase{ty_f32, "asfloat(data.Load(((32u * uint(i)) + 4u)))"},
+        TypeCase{ty_i32, "asint(data.Load(((32u * uint(i)) + 4u)))"},
+        TypeCase{ty_f16, "data.Load<float16_t>(((32u * uint(i)) + 4u))"},
+        TypeCase{ty_vec2<u32>, "data.Load2(((32u * uint(i)) + 8u))"},
+        TypeCase{ty_vec2<f32>, "asfloat(data.Load2(((32u * uint(i)) + 8u)))"},
+        TypeCase{ty_vec2<i32>, "asint(data.Load2(((32u * uint(i)) + 8u)))"},
+        TypeCase{ty_vec2<f16>, "data.Load<vector<float16_t, 2> >(((32u * uint(i)) + 4u))"},
+        TypeCase{ty_vec3<u32>, "data.Load3(((48u * uint(i)) + 16u))"},
+        TypeCase{ty_vec3<f32>, "asfloat(data.Load3(((48u * uint(i)) + 16u)))"},
+        TypeCase{ty_vec3<i32>, "asint(data.Load3(((48u * uint(i)) + 16u)))"},
+        TypeCase{ty_vec3<f16>, "data.Load<vector<float16_t, 3> >(((32u * uint(i)) + 8u))"},
+        TypeCase{ty_vec4<u32>, "data.Load4(((48u * uint(i)) + 16u))"},
+        TypeCase{ty_vec4<f32>, "asfloat(data.Load4(((48u * uint(i)) + 16u)))"},
+        TypeCase{ty_vec4<i32>, "asint(data.Load4(((48u * uint(i)) + 16u)))"},
+        TypeCase{ty_vec4<f16>, "data.Load<vector<float16_t, 4> >(((32u * uint(i)) + 8u))"},
+        TypeCase{ty_mat2x2<f32>,
+                 "return float2x2(asfloat(buffer.Load2((offset + 0u))), "
+                 "asfloat(buffer.Load2((offset + 8u))));"},
+        TypeCase{ty_mat2x3<f32>,
+                 "return float2x3(asfloat(buffer.Load3((offset + 0u))), "
+                 "asfloat(buffer.Load3((offset + 16u))));"},
+        TypeCase{ty_mat2x4<f32>,
+                 "return float2x4(asfloat(buffer.Load4((offset + 0u))), "
+                 "asfloat(buffer.Load4((offset + 16u))));"},
+        TypeCase{ty_mat3x2<f32>,
+                 "return float3x2(asfloat(buffer.Load2((offset + 0u))), "
+                 "asfloat(buffer.Load2((offset + 8u))), "
+                 "asfloat(buffer.Load2((offset + 16u))));"},
+        TypeCase{ty_mat3x3<f32>,
+                 "return float3x3(asfloat(buffer.Load3((offset + 0u))), "
+                 "asfloat(buffer.Load3((offset + 16u))), "
+                 "asfloat(buffer.Load3((offset + 32u))));"},
+        TypeCase{ty_mat3x4<f32>,
+                 "return float3x4(asfloat(buffer.Load4((offset + 0u))), "
+                 "asfloat(buffer.Load4((offset + 16u))), "
+                 "asfloat(buffer.Load4((offset + 32u))));"},
+        TypeCase{ty_mat4x2<f32>,
+                 "return float4x2(asfloat(buffer.Load2((offset + 0u))), "
+                 "asfloat(buffer.Load2((offset + 8u))), "
+                 "asfloat(buffer.Load2((offset + 16u))), "
+                 "asfloat(buffer.Load2((offset + 24u))));"},
+        TypeCase{ty_mat4x3<f32>,
+                 "return float4x3(asfloat(buffer.Load3((offset + 0u))), "
+                 "asfloat(buffer.Load3((offset + 16u))), "
+                 "asfloat(buffer.Load3((offset + 32u))), "
+                 "asfloat(buffer.Load3((offset + 48u))));"},
+        TypeCase{ty_mat4x4<f32>,
+                 "return float4x4(asfloat(buffer.Load4((offset + 0u))), "
+                 "asfloat(buffer.Load4((offset + 16u))), "
+                 "asfloat(buffer.Load4((offset + 32u))), "
+                 "asfloat(buffer.Load4((offset + 48u))));"},
+        TypeCase{ty_mat2x2<f16>,
+                 "return matrix<float16_t, 2, 2>("
+                 "buffer.Load<vector<float16_t, 2> >((offset + 0u)), "
+                 "buffer.Load<vector<float16_t, 2> >((offset + 4u)));"},
+        TypeCase{ty_mat2x3<f16>,
+                 "return matrix<float16_t, 2, 3>("
+                 "buffer.Load<vector<float16_t, 3> >((offset + 0u)), "
+                 "buffer.Load<vector<float16_t, 3> >((offset + 8u)));"},
+        TypeCase{ty_mat2x4<f16>,
+                 "return matrix<float16_t, 2, 4>("
+                 "buffer.Load<vector<float16_t, 4> >((offset + 0u)), "
+                 "buffer.Load<vector<float16_t, 4> >((offset + 8u)));"},
+        TypeCase{ty_mat3x2<f16>,
+                 "return matrix<float16_t, 3, 2>("
+                 "buffer.Load<vector<float16_t, 2> >((offset + 0u)), "
+                 "buffer.Load<vector<float16_t, 2> >((offset + 4u)), "
+                 "buffer.Load<vector<float16_t, 2> >((offset + 8u)));"},
+        TypeCase{ty_mat3x3<f16>,
+                 "return matrix<float16_t, 3, 3>("
+                 "buffer.Load<vector<float16_t, 3> >((offset + 0u)), "
+                 "buffer.Load<vector<float16_t, 3> >((offset + 8u)), "
+                 "buffer.Load<vector<float16_t, 3> >((offset + 16u)));"},
+        TypeCase{ty_mat3x4<f16>,
+                 "return matrix<float16_t, 3, 4>("
+                 "buffer.Load<vector<float16_t, 4> >((offset + 0u)), "
+                 "buffer.Load<vector<float16_t, 4> >((offset + 8u)), "
+                 "buffer.Load<vector<float16_t, 4> >((offset + 16u)));"},
+        TypeCase{ty_mat4x2<f16>,
+                 "return matrix<float16_t, 4, 2>("
+                 "buffer.Load<vector<float16_t, 2> >((offset + 0u)), "
+                 "buffer.Load<vector<float16_t, 2> >((offset + 4u)), "
+                 "buffer.Load<vector<float16_t, 2> >((offset + 8u)), "
+                 "buffer.Load<vector<float16_t, 2> >((offset + 12u)));"},
+        TypeCase{ty_mat4x3<f16>,
+                 "return matrix<float16_t, 4, 3>("
+                 "buffer.Load<vector<float16_t, 3> >((offset + 0u)), "
+                 "buffer.Load<vector<float16_t, 3> >((offset + 8u)), "
+                 "buffer.Load<vector<float16_t, 3> >((offset + 16u)), "
+                 "buffer.Load<vector<float16_t, 3> >((offset + 24u)));"},
+        TypeCase{ty_mat4x4<f16>,
+                 "return matrix<float16_t, 4, 4>("
+                 "buffer.Load<vector<float16_t, 4> >((offset + 0u)), "
+                 "buffer.Load<vector<float16_t, 4> >((offset + 8u)), "
+                 "buffer.Load<vector<float16_t, 4> >((offset + 16u)), "
+                 "buffer.Load<vector<float16_t, 4> >((offset + 24u)));"}));
+
+using HlslGeneratorImplTest_MemberAccessor_UniformBufferLoad_ConstantOffset =
+    HlslGeneratorImplTest_MemberAccessorWithParam<TypeCase>;
+TEST_P(HlslGeneratorImplTest_MemberAccessor_UniformBufferLoad_ConstantOffset, Test) {
+    // struct Data {
+    //   a : i32,
+    //   b : <type>,
+    // };
+    // var<uniform> data : Data;
+    // data.b;
+
+    auto p = GetParam();
+
+    Enable(ast::Extension::kF16);
+
+    SetupUniformBuffer(utils::Vector{
+        Member("a", ty.i32()),
+        Member("b", p.member_type(ty)),
+    });
+
+    SetupFunction(utils::Vector{
+        Decl(Var("x", MemberAccessor("data", "b"))),
+    });
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_THAT(gen.result(), HasSubstr(p.expected));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    HlslGeneratorImplTest_MemberAccessor,
+    HlslGeneratorImplTest_MemberAccessor_UniformBufferLoad_ConstantOffset,
+    testing::Values(TypeCase{ty_u32, "uint x = data[0].y;"},
+                    TypeCase{ty_f32, "float x = asfloat(data[0].y);"},
+                    TypeCase{ty_i32, "int x = asint(data[0].y);"},
+                    TypeCase{ty_f16, "float16_t x = float16_t(f16tof32(((data[0].y) & 0xFFFF)));"},
+                    TypeCase{ty_vec2<u32>, "uint2 x = data[0].zw;"},
+                    TypeCase{ty_vec2<f32>, "float2 x = asfloat(data[0].zw);"},
+                    TypeCase{ty_vec2<i32>, "int2 x = asint(data[0].zw);"},
+                    TypeCase{ty_vec2<f16>, R"(uint ubo_load = data[0].y;
+  vector<float16_t, 2> x = vector<float16_t, 2>(float16_t(f16tof32(ubo_load & 0xFFFF)), float16_t(f16tof32(ubo_load >> 16)));)"},
+                    TypeCase{ty_vec3<u32>, "uint3 x = data[1].xyz;"},
+                    TypeCase{ty_vec3<f32>, "float3 x = asfloat(data[1].xyz);"},
+                    TypeCase{ty_vec3<i32>, "int3 x = asint(data[1].xyz);"},
+                    TypeCase{ty_vec3<f16>, R"(uint2 ubo_load = data[0].zw;
+  vector<float16_t, 2> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load & 0xFFFF));
+  float16_t ubo_load_y = f16tof32(ubo_load[0] >> 16);
+  vector<float16_t, 3> x = vector<float16_t, 3>(ubo_load_xz[0], ubo_load_y, ubo_load_xz[1]);)"},
+                    TypeCase{ty_vec4<u32>, "uint4 x = data[1];"},
+                    TypeCase{ty_vec4<f32>, "float4 x = asfloat(data[1]);"},
+                    TypeCase{ty_vec4<i32>, "int4 x = asint(data[1]);"},
+                    TypeCase{ty_vec4<f16>,
+                             R"(uint2 ubo_load = data[0].zw;
+  vector<float16_t, 2> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load & 0xFFFF));
+  vector<float16_t, 2> ubo_load_yw = vector<float16_t, 2>(f16tof32(ubo_load >> 16));
+  vector<float16_t, 4> x = vector<float16_t, 4>(ubo_load_xz[0], ubo_load_yw[0], ubo_load_xz[1], ubo_load_yw[1]);)"},
+                    TypeCase{ty_mat2x2<f32>, R"(float2x2 tint_symbol(uint4 buffer[2], uint offset) {
+  const uint scalar_offset = ((offset + 0u)) / 4;
+  uint4 ubo_load = buffer[scalar_offset / 4];
+  const uint scalar_offset_1 = ((offset + 8u)) / 4;
+  uint4 ubo_load_1 = buffer[scalar_offset_1 / 4];
+  return float2x2(asfloat(((scalar_offset & 2) ? ubo_load.zw : ubo_load.xy)), asfloat(((scalar_offset_1 & 2) ? ubo_load_1.zw : ubo_load_1.xy)));
+})"},
+                    TypeCase{ty_mat2x3<f32>, R"(float2x3 tint_symbol(uint4 buffer[3], uint offset) {
+  const uint scalar_offset = ((offset + 0u)) / 4;
+  const uint scalar_offset_1 = ((offset + 16u)) / 4;
+  return float2x3(asfloat(buffer[scalar_offset / 4].xyz), asfloat(buffer[scalar_offset_1 / 4].xyz));
+})"},
+                    TypeCase{ty_mat2x4<f32>, R"(float2x4 tint_symbol(uint4 buffer[3], uint offset) {
+  const uint scalar_offset = ((offset + 0u)) / 4;
+  const uint scalar_offset_1 = ((offset + 16u)) / 4;
+  return float2x4(asfloat(buffer[scalar_offset / 4]), asfloat(buffer[scalar_offset_1 / 4]));
+})"},
+                    TypeCase{ty_mat3x2<f32>, R"(float3x2 tint_symbol(uint4 buffer[2], uint offset) {
+  const uint scalar_offset = ((offset + 0u)) / 4;
+  uint4 ubo_load = buffer[scalar_offset / 4];
+  const uint scalar_offset_1 = ((offset + 8u)) / 4;
+  uint4 ubo_load_1 = buffer[scalar_offset_1 / 4];
+  const uint scalar_offset_2 = ((offset + 16u)) / 4;
+  uint4 ubo_load_2 = buffer[scalar_offset_2 / 4];
+  return float3x2(asfloat(((scalar_offset & 2) ? ubo_load.zw : ubo_load.xy)), asfloat(((scalar_offset_1 & 2) ? ubo_load_1.zw : ubo_load_1.xy)), asfloat(((scalar_offset_2 & 2) ? ubo_load_2.zw : ubo_load_2.xy)));
+})"},
+                    TypeCase{ty_mat3x3<f32>, R"(float3x3 tint_symbol(uint4 buffer[4], uint offset) {
+  const uint scalar_offset = ((offset + 0u)) / 4;
+  const uint scalar_offset_1 = ((offset + 16u)) / 4;
+  const uint scalar_offset_2 = ((offset + 32u)) / 4;
+  return float3x3(asfloat(buffer[scalar_offset / 4].xyz), asfloat(buffer[scalar_offset_1 / 4].xyz), asfloat(buffer[scalar_offset_2 / 4].xyz));
+})"},
+                    TypeCase{ty_mat3x4<f32>, R"(float3x4 tint_symbol(uint4 buffer[4], uint offset) {
+  const uint scalar_offset = ((offset + 0u)) / 4;
+  const uint scalar_offset_1 = ((offset + 16u)) / 4;
+  const uint scalar_offset_2 = ((offset + 32u)) / 4;
+  return float3x4(asfloat(buffer[scalar_offset / 4]), asfloat(buffer[scalar_offset_1 / 4]), asfloat(buffer[scalar_offset_2 / 4]));
+})"},
+                    TypeCase{ty_mat4x2<f32>, R"(float4x2 tint_symbol(uint4 buffer[3], uint offset) {
+  const uint scalar_offset = ((offset + 0u)) / 4;
+  uint4 ubo_load = buffer[scalar_offset / 4];
+  const uint scalar_offset_1 = ((offset + 8u)) / 4;
+  uint4 ubo_load_1 = buffer[scalar_offset_1 / 4];
+  const uint scalar_offset_2 = ((offset + 16u)) / 4;
+  uint4 ubo_load_2 = buffer[scalar_offset_2 / 4];
+  const uint scalar_offset_3 = ((offset + 24u)) / 4;
+  uint4 ubo_load_3 = buffer[scalar_offset_3 / 4];
+  return float4x2(asfloat(((scalar_offset & 2) ? ubo_load.zw : ubo_load.xy)), asfloat(((scalar_offset_1 & 2) ? ubo_load_1.zw : ubo_load_1.xy)), asfloat(((scalar_offset_2 & 2) ? ubo_load_2.zw : ubo_load_2.xy)), asfloat(((scalar_offset_3 & 2) ? ubo_load_3.zw : ubo_load_3.xy)));
+})"},
+                    TypeCase{ty_mat4x3<f32>, R"(float4x3 tint_symbol(uint4 buffer[5], uint offset) {
+  const uint scalar_offset = ((offset + 0u)) / 4;
+  const uint scalar_offset_1 = ((offset + 16u)) / 4;
+  const uint scalar_offset_2 = ((offset + 32u)) / 4;
+  const uint scalar_offset_3 = ((offset + 48u)) / 4;
+  return float4x3(asfloat(buffer[scalar_offset / 4].xyz), asfloat(buffer[scalar_offset_1 / 4].xyz), asfloat(buffer[scalar_offset_2 / 4].xyz), asfloat(buffer[scalar_offset_3 / 4].xyz));
+})"},
+                    TypeCase{ty_mat4x4<f32>, R"(float4x4 tint_symbol(uint4 buffer[5], uint offset) {
+  const uint scalar_offset = ((offset + 0u)) / 4;
+  const uint scalar_offset_1 = ((offset + 16u)) / 4;
+  const uint scalar_offset_2 = ((offset + 32u)) / 4;
+  const uint scalar_offset_3 = ((offset + 48u)) / 4;
+  return float4x4(asfloat(buffer[scalar_offset / 4]), asfloat(buffer[scalar_offset_1 / 4]), asfloat(buffer[scalar_offset_2 / 4]), asfloat(buffer[scalar_offset_3 / 4]));
+})"},
+                    TypeCase{ty_mat2x2<f16>,
+                             R"(matrix<float16_t, 2, 2> tint_symbol(uint4 buffer[1], uint offset) {
+  const uint scalar_offset = ((offset + 0u)) / 4;
+  uint ubo_load = buffer[scalar_offset / 4][scalar_offset % 4];
+  const uint scalar_offset_1 = ((offset + 4u)) / 4;
+  uint ubo_load_1 = buffer[scalar_offset_1 / 4][scalar_offset_1 % 4];
+  return matrix<float16_t, 2, 2>(vector<float16_t, 2>(float16_t(f16tof32(ubo_load & 0xFFFF)), float16_t(f16tof32(ubo_load >> 16))), vector<float16_t, 2>(float16_t(f16tof32(ubo_load_1 & 0xFFFF)), float16_t(f16tof32(ubo_load_1 >> 16))));
+})"},
+                    TypeCase{ty_mat2x3<f16>,
+                             R"(matrix<float16_t, 2, 3> tint_symbol(uint4 buffer[2], uint offset) {
+  const uint scalar_offset = ((offset + 0u)) / 4;
+  uint4 ubo_load_1 = buffer[scalar_offset / 4];
+  uint2 ubo_load = ((scalar_offset & 2) ? ubo_load_1.zw : ubo_load_1.xy);
+  vector<float16_t, 2> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load & 0xFFFF));
+  float16_t ubo_load_y = f16tof32(ubo_load[0] >> 16);
+  const uint scalar_offset_1 = ((offset + 8u)) / 4;
+  uint4 ubo_load_3 = buffer[scalar_offset_1 / 4];
+  uint2 ubo_load_2 = ((scalar_offset_1 & 2) ? ubo_load_3.zw : ubo_load_3.xy);
+  vector<float16_t, 2> ubo_load_2_xz = vector<float16_t, 2>(f16tof32(ubo_load_2 & 0xFFFF));
+  float16_t ubo_load_2_y = f16tof32(ubo_load_2[0] >> 16);
+  return matrix<float16_t, 2, 3>(vector<float16_t, 3>(ubo_load_xz[0], ubo_load_y, ubo_load_xz[1]), vector<float16_t, 3>(ubo_load_2_xz[0], ubo_load_2_y, ubo_load_2_xz[1]));
+})"},
+                    TypeCase{ty_mat2x4<f16>,
+                             R"(matrix<float16_t, 2, 4> tint_symbol(uint4 buffer[2], uint offset) {
+  const uint scalar_offset = ((offset + 0u)) / 4;
+  uint4 ubo_load_1 = buffer[scalar_offset / 4];
+  uint2 ubo_load = ((scalar_offset & 2) ? ubo_load_1.zw : ubo_load_1.xy);
+  vector<float16_t, 2> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load & 0xFFFF));
+  vector<float16_t, 2> ubo_load_yw = vector<float16_t, 2>(f16tof32(ubo_load >> 16));
+  const uint scalar_offset_1 = ((offset + 8u)) / 4;
+  uint4 ubo_load_3 = buffer[scalar_offset_1 / 4];
+  uint2 ubo_load_2 = ((scalar_offset_1 & 2) ? ubo_load_3.zw : ubo_load_3.xy);
+  vector<float16_t, 2> ubo_load_2_xz = vector<float16_t, 2>(f16tof32(ubo_load_2 & 0xFFFF));
+  vector<float16_t, 2> ubo_load_2_yw = vector<float16_t, 2>(f16tof32(ubo_load_2 >> 16));
+  return matrix<float16_t, 2, 4>(vector<float16_t, 4>(ubo_load_xz[0], ubo_load_yw[0], ubo_load_xz[1], ubo_load_yw[1]), vector<float16_t, 4>(ubo_load_2_xz[0], ubo_load_2_yw[0], ubo_load_2_xz[1], ubo_load_2_yw[1]));
+})"},
+                    TypeCase{ty_mat3x2<f16>,
+                             R"(matrix<float16_t, 3, 2> tint_symbol(uint4 buffer[1], uint offset) {
+  const uint scalar_offset = ((offset + 0u)) / 4;
+  uint ubo_load = buffer[scalar_offset / 4][scalar_offset % 4];
+  const uint scalar_offset_1 = ((offset + 4u)) / 4;
+  uint ubo_load_1 = buffer[scalar_offset_1 / 4][scalar_offset_1 % 4];
+  const uint scalar_offset_2 = ((offset + 8u)) / 4;
+  uint ubo_load_2 = buffer[scalar_offset_2 / 4][scalar_offset_2 % 4];
+  return matrix<float16_t, 3, 2>(vector<float16_t, 2>(float16_t(f16tof32(ubo_load & 0xFFFF)), float16_t(f16tof32(ubo_load >> 16))), vector<float16_t, 2>(float16_t(f16tof32(ubo_load_1 & 0xFFFF)), float16_t(f16tof32(ubo_load_1 >> 16))), vector<float16_t, 2>(float16_t(f16tof32(ubo_load_2 & 0xFFFF)), float16_t(f16tof32(ubo_load_2 >> 16))));
+})"},
+                    TypeCase{ty_mat3x3<f16>,
+                             R"(matrix<float16_t, 3, 3> tint_symbol(uint4 buffer[2], uint offset) {
+  const uint scalar_offset = ((offset + 0u)) / 4;
+  uint4 ubo_load_1 = buffer[scalar_offset / 4];
+  uint2 ubo_load = ((scalar_offset & 2) ? ubo_load_1.zw : ubo_load_1.xy);
+  vector<float16_t, 2> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load & 0xFFFF));
+  float16_t ubo_load_y = f16tof32(ubo_load[0] >> 16);
+  const uint scalar_offset_1 = ((offset + 8u)) / 4;
+  uint4 ubo_load_3 = buffer[scalar_offset_1 / 4];
+  uint2 ubo_load_2 = ((scalar_offset_1 & 2) ? ubo_load_3.zw : ubo_load_3.xy);
+  vector<float16_t, 2> ubo_load_2_xz = vector<float16_t, 2>(f16tof32(ubo_load_2 & 0xFFFF));
+  float16_t ubo_load_2_y = f16tof32(ubo_load_2[0] >> 16);
+  const uint scalar_offset_2 = ((offset + 16u)) / 4;
+  uint4 ubo_load_5 = buffer[scalar_offset_2 / 4];
+  uint2 ubo_load_4 = ((scalar_offset_2 & 2) ? ubo_load_5.zw : ubo_load_5.xy);
+  vector<float16_t, 2> ubo_load_4_xz = vector<float16_t, 2>(f16tof32(ubo_load_4 & 0xFFFF));
+  float16_t ubo_load_4_y = f16tof32(ubo_load_4[0] >> 16);
+  return matrix<float16_t, 3, 3>(vector<float16_t, 3>(ubo_load_xz[0], ubo_load_y, ubo_load_xz[1]), vector<float16_t, 3>(ubo_load_2_xz[0], ubo_load_2_y, ubo_load_2_xz[1]), vector<float16_t, 3>(ubo_load_4_xz[0], ubo_load_4_y, ubo_load_4_xz[1]));
+})"},
+                    TypeCase{ty_mat3x4<f16>,
+                             R"(matrix<float16_t, 3, 4> tint_symbol(uint4 buffer[2], uint offset) {
+  const uint scalar_offset = ((offset + 0u)) / 4;
+  uint4 ubo_load_1 = buffer[scalar_offset / 4];
+  uint2 ubo_load = ((scalar_offset & 2) ? ubo_load_1.zw : ubo_load_1.xy);
+  vector<float16_t, 2> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load & 0xFFFF));
+  vector<float16_t, 2> ubo_load_yw = vector<float16_t, 2>(f16tof32(ubo_load >> 16));
+  const uint scalar_offset_1 = ((offset + 8u)) / 4;
+  uint4 ubo_load_3 = buffer[scalar_offset_1 / 4];
+  uint2 ubo_load_2 = ((scalar_offset_1 & 2) ? ubo_load_3.zw : ubo_load_3.xy);
+  vector<float16_t, 2> ubo_load_2_xz = vector<float16_t, 2>(f16tof32(ubo_load_2 & 0xFFFF));
+  vector<float16_t, 2> ubo_load_2_yw = vector<float16_t, 2>(f16tof32(ubo_load_2 >> 16));
+  const uint scalar_offset_2 = ((offset + 16u)) / 4;
+  uint4 ubo_load_5 = buffer[scalar_offset_2 / 4];
+  uint2 ubo_load_4 = ((scalar_offset_2 & 2) ? ubo_load_5.zw : ubo_load_5.xy);
+  vector<float16_t, 2> ubo_load_4_xz = vector<float16_t, 2>(f16tof32(ubo_load_4 & 0xFFFF));
+  vector<float16_t, 2> ubo_load_4_yw = vector<float16_t, 2>(f16tof32(ubo_load_4 >> 16));
+  return matrix<float16_t, 3, 4>(vector<float16_t, 4>(ubo_load_xz[0], ubo_load_yw[0], ubo_load_xz[1], ubo_load_yw[1]), vector<float16_t, 4>(ubo_load_2_xz[0], ubo_load_2_yw[0], ubo_load_2_xz[1], ubo_load_2_yw[1]), vector<float16_t, 4>(ubo_load_4_xz[0], ubo_load_4_yw[0], ubo_load_4_xz[1], ubo_load_4_yw[1]));)"},
+                    TypeCase{ty_mat4x2<f16>,
+                             R"(matrix<float16_t, 4, 2> tint_symbol(uint4 buffer[2], uint offset) {
+  const uint scalar_offset = ((offset + 0u)) / 4;
+  uint ubo_load = buffer[scalar_offset / 4][scalar_offset % 4];
+  const uint scalar_offset_1 = ((offset + 4u)) / 4;
+  uint ubo_load_1 = buffer[scalar_offset_1 / 4][scalar_offset_1 % 4];
+  const uint scalar_offset_2 = ((offset + 8u)) / 4;
+  uint ubo_load_2 = buffer[scalar_offset_2 / 4][scalar_offset_2 % 4];
+  const uint scalar_offset_3 = ((offset + 12u)) / 4;
+  uint ubo_load_3 = buffer[scalar_offset_3 / 4][scalar_offset_3 % 4];
+  return matrix<float16_t, 4, 2>(vector<float16_t, 2>(float16_t(f16tof32(ubo_load & 0xFFFF)), float16_t(f16tof32(ubo_load >> 16))), vector<float16_t, 2>(float16_t(f16tof32(ubo_load_1 & 0xFFFF)), float16_t(f16tof32(ubo_load_1 >> 16))), vector<float16_t, 2>(float16_t(f16tof32(ubo_load_2 & 0xFFFF)), float16_t(f16tof32(ubo_load_2 >> 16))), vector<float16_t, 2>(float16_t(f16tof32(ubo_load_3 & 0xFFFF)), float16_t(f16tof32(ubo_load_3 >> 16))));
+})"},
+                    TypeCase{ty_mat4x3<f16>,
+                             R"(matrix<float16_t, 4, 3> tint_symbol(uint4 buffer[3], uint offset) {
+  const uint scalar_offset = ((offset + 0u)) / 4;
+  uint4 ubo_load_1 = buffer[scalar_offset / 4];
+  uint2 ubo_load = ((scalar_offset & 2) ? ubo_load_1.zw : ubo_load_1.xy);
+  vector<float16_t, 2> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load & 0xFFFF));
+  float16_t ubo_load_y = f16tof32(ubo_load[0] >> 16);
+  const uint scalar_offset_1 = ((offset + 8u)) / 4;
+  uint4 ubo_load_3 = buffer[scalar_offset_1 / 4];
+  uint2 ubo_load_2 = ((scalar_offset_1 & 2) ? ubo_load_3.zw : ubo_load_3.xy);
+  vector<float16_t, 2> ubo_load_2_xz = vector<float16_t, 2>(f16tof32(ubo_load_2 & 0xFFFF));
+  float16_t ubo_load_2_y = f16tof32(ubo_load_2[0] >> 16);
+  const uint scalar_offset_2 = ((offset + 16u)) / 4;
+  uint4 ubo_load_5 = buffer[scalar_offset_2 / 4];
+  uint2 ubo_load_4 = ((scalar_offset_2 & 2) ? ubo_load_5.zw : ubo_load_5.xy);
+  vector<float16_t, 2> ubo_load_4_xz = vector<float16_t, 2>(f16tof32(ubo_load_4 & 0xFFFF));
+  float16_t ubo_load_4_y = f16tof32(ubo_load_4[0] >> 16);
+  const uint scalar_offset_3 = ((offset + 24u)) / 4;
+  uint4 ubo_load_7 = buffer[scalar_offset_3 / 4];
+  uint2 ubo_load_6 = ((scalar_offset_3 & 2) ? ubo_load_7.zw : ubo_load_7.xy);
+  vector<float16_t, 2> ubo_load_6_xz = vector<float16_t, 2>(f16tof32(ubo_load_6 & 0xFFFF));
+  float16_t ubo_load_6_y = f16tof32(ubo_load_6[0] >> 16);
+  return matrix<float16_t, 4, 3>(vector<float16_t, 3>(ubo_load_xz[0], ubo_load_y, ubo_load_xz[1]), vector<float16_t, 3>(ubo_load_2_xz[0], ubo_load_2_y, ubo_load_2_xz[1]), vector<float16_t, 3>(ubo_load_4_xz[0], ubo_load_4_y, ubo_load_4_xz[1]), vector<float16_t, 3>(ubo_load_6_xz[0], ubo_load_6_y, ubo_load_6_xz[1]));
+})"},
+                    TypeCase{ty_mat4x4<f16>,
+                             R"(matrix<float16_t, 4, 4> tint_symbol(uint4 buffer[3], uint offset) {
+  const uint scalar_offset = ((offset + 0u)) / 4;
+  uint4 ubo_load_1 = buffer[scalar_offset / 4];
+  uint2 ubo_load = ((scalar_offset & 2) ? ubo_load_1.zw : ubo_load_1.xy);
+  vector<float16_t, 2> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load & 0xFFFF));
+  vector<float16_t, 2> ubo_load_yw = vector<float16_t, 2>(f16tof32(ubo_load >> 16));
+  const uint scalar_offset_1 = ((offset + 8u)) / 4;
+  uint4 ubo_load_3 = buffer[scalar_offset_1 / 4];
+  uint2 ubo_load_2 = ((scalar_offset_1 & 2) ? ubo_load_3.zw : ubo_load_3.xy);
+  vector<float16_t, 2> ubo_load_2_xz = vector<float16_t, 2>(f16tof32(ubo_load_2 & 0xFFFF));
+  vector<float16_t, 2> ubo_load_2_yw = vector<float16_t, 2>(f16tof32(ubo_load_2 >> 16));
+  const uint scalar_offset_2 = ((offset + 16u)) / 4;
+  uint4 ubo_load_5 = buffer[scalar_offset_2 / 4];
+  uint2 ubo_load_4 = ((scalar_offset_2 & 2) ? ubo_load_5.zw : ubo_load_5.xy);
+  vector<float16_t, 2> ubo_load_4_xz = vector<float16_t, 2>(f16tof32(ubo_load_4 & 0xFFFF));
+  vector<float16_t, 2> ubo_load_4_yw = vector<float16_t, 2>(f16tof32(ubo_load_4 >> 16));
+  const uint scalar_offset_3 = ((offset + 24u)) / 4;
+  uint4 ubo_load_7 = buffer[scalar_offset_3 / 4];
+  uint2 ubo_load_6 = ((scalar_offset_3 & 2) ? ubo_load_7.zw : ubo_load_7.xy);
+  vector<float16_t, 2> ubo_load_6_xz = vector<float16_t, 2>(f16tof32(ubo_load_6 & 0xFFFF));
+  vector<float16_t, 2> ubo_load_6_yw = vector<float16_t, 2>(f16tof32(ubo_load_6 >> 16));
+  return matrix<float16_t, 4, 4>(vector<float16_t, 4>(ubo_load_xz[0], ubo_load_yw[0], ubo_load_xz[1], ubo_load_yw[1]), vector<float16_t, 4>(ubo_load_2_xz[0], ubo_load_2_yw[0], ubo_load_2_xz[1], ubo_load_2_yw[1]), vector<float16_t, 4>(ubo_load_4_xz[0], ubo_load_4_yw[0], ubo_load_4_xz[1], ubo_load_4_yw[1]), vector<float16_t, 4>(ubo_load_6_xz[0], ubo_load_6_yw[0], ubo_load_6_xz[1], ubo_load_6_yw[1]));
+})"}));
+
+using HlslGeneratorImplTest_MemberAccessor_UniformBufferLoad_DynamicOffset =
+    HlslGeneratorImplTest_MemberAccessorWithParam<TypeCase>;
+
+TEST_P(HlslGeneratorImplTest_MemberAccessor_UniformBufferLoad_DynamicOffset, Test) {
+    // struct Inner {
+    //   a : i32,
+    //   b : <type>,
+    //   c : vec4<i32>,
+    // };
+    // struct Data {
+    //  arr : array<Inner, 4i>,
+    // }
+    // var<uniform> data : Data;
+    // data.arr[i].b;
+
+    auto p = GetParam();
+
+    Enable(ast::Extension::kF16);
+
+    auto* inner = Structure("Inner", utils::Vector{
+                                         Member("a", ty.i32()),
+                                         Member("b", p.member_type(ty)),
+                                         Member("c", ty.vec4(ty.i32())),
+                                     });
+
+    SetupUniformBuffer(utils::Vector{
+        Member("arr", ty.array(ty.Of(inner), 4_i)),
+    });
+
+    auto* i = Var("i", Expr(2_i));
+
+    SetupFunction(utils::Vector{
+        Decl(i),
+        Decl(Var("x", MemberAccessor(IndexAccessor(MemberAccessor("data", "arr"), i), "b"))),
+    });
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_THAT(gen.result(), HasSubstr(p.expected));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    HlslGeneratorImplTest_MemberAccessor,
+    HlslGeneratorImplTest_MemberAccessor_UniformBufferLoad_DynamicOffset,
+    testing::Values(
+        TypeCase{ty_u32, "x = data[scalar_offset / 4][scalar_offset % 4]"},
+        TypeCase{ty_f32, "x = asfloat(data[scalar_offset / 4][scalar_offset % 4])"},
+        TypeCase{ty_i32, "x = asint(data[scalar_offset / 4][scalar_offset % 4])"},
+        TypeCase{ty_f16, R"(const uint scalar_offset_bytes = (((32u * uint(i)) + 4u));
+  const uint scalar_offset_index = scalar_offset_bytes / 4;
+  float16_t x = float16_t(f16tof32(((data[scalar_offset_index / 4][scalar_offset_index % 4] >> (scalar_offset_bytes % 4 == 0 ? 0 : 16)) & 0xFFFF)));)"},
+        TypeCase{ty_vec2<u32>, R"(uint4 ubo_load = data[scalar_offset / 4];
+  uint2 x = ((scalar_offset & 2) ? ubo_load.zw : ubo_load.xy);)"},
+        TypeCase{ty_vec2<f32>, R"(uint4 ubo_load = data[scalar_offset / 4];
+  float2 x = asfloat(((scalar_offset & 2) ? ubo_load.zw : ubo_load.xy));)"},
+        TypeCase{ty_vec2<i32>, R"(uint4 ubo_load = data[scalar_offset / 4];
+  int2 x = asint(((scalar_offset & 2) ? ubo_load.zw : ubo_load.xy));)"},
+        TypeCase{ty_vec2<f16>, R"(const uint scalar_offset = (((32u * uint(i)) + 4u)) / 4;
+  uint ubo_load = data[scalar_offset / 4][scalar_offset % 4];
+  vector<float16_t, 2> x = vector<float16_t, 2>(float16_t(f16tof32(ubo_load & 0xFFFF)), float16_t(f16tof32(ubo_load >> 16)));)"},
+        TypeCase{ty_vec3<u32>, "x = data[scalar_offset / 4].xyz"},
+        TypeCase{ty_vec3<f32>, "x = asfloat(data[scalar_offset / 4].xyz)"},
+        TypeCase{ty_vec3<i32>, "x = asint(data[scalar_offset / 4].xyz)"},
+        TypeCase{ty_vec3<f16>, R"(const uint scalar_offset = (((32u * uint(i)) + 8u)) / 4;
+  uint4 ubo_load_1 = data[scalar_offset / 4];
+  uint2 ubo_load = ((scalar_offset & 2) ? ubo_load_1.zw : ubo_load_1.xy);
+  vector<float16_t, 2> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load & 0xFFFF));
+  float16_t ubo_load_y = f16tof32(ubo_load[0] >> 16);
+  vector<float16_t, 3> x = vector<float16_t, 3>(ubo_load_xz[0], ubo_load_y, ubo_load_xz[1]);)"},
+        TypeCase{ty_vec4<u32>, "x = data[scalar_offset / 4]"},
+        TypeCase{ty_vec4<f32>, "x = asfloat(data[scalar_offset / 4])"},
+        TypeCase{ty_vec4<i32>, "x = asint(data[scalar_offset / 4])"},
+        TypeCase{ty_vec4<f16>, R"(const uint scalar_offset = (((32u * uint(i)) + 8u)) / 4;
+  uint4 ubo_load_1 = data[scalar_offset / 4];
+  uint2 ubo_load = ((scalar_offset & 2) ? ubo_load_1.zw : ubo_load_1.xy);
+  vector<float16_t, 2> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load & 0xFFFF));
+  vector<float16_t, 2> ubo_load_yw = vector<float16_t, 2>(f16tof32(ubo_load >> 16));
+  vector<float16_t, 4> x = vector<float16_t, 4>(ubo_load_xz[0], ubo_load_yw[0], ubo_load_xz[1], ubo_load_yw[1]);)"},
+        TypeCase{ty_mat2x2<f32>, R"(float2x2 tint_symbol(uint4 buffer[12], uint offset) {
+  const uint scalar_offset = ((offset + 0u)) / 4;
+  uint4 ubo_load = buffer[scalar_offset / 4];
+  const uint scalar_offset_1 = ((offset + 8u)) / 4;
+  uint4 ubo_load_1 = buffer[scalar_offset_1 / 4];
+  return float2x2(asfloat(((scalar_offset & 2) ? ubo_load.zw : ubo_load.xy)), asfloat(((scalar_offset_1 & 2) ? ubo_load_1.zw : ubo_load_1.xy)));
+})"},
+        TypeCase{ty_mat2x3<f32>, R"(float2x3 tint_symbol(uint4 buffer[16], uint offset) {
+  const uint scalar_offset = ((offset + 0u)) / 4;
+  const uint scalar_offset_1 = ((offset + 16u)) / 4;
+  return float2x3(asfloat(buffer[scalar_offset / 4].xyz), asfloat(buffer[scalar_offset_1 / 4].xyz));
+})"},
+        TypeCase{ty_mat2x4<f32>, R"(float2x4 tint_symbol(uint4 buffer[16], uint offset) {
+  const uint scalar_offset = ((offset + 0u)) / 4;
+  const uint scalar_offset_1 = ((offset + 16u)) / 4;
+  return float2x4(asfloat(buffer[scalar_offset / 4]), asfloat(buffer[scalar_offset_1 / 4]));
+})"},
+        TypeCase{ty_mat3x2<f32>, R"(float3x2 tint_symbol(uint4 buffer[12], uint offset) {
+  const uint scalar_offset = ((offset + 0u)) / 4;
+  uint4 ubo_load = buffer[scalar_offset / 4];
+  const uint scalar_offset_1 = ((offset + 8u)) / 4;
+  uint4 ubo_load_1 = buffer[scalar_offset_1 / 4];
+  const uint scalar_offset_2 = ((offset + 16u)) / 4;
+  uint4 ubo_load_2 = buffer[scalar_offset_2 / 4];
+  return float3x2(asfloat(((scalar_offset & 2) ? ubo_load.zw : ubo_load.xy)), asfloat(((scalar_offset_1 & 2) ? ubo_load_1.zw : ubo_load_1.xy)), asfloat(((scalar_offset_2 & 2) ? ubo_load_2.zw : ubo_load_2.xy)));
+})"},
+        TypeCase{ty_mat3x3<f32>, R"(float3x3 tint_symbol(uint4 buffer[20], uint offset) {
+  const uint scalar_offset = ((offset + 0u)) / 4;
+  const uint scalar_offset_1 = ((offset + 16u)) / 4;
+  const uint scalar_offset_2 = ((offset + 32u)) / 4;
+  return float3x3(asfloat(buffer[scalar_offset / 4].xyz), asfloat(buffer[scalar_offset_1 / 4].xyz), asfloat(buffer[scalar_offset_2 / 4].xyz));
+})"},
+        TypeCase{ty_mat3x4<f32>, R"(float3x4 tint_symbol(uint4 buffer[20], uint offset) {
+  const uint scalar_offset = ((offset + 0u)) / 4;
+  const uint scalar_offset_1 = ((offset + 16u)) / 4;
+  const uint scalar_offset_2 = ((offset + 32u)) / 4;
+  return float3x4(asfloat(buffer[scalar_offset / 4]), asfloat(buffer[scalar_offset_1 / 4]), asfloat(buffer[scalar_offset_2 / 4]));
+})"},
+        TypeCase{ty_mat4x2<f32>, R"(float4x2 tint_symbol(uint4 buffer[16], uint offset) {
+  const uint scalar_offset = ((offset + 0u)) / 4;
+  uint4 ubo_load = buffer[scalar_offset / 4];
+  const uint scalar_offset_1 = ((offset + 8u)) / 4;
+  uint4 ubo_load_1 = buffer[scalar_offset_1 / 4];
+  const uint scalar_offset_2 = ((offset + 16u)) / 4;
+  uint4 ubo_load_2 = buffer[scalar_offset_2 / 4];
+  const uint scalar_offset_3 = ((offset + 24u)) / 4;
+  uint4 ubo_load_3 = buffer[scalar_offset_3 / 4];
+  return float4x2(asfloat(((scalar_offset & 2) ? ubo_load.zw : ubo_load.xy)), asfloat(((scalar_offset_1 & 2) ? ubo_load_1.zw : ubo_load_1.xy)), asfloat(((scalar_offset_2 & 2) ? ubo_load_2.zw : ubo_load_2.xy)), asfloat(((scalar_offset_3 & 2) ? ubo_load_3.zw : ubo_load_3.xy)));
+})"},
+        TypeCase{ty_mat4x3<f32>, R"(float4x3 tint_symbol(uint4 buffer[24], uint offset) {
+  const uint scalar_offset = ((offset + 0u)) / 4;
+  const uint scalar_offset_1 = ((offset + 16u)) / 4;
+  const uint scalar_offset_2 = ((offset + 32u)) / 4;
+  const uint scalar_offset_3 = ((offset + 48u)) / 4;
+  return float4x3(asfloat(buffer[scalar_offset / 4].xyz), asfloat(buffer[scalar_offset_1 / 4].xyz), asfloat(buffer[scalar_offset_2 / 4].xyz), asfloat(buffer[scalar_offset_3 / 4].xyz));
+})"},
+        TypeCase{ty_mat4x4<f32>, R"(float4x4 tint_symbol(uint4 buffer[24], uint offset) {
+  const uint scalar_offset = ((offset + 0u)) / 4;
+  const uint scalar_offset_1 = ((offset + 16u)) / 4;
+  const uint scalar_offset_2 = ((offset + 32u)) / 4;
+  const uint scalar_offset_3 = ((offset + 48u)) / 4;
+  return float4x4(asfloat(buffer[scalar_offset / 4]), asfloat(buffer[scalar_offset_1 / 4]), asfloat(buffer[scalar_offset_2 / 4]), asfloat(buffer[scalar_offset_3 / 4]));
+})"},
+        TypeCase{ty_mat2x2<f16>,
+                 R"(matrix<float16_t, 2, 2> tint_symbol(uint4 buffer[8], uint offset) {
+  const uint scalar_offset = ((offset + 0u)) / 4;
+  uint ubo_load = buffer[scalar_offset / 4][scalar_offset % 4];
+  const uint scalar_offset_1 = ((offset + 4u)) / 4;
+  uint ubo_load_1 = buffer[scalar_offset_1 / 4][scalar_offset_1 % 4];
+  return matrix<float16_t, 2, 2>(vector<float16_t, 2>(float16_t(f16tof32(ubo_load & 0xFFFF)), float16_t(f16tof32(ubo_load >> 16))), vector<float16_t, 2>(float16_t(f16tof32(ubo_load_1 & 0xFFFF)), float16_t(f16tof32(ubo_load_1 >> 16))));
+})"},
+        TypeCase{ty_mat2x3<f16>,
+                 R"(matrix<float16_t, 2, 3> tint_symbol(uint4 buffer[12], uint offset) {
+  const uint scalar_offset = ((offset + 0u)) / 4;
+  uint4 ubo_load_1 = buffer[scalar_offset / 4];
+  uint2 ubo_load = ((scalar_offset & 2) ? ubo_load_1.zw : ubo_load_1.xy);
+  vector<float16_t, 2> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load & 0xFFFF));
+  float16_t ubo_load_y = f16tof32(ubo_load[0] >> 16);
+  const uint scalar_offset_1 = ((offset + 8u)) / 4;
+  uint4 ubo_load_3 = buffer[scalar_offset_1 / 4];
+  uint2 ubo_load_2 = ((scalar_offset_1 & 2) ? ubo_load_3.zw : ubo_load_3.xy);
+  vector<float16_t, 2> ubo_load_2_xz = vector<float16_t, 2>(f16tof32(ubo_load_2 & 0xFFFF));
+  float16_t ubo_load_2_y = f16tof32(ubo_load_2[0] >> 16);
+  return matrix<float16_t, 2, 3>(vector<float16_t, 3>(ubo_load_xz[0], ubo_load_y, ubo_load_xz[1]), vector<float16_t, 3>(ubo_load_2_xz[0], ubo_load_2_y, ubo_load_2_xz[1]));
+})"},
+        TypeCase{ty_mat2x4<f16>,
+                 R"(matrix<float16_t, 2, 4> tint_symbol(uint4 buffer[12], uint offset) {
+  const uint scalar_offset = ((offset + 0u)) / 4;
+  uint4 ubo_load_1 = buffer[scalar_offset / 4];
+  uint2 ubo_load = ((scalar_offset & 2) ? ubo_load_1.zw : ubo_load_1.xy);
+  vector<float16_t, 2> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load & 0xFFFF));
+  vector<float16_t, 2> ubo_load_yw = vector<float16_t, 2>(f16tof32(ubo_load >> 16));
+  const uint scalar_offset_1 = ((offset + 8u)) / 4;
+  uint4 ubo_load_3 = buffer[scalar_offset_1 / 4];
+  uint2 ubo_load_2 = ((scalar_offset_1 & 2) ? ubo_load_3.zw : ubo_load_3.xy);
+  vector<float16_t, 2> ubo_load_2_xz = vector<float16_t, 2>(f16tof32(ubo_load_2 & 0xFFFF));
+  vector<float16_t, 2> ubo_load_2_yw = vector<float16_t, 2>(f16tof32(ubo_load_2 >> 16));
+  return matrix<float16_t, 2, 4>(vector<float16_t, 4>(ubo_load_xz[0], ubo_load_yw[0], ubo_load_xz[1], ubo_load_yw[1]), vector<float16_t, 4>(ubo_load_2_xz[0], ubo_load_2_yw[0], ubo_load_2_xz[1], ubo_load_2_yw[1]));
+})"},
+        TypeCase{ty_mat3x2<f16>,
+                 R"(matrix<float16_t, 3, 2> tint_symbol(uint4 buffer[8], uint offset) {
+  const uint scalar_offset = ((offset + 0u)) / 4;
+  uint ubo_load = buffer[scalar_offset / 4][scalar_offset % 4];
+  const uint scalar_offset_1 = ((offset + 4u)) / 4;
+  uint ubo_load_1 = buffer[scalar_offset_1 / 4][scalar_offset_1 % 4];
+  const uint scalar_offset_2 = ((offset + 8u)) / 4;
+  uint ubo_load_2 = buffer[scalar_offset_2 / 4][scalar_offset_2 % 4];
+  return matrix<float16_t, 3, 2>(vector<float16_t, 2>(float16_t(f16tof32(ubo_load & 0xFFFF)), float16_t(f16tof32(ubo_load >> 16))), vector<float16_t, 2>(float16_t(f16tof32(ubo_load_1 & 0xFFFF)), float16_t(f16tof32(ubo_load_1 >> 16))), vector<float16_t, 2>(float16_t(f16tof32(ubo_load_2 & 0xFFFF)), float16_t(f16tof32(ubo_load_2 >> 16))));
+})"},
+        TypeCase{ty_mat3x3<f16>,
+                 R"(matrix<float16_t, 3, 3> tint_symbol(uint4 buffer[12], uint offset) {
+  const uint scalar_offset = ((offset + 0u)) / 4;
+  uint4 ubo_load_1 = buffer[scalar_offset / 4];
+  uint2 ubo_load = ((scalar_offset & 2) ? ubo_load_1.zw : ubo_load_1.xy);
+  vector<float16_t, 2> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load & 0xFFFF));
+  float16_t ubo_load_y = f16tof32(ubo_load[0] >> 16);
+  const uint scalar_offset_1 = ((offset + 8u)) / 4;
+  uint4 ubo_load_3 = buffer[scalar_offset_1 / 4];
+  uint2 ubo_load_2 = ((scalar_offset_1 & 2) ? ubo_load_3.zw : ubo_load_3.xy);
+  vector<float16_t, 2> ubo_load_2_xz = vector<float16_t, 2>(f16tof32(ubo_load_2 & 0xFFFF));
+  float16_t ubo_load_2_y = f16tof32(ubo_load_2[0] >> 16);
+  const uint scalar_offset_2 = ((offset + 16u)) / 4;
+  uint4 ubo_load_5 = buffer[scalar_offset_2 / 4];
+  uint2 ubo_load_4 = ((scalar_offset_2 & 2) ? ubo_load_5.zw : ubo_load_5.xy);
+  vector<float16_t, 2> ubo_load_4_xz = vector<float16_t, 2>(f16tof32(ubo_load_4 & 0xFFFF));
+  float16_t ubo_load_4_y = f16tof32(ubo_load_4[0] >> 16);
+  return matrix<float16_t, 3, 3>(vector<float16_t, 3>(ubo_load_xz[0], ubo_load_y, ubo_load_xz[1]), vector<float16_t, 3>(ubo_load_2_xz[0], ubo_load_2_y, ubo_load_2_xz[1]), vector<float16_t, 3>(ubo_load_4_xz[0], ubo_load_4_y, ubo_load_4_xz[1]));
+})"},
+        TypeCase{ty_mat3x4<f16>,
+                 R"(matrix<float16_t, 3, 4> tint_symbol(uint4 buffer[12], uint offset) {
+  const uint scalar_offset = ((offset + 0u)) / 4;
+  uint4 ubo_load_1 = buffer[scalar_offset / 4];
+  uint2 ubo_load = ((scalar_offset & 2) ? ubo_load_1.zw : ubo_load_1.xy);
+  vector<float16_t, 2> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load & 0xFFFF));
+  vector<float16_t, 2> ubo_load_yw = vector<float16_t, 2>(f16tof32(ubo_load >> 16));
+  const uint scalar_offset_1 = ((offset + 8u)) / 4;
+  uint4 ubo_load_3 = buffer[scalar_offset_1 / 4];
+  uint2 ubo_load_2 = ((scalar_offset_1 & 2) ? ubo_load_3.zw : ubo_load_3.xy);
+  vector<float16_t, 2> ubo_load_2_xz = vector<float16_t, 2>(f16tof32(ubo_load_2 & 0xFFFF));
+  vector<float16_t, 2> ubo_load_2_yw = vector<float16_t, 2>(f16tof32(ubo_load_2 >> 16));
+  const uint scalar_offset_2 = ((offset + 16u)) / 4;
+  uint4 ubo_load_5 = buffer[scalar_offset_2 / 4];
+  uint2 ubo_load_4 = ((scalar_offset_2 & 2) ? ubo_load_5.zw : ubo_load_5.xy);
+  vector<float16_t, 2> ubo_load_4_xz = vector<float16_t, 2>(f16tof32(ubo_load_4 & 0xFFFF));
+  vector<float16_t, 2> ubo_load_4_yw = vector<float16_t, 2>(f16tof32(ubo_load_4 >> 16));
+  return matrix<float16_t, 3, 4>(vector<float16_t, 4>(ubo_load_xz[0], ubo_load_yw[0], ubo_load_xz[1], ubo_load_yw[1]), vector<float16_t, 4>(ubo_load_2_xz[0], ubo_load_2_yw[0], ubo_load_2_xz[1], ubo_load_2_yw[1]), vector<float16_t, 4>(ubo_load_4_xz[0], ubo_load_4_yw[0], ubo_load_4_xz[1], ubo_load_4_yw[1]));
+})"},
+        TypeCase{ty_mat4x2<f16>,
+                 R"(matrix<float16_t, 4, 2> tint_symbol(uint4 buffer[12], uint offset) {
+  const uint scalar_offset = ((offset + 0u)) / 4;
+  uint ubo_load = buffer[scalar_offset / 4][scalar_offset % 4];
+  const uint scalar_offset_1 = ((offset + 4u)) / 4;
+  uint ubo_load_1 = buffer[scalar_offset_1 / 4][scalar_offset_1 % 4];
+  const uint scalar_offset_2 = ((offset + 8u)) / 4;
+  uint ubo_load_2 = buffer[scalar_offset_2 / 4][scalar_offset_2 % 4];
+  const uint scalar_offset_3 = ((offset + 12u)) / 4;
+  uint ubo_load_3 = buffer[scalar_offset_3 / 4][scalar_offset_3 % 4];
+  return matrix<float16_t, 4, 2>(vector<float16_t, 2>(float16_t(f16tof32(ubo_load & 0xFFFF)), float16_t(f16tof32(ubo_load >> 16))), vector<float16_t, 2>(float16_t(f16tof32(ubo_load_1 & 0xFFFF)), float16_t(f16tof32(ubo_load_1 >> 16))), vector<float16_t, 2>(float16_t(f16tof32(ubo_load_2 & 0xFFFF)), float16_t(f16tof32(ubo_load_2 >> 16))), vector<float16_t, 2>(float16_t(f16tof32(ubo_load_3 & 0xFFFF)), float16_t(f16tof32(ubo_load_3 >> 16))));
+})"},
+        TypeCase{ty_mat4x3<f16>,
+                 R"(matrix<float16_t, 4, 3> tint_symbol(uint4 buffer[16], uint offset) {
+  const uint scalar_offset = ((offset + 0u)) / 4;
+  uint4 ubo_load_1 = buffer[scalar_offset / 4];
+  uint2 ubo_load = ((scalar_offset & 2) ? ubo_load_1.zw : ubo_load_1.xy);
+  vector<float16_t, 2> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load & 0xFFFF));
+  float16_t ubo_load_y = f16tof32(ubo_load[0] >> 16);
+  const uint scalar_offset_1 = ((offset + 8u)) / 4;
+  uint4 ubo_load_3 = buffer[scalar_offset_1 / 4];
+  uint2 ubo_load_2 = ((scalar_offset_1 & 2) ? ubo_load_3.zw : ubo_load_3.xy);
+  vector<float16_t, 2> ubo_load_2_xz = vector<float16_t, 2>(f16tof32(ubo_load_2 & 0xFFFF));
+  float16_t ubo_load_2_y = f16tof32(ubo_load_2[0] >> 16);
+  const uint scalar_offset_2 = ((offset + 16u)) / 4;
+  uint4 ubo_load_5 = buffer[scalar_offset_2 / 4];
+  uint2 ubo_load_4 = ((scalar_offset_2 & 2) ? ubo_load_5.zw : ubo_load_5.xy);
+  vector<float16_t, 2> ubo_load_4_xz = vector<float16_t, 2>(f16tof32(ubo_load_4 & 0xFFFF));
+  float16_t ubo_load_4_y = f16tof32(ubo_load_4[0] >> 16);
+  const uint scalar_offset_3 = ((offset + 24u)) / 4;
+  uint4 ubo_load_7 = buffer[scalar_offset_3 / 4];
+  uint2 ubo_load_6 = ((scalar_offset_3 & 2) ? ubo_load_7.zw : ubo_load_7.xy);
+  vector<float16_t, 2> ubo_load_6_xz = vector<float16_t, 2>(f16tof32(ubo_load_6 & 0xFFFF));
+  float16_t ubo_load_6_y = f16tof32(ubo_load_6[0] >> 16);
+  return matrix<float16_t, 4, 3>(vector<float16_t, 3>(ubo_load_xz[0], ubo_load_y, ubo_load_xz[1]), vector<float16_t, 3>(ubo_load_2_xz[0], ubo_load_2_y, ubo_load_2_xz[1]), vector<float16_t, 3>(ubo_load_4_xz[0], ubo_load_4_y, ubo_load_4_xz[1]), vector<float16_t, 3>(ubo_load_6_xz[0], ubo_load_6_y, ubo_load_6_xz[1]));
+})"},
+        TypeCase{ty_mat4x4<f16>,
+                 R"(matrix<float16_t, 4, 4> tint_symbol(uint4 buffer[16], uint offset) {
+  const uint scalar_offset = ((offset + 0u)) / 4;
+  uint4 ubo_load_1 = buffer[scalar_offset / 4];
+  uint2 ubo_load = ((scalar_offset & 2) ? ubo_load_1.zw : ubo_load_1.xy);
+  vector<float16_t, 2> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load & 0xFFFF));
+  vector<float16_t, 2> ubo_load_yw = vector<float16_t, 2>(f16tof32(ubo_load >> 16));
+  const uint scalar_offset_1 = ((offset + 8u)) / 4;
+  uint4 ubo_load_3 = buffer[scalar_offset_1 / 4];
+  uint2 ubo_load_2 = ((scalar_offset_1 & 2) ? ubo_load_3.zw : ubo_load_3.xy);
+  vector<float16_t, 2> ubo_load_2_xz = vector<float16_t, 2>(f16tof32(ubo_load_2 & 0xFFFF));
+  vector<float16_t, 2> ubo_load_2_yw = vector<float16_t, 2>(f16tof32(ubo_load_2 >> 16));
+  const uint scalar_offset_2 = ((offset + 16u)) / 4;
+  uint4 ubo_load_5 = buffer[scalar_offset_2 / 4];
+  uint2 ubo_load_4 = ((scalar_offset_2 & 2) ? ubo_load_5.zw : ubo_load_5.xy);
+  vector<float16_t, 2> ubo_load_4_xz = vector<float16_t, 2>(f16tof32(ubo_load_4 & 0xFFFF));
+  vector<float16_t, 2> ubo_load_4_yw = vector<float16_t, 2>(f16tof32(ubo_load_4 >> 16));
+  const uint scalar_offset_3 = ((offset + 24u)) / 4;
+  uint4 ubo_load_7 = buffer[scalar_offset_3 / 4];
+  uint2 ubo_load_6 = ((scalar_offset_3 & 2) ? ubo_load_7.zw : ubo_load_7.xy);
+  vector<float16_t, 2> ubo_load_6_xz = vector<float16_t, 2>(f16tof32(ubo_load_6 & 0xFFFF));
+  vector<float16_t, 2> ubo_load_6_yw = vector<float16_t, 2>(f16tof32(ubo_load_6 >> 16));
+  return matrix<float16_t, 4, 4>(vector<float16_t, 4>(ubo_load_xz[0], ubo_load_yw[0], ubo_load_xz[1], ubo_load_yw[1]), vector<float16_t, 4>(ubo_load_2_xz[0], ubo_load_2_yw[0], ubo_load_2_xz[1], ubo_load_2_yw[1]), vector<float16_t, 4>(ubo_load_4_xz[0], ubo_load_4_yw[0], ubo_load_4_xz[1], ubo_load_4_yw[1]), vector<float16_t, 4>(ubo_load_6_xz[0], ubo_load_6_yw[0], ubo_load_6_xz[1], ubo_load_6_yw[1]));
+})"}));
 
 using HlslGeneratorImplTest_MemberAccessor_StorageBufferStore =
     HlslGeneratorImplTest_MemberAccessorWithParam<TypeCase>;
 TEST_P(HlslGeneratorImplTest_MemberAccessor_StorageBufferStore, Test) {
     // struct Data {
-    //   a : i32;
-    //   b : <type>;
+    //   a : i32,
+    //   b : <type>,
     // };
     // var<storage> data : Data;
     // data.b = <type>();
 
     auto p = GetParam();
 
+    Enable(ast::Extension::kF16);
+
     SetupStorageBuffer(utils::Vector{
         Member("a", ty.i32()),
         Member("b", p.member_type(ty)),
@@ -243,73 +1010,123 @@
     EXPECT_THAT(gen.result(), HasSubstr(p.expected));
 }
 
-INSTANTIATE_TEST_SUITE_P(HlslGeneratorImplTest_MemberAccessor,
-                         HlslGeneratorImplTest_MemberAccessor_StorageBufferStore,
-                         testing::Values(TypeCase{ty_u32, "data.Store(4u, asuint(value))"},
-                                         TypeCase{ty_f32, "data.Store(4u, asuint(value))"},
-                                         TypeCase{ty_i32, "data.Store(4u, asuint(value))"},
-                                         TypeCase{ty_vec2<u32>, "data.Store2(8u, asuint(value))"},
-                                         TypeCase{ty_vec2<f32>, "data.Store2(8u, asuint(value))"},
-                                         TypeCase{ty_vec2<i32>, "data.Store2(8u, asuint(value))"},
-                                         TypeCase{ty_vec3<u32>, "data.Store3(16u, asuint(value))"},
-                                         TypeCase{ty_vec3<f32>, "data.Store3(16u, asuint(value))"},
-                                         TypeCase{ty_vec3<i32>, "data.Store3(16u, asuint(value))"},
-                                         TypeCase{ty_vec4<u32>, "data.Store4(16u, asuint(value))"},
-                                         TypeCase{ty_vec4<f32>, "data.Store4(16u, asuint(value))"},
-                                         TypeCase{ty_vec4<i32>, "data.Store4(16u, asuint(value))"},
-                                         TypeCase{ty_mat2x2<f32>, R"({
+INSTANTIATE_TEST_SUITE_P(
+    HlslGeneratorImplTest_MemberAccessor,
+    HlslGeneratorImplTest_MemberAccessor_StorageBufferStore,
+    testing::Values(TypeCase{ty_u32, "data.Store(4u, asuint(value))"},
+                    TypeCase{ty_f32, "data.Store(4u, asuint(value))"},
+                    TypeCase{ty_i32, "data.Store(4u, asuint(value))"},
+                    TypeCase{ty_f16, "data.Store<float16_t>(4u, value)"},
+                    TypeCase{ty_vec2<u32>, "data.Store2(8u, asuint(value))"},
+                    TypeCase{ty_vec2<f32>, "data.Store2(8u, asuint(value))"},
+                    TypeCase{ty_vec2<i32>, "data.Store2(8u, asuint(value))"},
+                    TypeCase{ty_vec2<f16>, "data.Store<vector<float16_t, 2> >(4u, value)"},
+                    TypeCase{ty_vec3<u32>, "data.Store3(16u, asuint(value))"},
+                    TypeCase{ty_vec3<f32>, "data.Store3(16u, asuint(value))"},
+                    TypeCase{ty_vec3<i32>, "data.Store3(16u, asuint(value))"},
+                    TypeCase{ty_vec3<f16>, "data.Store<vector<float16_t, 3> >(8u, value)"},
+                    TypeCase{ty_vec4<u32>, "data.Store4(16u, asuint(value))"},
+                    TypeCase{ty_vec4<f32>, "data.Store4(16u, asuint(value))"},
+                    TypeCase{ty_vec4<i32>, "data.Store4(16u, asuint(value))"},
+                    TypeCase{ty_vec4<f16>, "data.Store<vector<float16_t, 4> >(8u, value)"},
+                    TypeCase{ty_mat2x2<f32>, R"({
   buffer.Store2((offset + 0u), asuint(value[0u]));
   buffer.Store2((offset + 8u), asuint(value[1u]));
 })"},
-                                         TypeCase{ty_mat2x3<f32>, R"({
+                    TypeCase{ty_mat2x3<f32>, R"({
   buffer.Store3((offset + 0u), asuint(value[0u]));
   buffer.Store3((offset + 16u), asuint(value[1u]));
 })"},
-                                         TypeCase{ty_mat2x4<f32>, R"({
+                    TypeCase{ty_mat2x4<f32>, R"({
   buffer.Store4((offset + 0u), asuint(value[0u]));
   buffer.Store4((offset + 16u), asuint(value[1u]));
 })"},
-                                         TypeCase{ty_mat3x2<f32>, R"({
+                    TypeCase{ty_mat3x2<f32>, R"({
   buffer.Store2((offset + 0u), asuint(value[0u]));
   buffer.Store2((offset + 8u), asuint(value[1u]));
   buffer.Store2((offset + 16u), asuint(value[2u]));
 })"},
-                                         TypeCase{ty_mat3x3<f32>, R"({
+                    TypeCase{ty_mat3x3<f32>, R"({
   buffer.Store3((offset + 0u), asuint(value[0u]));
   buffer.Store3((offset + 16u), asuint(value[1u]));
   buffer.Store3((offset + 32u), asuint(value[2u]));
 })"},
-                                         TypeCase{ty_mat3x4<f32>, R"({
+                    TypeCase{ty_mat3x4<f32>, R"({
   buffer.Store4((offset + 0u), asuint(value[0u]));
   buffer.Store4((offset + 16u), asuint(value[1u]));
   buffer.Store4((offset + 32u), asuint(value[2u]));
 })"},
-                                         TypeCase{ty_mat4x2<f32>, R"({
+                    TypeCase{ty_mat4x2<f32>, R"({
   buffer.Store2((offset + 0u), asuint(value[0u]));
   buffer.Store2((offset + 8u), asuint(value[1u]));
   buffer.Store2((offset + 16u), asuint(value[2u]));
   buffer.Store2((offset + 24u), asuint(value[3u]));
 })"},
-                                         TypeCase{ty_mat4x3<f32>, R"({
+                    TypeCase{ty_mat4x3<f32>, R"({
   buffer.Store3((offset + 0u), asuint(value[0u]));
   buffer.Store3((offset + 16u), asuint(value[1u]));
   buffer.Store3((offset + 32u), asuint(value[2u]));
   buffer.Store3((offset + 48u), asuint(value[3u]));
 })"},
-                                         TypeCase{ty_mat4x4<f32>, R"({
+                    TypeCase{ty_mat4x4<f32>, R"({
   buffer.Store4((offset + 0u), asuint(value[0u]));
   buffer.Store4((offset + 16u), asuint(value[1u]));
   buffer.Store4((offset + 32u), asuint(value[2u]));
   buffer.Store4((offset + 48u), asuint(value[3u]));
+})"},
+                    TypeCase{ty_mat2x2<f16>, R"({
+  buffer.Store<vector<float16_t, 2> >((offset + 0u), value[0u]);
+  buffer.Store<vector<float16_t, 2> >((offset + 4u), value[1u]);
+})"},
+                    TypeCase{ty_mat2x3<f16>, R"({
+  buffer.Store<vector<float16_t, 3> >((offset + 0u), value[0u]);
+  buffer.Store<vector<float16_t, 3> >((offset + 8u), value[1u]);
+})"},
+                    TypeCase{ty_mat2x4<f16>, R"({
+  buffer.Store<vector<float16_t, 4> >((offset + 0u), value[0u]);
+  buffer.Store<vector<float16_t, 4> >((offset + 8u), value[1u]);
+})"},
+                    TypeCase{ty_mat3x2<f16>, R"({
+  buffer.Store<vector<float16_t, 2> >((offset + 0u), value[0u]);
+  buffer.Store<vector<float16_t, 2> >((offset + 4u), value[1u]);
+  buffer.Store<vector<float16_t, 2> >((offset + 8u), value[2u]);
+})"},
+                    TypeCase{ty_mat3x3<f16>, R"({
+  buffer.Store<vector<float16_t, 3> >((offset + 0u), value[0u]);
+  buffer.Store<vector<float16_t, 3> >((offset + 8u), value[1u]);
+  buffer.Store<vector<float16_t, 3> >((offset + 16u), value[2u]);
+})"},
+                    TypeCase{ty_mat3x4<f16>, R"({
+  buffer.Store<vector<float16_t, 4> >((offset + 0u), value[0u]);
+  buffer.Store<vector<float16_t, 4> >((offset + 8u), value[1u]);
+  buffer.Store<vector<float16_t, 4> >((offset + 16u), value[2u]);
+})"},
+                    TypeCase{ty_mat4x2<f16>, R"({
+  buffer.Store<vector<float16_t, 2> >((offset + 0u), value[0u]);
+  buffer.Store<vector<float16_t, 2> >((offset + 4u), value[1u]);
+  buffer.Store<vector<float16_t, 2> >((offset + 8u), value[2u]);
+  buffer.Store<vector<float16_t, 2> >((offset + 12u), value[3u]);
+})"},
+                    TypeCase{ty_mat4x3<f16>, R"({
+  buffer.Store<vector<float16_t, 3> >((offset + 0u), value[0u]);
+  buffer.Store<vector<float16_t, 3> >((offset + 8u), value[1u]);
+  buffer.Store<vector<float16_t, 3> >((offset + 16u), value[2u]);
+  buffer.Store<vector<float16_t, 3> >((offset + 24u), value[3u]);
+})"},
+                    TypeCase{ty_mat4x4<f16>, R"({
+  buffer.Store<vector<float16_t, 4> >((offset + 0u), value[0u]);
+  buffer.Store<vector<float16_t, 4> >((offset + 8u), value[1u]);
+  buffer.Store<vector<float16_t, 4> >((offset + 16u), value[2u]);
+  buffer.Store<vector<float16_t, 4> >((offset + 24u), value[3u]);
 })"}));
 
 TEST_F(HlslGeneratorImplTest_MemberAccessor, StorageBuffer_Store_Matrix_Empty) {
     // struct Data {
-    //   z : f32;
-    //   a : mat2x3<f32>;
+    //   a : f32,
+    //   b : mat2x3<f32>,
     // };
     // var<storage> data : Data;
-    // data.a = mat2x3<f32>();
+    // data.b = mat2x3<f32>();
 
     SetupStorageBuffer(utils::Vector{
         Member("a", ty.i32()),
@@ -339,10 +1156,10 @@
     EXPECT_EQ(gen.result(), expected);
 }
 
-TEST_F(HlslGeneratorImplTest_MemberAccessor, StorageBuffer_Load_Matrix_Single_Element) {
+TEST_F(HlslGeneratorImplTest_MemberAccessor, StorageBuffer_Load_Matrix_F32_Single_Element) {
     // struct Data {
-    //   z : f32;
-    //   a : mat4x3<f32>;
+    //   z : f32,
+    //   a : mat4x3<f32>,
     // };
     // var<storage> data : Data;
     // data.a[2i][1i];
@@ -370,17 +1187,119 @@
     EXPECT_EQ(gen.result(), expected);
 }
 
-TEST_F(HlslGeneratorImplTest_MemberAccessor,
-       EmitExpression_IndexAccessor_StorageBuffer_Load_Int_FromArray) {
+TEST_F(HlslGeneratorImplTest_MemberAccessor, StorageBuffer_Load_Matrix_F16_Single_Element) {
     // struct Data {
-    //   a : array<i32, 5>;
+    //   z : f16,
+    //   a : mat4x3<f16>,
+    // };
+    // var<storage> data : Data;
+    // data.a[2i][1i];
+
+    Enable(ast::Extension::kF16);
+
+    SetupStorageBuffer(utils::Vector{
+        Member("z", ty.f16()),
+        Member("a", ty.mat4x3<f16>()),
+    });
+
+    SetupFunction(utils::Vector{
+        Decl(Var("x", IndexAccessor(IndexAccessor(MemberAccessor("data", "a"), 2_i), 1_i))),
+    });
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    auto* expected =
+        R"(RWByteAddressBuffer data : register(u0, space1);
+
+void main() {
+  float16_t x = data.Load<float16_t>(26u);
+  return;
+}
+)";
+    EXPECT_EQ(gen.result(), expected);
+}
+
+TEST_F(HlslGeneratorImplTest_MemberAccessor, UniformBuffer_Load_Matrix_F32_Single_Element) {
+    // struct Data {
+    //   z : f32,
+    //   a : mat4x3<f32>,
+    // };
+    // var<uniform> data : Data;
+    // data.a[2i][1i];
+
+    SetupUniformBuffer(utils::Vector{
+        Member("z", ty.f32()),
+        Member("a", ty.mat4x3<f32>()),
+    });
+
+    SetupFunction(utils::Vector{
+        Decl(Var("x", IndexAccessor(IndexAccessor(MemberAccessor("data", "a"), 2_i), 1_i))),
+    });
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    auto* expected =
+        R"(cbuffer cbuffer_data : register(b1, space1) {
+  uint4 data[5];
+};
+
+void main() {
+  float x = asfloat(data[3].y);
+  return;
+}
+)";
+    EXPECT_EQ(gen.result(), expected);
+}
+
+TEST_F(HlslGeneratorImplTest_MemberAccessor, UniformBuffer_Load_Matrix_F16_Single_Element) {
+    // struct Data {
+    //   z : f16,
+    //   a : mat4x3<f16>,
+    // };
+    // var<uniform> data : Data;
+    // data.a[2i][1i];
+
+    Enable(ast::Extension::kF16);
+
+    SetupUniformBuffer(utils::Vector{
+        Member("z", ty.f16()),
+        Member("a", ty.mat4x3<f16>()),
+    });
+
+    SetupFunction(utils::Vector{
+        Decl(Var("x", IndexAccessor(IndexAccessor(MemberAccessor("data", "a"), 2_i), 1_i))),
+    });
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    auto* expected =
+        R"(cbuffer cbuffer_data : register(b1, space1) {
+  uint4 data[3];
+};
+
+void main() {
+  float16_t x = float16_t(f16tof32(((data[1].z >> 16) & 0xFFFF)));
+  return;
+}
+)";
+    EXPECT_EQ(gen.result(), expected);
+}
+
+TEST_F(HlslGeneratorImplTest_MemberAccessor,
+       EmitExpression_IndexAccessor_StorageBuffer_Load_I32_FromArray) {
+    // struct Data {
+    //   z : f32,
+    //   a : array<i32, 5i>,
     // };
     // var<storage> data : Data;
     // data.a[2];
 
     SetupStorageBuffer(utils::Vector{
         Member("z", ty.f32()),
-        Member("a", ty.array<i32, 5>(4)),
+        Member("a", ty.array(ty.i32(), 5_i)),
     });
 
     SetupFunction(utils::Vector{
@@ -402,16 +1321,154 @@
 }
 
 TEST_F(HlslGeneratorImplTest_MemberAccessor,
-       EmitExpression_IndexAccessor_StorageBuffer_Load_Int_FromArray_ExprIdx) {
+       EmitExpression_IndexAccessor_UniformBuffer_Load_Vec4_I32_FromArray) {
     // struct Data {
-    //   a : array<i32, 5>;
+    //   z : f32,
+    //   a : array<vec4<i32>, 5i>,
+    // };
+    // var<uniform> data : Data;
+    // data.a[2];
+
+    SetupUniformBuffer(utils::Vector{
+        Member("z", ty.f32()),
+        Member("a", ty.array(ty.vec4(ty.i32()), 5_i)),
+    });
+
+    SetupFunction(utils::Vector{
+        Decl(Var("x", IndexAccessor(MemberAccessor("data", "a"), 2_i))),
+    });
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    auto* expected =
+        R"(cbuffer cbuffer_data : register(b1, space1) {
+  uint4 data[6];
+};
+
+void main() {
+  int4 x = asint(data[3]);
+  return;
+}
+)";
+    EXPECT_EQ(gen.result(), expected);
+}
+
+TEST_F(HlslGeneratorImplTest_MemberAccessor,
+       EmitExpression_IndexAccessor_StorageBuffer_Load_Struct_FromArray) {
+    // struct Inner {
+    //   @size(16i) @align(16i)
+    //   v : i32,
+    // };
+    // struct Data {
+    //   z : f32,
+    //   a : array<Inner, 5i>,
+    // };
+    // var<storage> data : Data;
+    // data.a[2i];
+
+    auto* elem_type = Structure(
+        "Inner", utils::Vector{
+                     Member("v", ty.i32(), utils::Vector{MemberSize(16_i), MemberAlign(16_i)}),
+                 });
+
+    SetupStorageBuffer(utils::Vector{
+        Member("z", ty.f32()),
+        Member("a", ty.array(ty.Of(elem_type), 5_i)),
+    });
+
+    SetupFunction(utils::Vector{
+        Decl(Var("x", IndexAccessor(MemberAccessor("data", "a"), 2_i))),
+    });
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    auto* expected =
+        R"(struct Inner {
+  int v;
+};
+
+RWByteAddressBuffer data : register(u0, space1);
+
+Inner tint_symbol(RWByteAddressBuffer buffer, uint offset) {
+  const Inner tint_symbol_2 = {asint(buffer.Load((offset + 0u)))};
+  return tint_symbol_2;
+}
+
+void main() {
+  Inner x = tint_symbol(data, 48u);
+  return;
+}
+)";
+    EXPECT_EQ(gen.result(), expected);
+}
+
+TEST_F(HlslGeneratorImplTest_MemberAccessor,
+       EmitExpression_IndexAccessor_UniformBuffer_Load_Struct_FromArray) {
+    // struct Inner {
+    //   @size(16i) @align(16i)
+    //   v : i32,
+    // };
+    // struct Data {
+    //   z : f32,
+    //   a : array<Inner, 5i>,
+    // };
+    // var<uniform> data : Data;
+    // data.a[2i];
+
+    auto* elem_type = Structure(
+        "Inner", utils::Vector{
+                     Member("v", ty.i32(), utils::Vector{MemberSize(16_i), MemberAlign(16_i)}),
+                 });
+
+    SetupUniformBuffer(utils::Vector{
+        Member("z", ty.f32()),
+        Member("a", ty.array(ty.Of(elem_type), 5_i)),
+    });
+
+    SetupFunction(utils::Vector{
+        Decl(Var("x", IndexAccessor(MemberAccessor("data", "a"), 2_i))),
+    });
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    auto* expected =
+        R"(struct Inner {
+  int v;
+};
+
+cbuffer cbuffer_data : register(b1, space1) {
+  uint4 data[6];
+};
+
+Inner tint_symbol(uint4 buffer[6], uint offset) {
+  const uint scalar_offset = ((offset + 0u)) / 4;
+  const Inner tint_symbol_2 = {asint(buffer[scalar_offset / 4][scalar_offset % 4])};
+  return tint_symbol_2;
+}
+
+void main() {
+  Inner x = tint_symbol(data, 48u);
+  return;
+}
+)";
+    EXPECT_EQ(gen.result(), expected);
+}
+
+TEST_F(HlslGeneratorImplTest_MemberAccessor,
+       EmitExpression_IndexAccessor_StorageBuffer_Load_I32_FromArray_ExprIdx) {
+    // struct Data {
+    //   z : f32,
+    //   a : array<i32, 5i>,
     // };
     // var<storage> data : Data;
     // data.a[(2i + 4i) - 3i];
 
     SetupStorageBuffer(utils::Vector{
         Member("z", ty.f32()),
-        Member("a", ty.array<i32, 5>(4)),
+        Member("a", ty.array(ty.i32(), 5_i)),
     });
 
     SetupFunction(utils::Vector{
@@ -438,16 +1495,57 @@
     EXPECT_EQ(gen.result(), expected);
 }
 
+TEST_F(HlslGeneratorImplTest_MemberAccessor,
+       EmitExpression_IndexAccessor_UniformBuffer_Load_Vec4_I32_FromArray_ExprIdx) {
+    // struct Data {
+    //   z : f32,
+    //   a : array<vec4<i32>, 5i>,
+    // };
+    // var<uniform> data : Data;
+    // data.a[(2i + 4i) - 3i];
+
+    SetupUniformBuffer(utils::Vector{
+        Member("z", ty.f32()),
+        Member("a", ty.array(ty.vec4(ty.i32()), 5_i)),
+    });
+
+    SetupFunction(utils::Vector{
+        Decl(Var("a", Expr(2_i))),
+        Decl(Var("b", Expr(4_i))),
+        Decl(Var("c", Expr(3_i))),
+        Decl(Var("x", IndexAccessor(MemberAccessor("data", "a"), Sub(Add("a", "b"), "c")))),
+    });
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    auto* expected =
+        R"(cbuffer cbuffer_data : register(b1, space1) {
+  uint4 data[6];
+};
+
+void main() {
+  int a = 2;
+  int b = 4;
+  int c = 3;
+  const uint scalar_offset = ((16u + (16u * uint(((a + b) - c))))) / 4;
+  int4 x = asint(data[scalar_offset / 4]);
+  return;
+}
+)";
+    EXPECT_EQ(gen.result(), expected);
+}
+
 TEST_F(HlslGeneratorImplTest_MemberAccessor, StorageBuffer_Store_ToArray) {
     // struct Data {
-    //   a : array<i32, 5>;
+    //   a : array<i32, 5i>,
     // };
     // var<storage> data : Data;
-    // data.a[2] = 2;
+    // data.a[2i] = 2i;
 
     SetupStorageBuffer(utils::Vector{
         Member("z", ty.f32()),
-        Member("a", ty.array<i32, 5>(4)),
+        Member("a", ty.array(ty.i32(), 5_i)),
     });
 
     SetupFunction(utils::Vector{
@@ -470,23 +1568,23 @@
 
 TEST_F(HlslGeneratorImplTest_MemberAccessor, StorageBuffer_Load_MultiLevel) {
     // struct Inner {
-    //   a : vec3<i32>;
-    //   b : vec3<f32>;
+    //   a : vec3<i32>,
+    //   b : vec3<f32>,
     // };
     // struct Data {
-    //   var c : array<Inner, 4u>;
+    //   c : array<Inner, 4u>,
     // };
     //
-    // var<storage> data : Pre;
-    // data.c[2].b
+    // var<storage> data : Data;
+    // data.c[2i].b
 
     auto* inner = Structure("Inner", utils::Vector{
-                                         Member("a", ty.vec3<f32>()),
+                                         Member("a", ty.vec3<i32>()),
                                          Member("b", ty.vec3<f32>()),
                                      });
 
     SetupStorageBuffer(utils::Vector{
-        Member("c", ty.array(ty.Of(inner), 4_u, 32)),
+        Member("c", ty.array(ty.Of(inner), 4_u)),
     });
 
     SetupFunction(utils::Vector{
@@ -507,31 +1605,72 @@
     EXPECT_EQ(gen.result(), expected);
 }
 
-TEST_F(HlslGeneratorImplTest_MemberAccessor, StorageBuffer_Load_MultiLevel_Swizzle) {
+TEST_F(HlslGeneratorImplTest_MemberAccessor, UniformBuffer_Load_MultiLevel) {
     // struct Inner {
-    //   a : vec3<i32>;
-    //   b : vec3<f32>;
+    //   a : vec3<i32>,
+    //   b : vec3<f32>,
     // };
     // struct Data {
-    //   var c : array<Inner, 4u>;
+    //   var c : array<Inner, 4u>,
     // };
     //
-    // var<storage> data : Pre;
-    // data.c[2].b.xy
+    // var<storage> data : Data;
+    // data.c[2i].b
 
     auto* inner = Structure("Inner", utils::Vector{
-                                         Member("a", ty.vec3<f32>()),
+                                         Member("a", ty.vec3<i32>()),
+                                         Member("b", ty.vec3<f32>()),
+                                     });
+
+    SetupUniformBuffer(utils::Vector{
+        Member("c", ty.array(ty.Of(inner), 4_u)),
+    });
+
+    SetupFunction(utils::Vector{
+        Decl(Var("x", MemberAccessor(IndexAccessor(MemberAccessor("data", "c"), 2_i), "b"))),
+    });
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    auto* expected =
+        R"(cbuffer cbuffer_data : register(b1, space1) {
+  uint4 data[8];
+};
+
+void main() {
+  float3 x = asfloat(data[5].xyz);
+  return;
+}
+)";
+    EXPECT_EQ(gen.result(), expected);
+}
+
+TEST_F(HlslGeneratorImplTest_MemberAccessor, StorageBuffer_Load_MultiLevel_Swizzle) {
+    // struct Inner {
+    //   a : vec3<i32>,
+    //   b : vec3<f32>,
+    // };
+    // struct Data {
+    //   var c : array<Inner, 4u>,
+    // };
+    //
+    // var<storage> data : Data;
+    // data.c[2i].b.yx
+
+    auto* inner = Structure("Inner", utils::Vector{
+                                         Member("a", ty.vec3<i32>()),
                                          Member("b", ty.vec3<f32>()),
                                      });
 
     SetupStorageBuffer(utils::Vector{
-        Member("c", ty.array(ty.Of(inner), 4_u, 32)),
+        Member("c", ty.array(ty.Of(inner), 4_u)),
     });
 
     SetupFunction(utils::Vector{
         Decl(Var("x",
                  MemberAccessor(
-                     MemberAccessor(IndexAccessor(MemberAccessor("data", "c"), 2_i), "b"), "xy"))),
+                     MemberAccessor(IndexAccessor(MemberAccessor("data", "c"), 2_i), "b"), "yx"))),
     });
 
     GeneratorImpl& gen = SanitizeAndBuild();
@@ -541,7 +1680,50 @@
         R"(RWByteAddressBuffer data : register(u0, space1);
 
 void main() {
-  float2 x = asfloat(data.Load3(80u)).xy;
+  float2 x = asfloat(data.Load3(80u)).yx;
+  return;
+}
+)";
+    EXPECT_EQ(gen.result(), expected);
+}
+
+TEST_F(HlslGeneratorImplTest_MemberAccessor, UniformBuffer_Load_MultiLevel_Swizzle) {
+    // struct Inner {
+    //   a : vec3<i32>,
+    //   b : vec3<f32>,
+    // };
+    // struct Data {
+    //   var c : array<Inner, 4u>,
+    // };
+    //
+    // var<uniform> data : Data;
+    // data.c[2i].b.yx
+
+    auto* inner = Structure("Inner", utils::Vector{
+                                         Member("a", ty.vec3<i32>()),
+                                         Member("b", ty.vec3<f32>()),
+                                     });
+
+    SetupUniformBuffer(utils::Vector{
+        Member("c", ty.array(ty.Of(inner), 4_u)),
+    });
+
+    SetupFunction(utils::Vector{
+        Decl(Var("x",
+                 MemberAccessor(
+                     MemberAccessor(IndexAccessor(MemberAccessor("data", "c"), 2_i), "b"), "yx"))),
+    });
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    auto* expected =
+        R"(cbuffer cbuffer_data : register(b1, space1) {
+  uint4 data[8];
+};
+
+void main() {
+  float2 x = asfloat(data[5].xyz).yx;
   return;
 }
 )";
@@ -551,23 +1733,23 @@
 TEST_F(HlslGeneratorImplTest_MemberAccessor,
        StorageBuffer_Load_MultiLevel_Swizzle_SingleLetter) {  // NOLINT
     // struct Inner {
-    //   a : vec3<i32>;
-    //   b : vec3<f32>;
+    //   a : vec3<i32>,
+    //   b : vec3<f32>,
     // };
     // struct Data {
-    //   var c : array<Inner, 4u>;
+    //   var c : array<Inner, 4u>,
     // };
     //
-    // var<storage> data : Pre;
-    // data.c[2].b.g
+    // var<storage> data : Data;
+    // data.c[2i].b.g
 
     auto* inner = Structure("Inner", utils::Vector{
-                                         Member("a", ty.vec3<f32>()),
+                                         Member("a", ty.vec3<i32>()),
                                          Member("b", ty.vec3<f32>()),
                                      });
 
     SetupStorageBuffer(utils::Vector{
-        Member("c", ty.array(ty.Of(inner), 4_u, 32)),
+        Member("c", ty.array(ty.Of(inner), 4_u)),
     });
 
     SetupFunction(utils::Vector{
@@ -590,25 +1772,69 @@
     EXPECT_EQ(gen.result(), expected);
 }
 
-TEST_F(HlslGeneratorImplTest_MemberAccessor, StorageBuffer_Load_MultiLevel_Index) {
+TEST_F(HlslGeneratorImplTest_MemberAccessor,
+       UniformBuffer_Load_MultiLevel_Swizzle_SingleLetter) {  // NOLINT
     // struct Inner {
-    //   a : vec3<i32>;
-    //   b : vec3<f32>;
+    //   a : vec3<i32>,
+    //   b : vec3<f32>,
     // };
     // struct Data {
-    //   var c : array<Inner, 4u>;
+    //   var c : array<Inner, 4u>,
     // };
     //
-    // var<storage> data : Pre;
-    // data.c[2].b[1]
+    // var<uniform> data : Data;
+    // data.c[2i].b.g
 
     auto* inner = Structure("Inner", utils::Vector{
-                                         Member("a", ty.vec3<f32>()),
+                                         Member("a", ty.vec3<i32>()),
+                                         Member("b", ty.vec3<f32>()),
+                                     });
+
+    SetupUniformBuffer(utils::Vector{
+        Member("c", ty.array(ty.Of(inner), 4_u)),
+    });
+
+    SetupFunction(utils::Vector{
+        Decl(Var("x",
+                 MemberAccessor(
+                     MemberAccessor(IndexAccessor(MemberAccessor("data", "c"), 2_i), "b"), "g"))),
+    });
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    auto* expected =
+        R"(cbuffer cbuffer_data : register(b1, space1) {
+  uint4 data[8];
+};
+
+void main() {
+  float x = asfloat(data[5].y);
+  return;
+}
+)";
+    EXPECT_EQ(gen.result(), expected);
+}
+
+TEST_F(HlslGeneratorImplTest_MemberAccessor, StorageBuffer_Load_MultiLevel_Index) {
+    // struct Inner {
+    //   a : vec3<i32>,
+    //   b : vec3<f32>,
+    // };
+    // struct Data {
+    //   var c : array<Inner, 4u>,
+    // };
+    //
+    // var<storage> data : Data;
+    // data.c[2i].b[1i]
+
+    auto* inner = Structure("Inner", utils::Vector{
+                                         Member("a", ty.vec3<i32>()),
                                          Member("b", ty.vec3<f32>()),
                                      });
 
     SetupStorageBuffer(utils::Vector{
-        Member("c", ty.array(ty.Of(inner), 4_u, 32)),
+        Member("c", ty.array(ty.Of(inner), 4_u)),
     });
 
     SetupFunction(utils::Vector{
@@ -631,25 +1857,68 @@
     EXPECT_EQ(gen.result(), expected);
 }
 
-TEST_F(HlslGeneratorImplTest_MemberAccessor, StorageBuffer_Store_MultiLevel) {
+TEST_F(HlslGeneratorImplTest_MemberAccessor, UniformBuffer_Load_MultiLevel_Index) {
     // struct Inner {
-    //   a : vec3<i32>;
-    //   b : vec3<f32>;
+    //   a : vec3<i32>,
+    //   b : vec3<f32>,
     // };
     // struct Data {
-    //   var c : array<Inner, 4u>;
+    //   var c : array<Inner, 4u>,
+    // };
+    //
+    // var<uniform> data : Data;
+    // data.c[2i].b[1i]
+
+    auto* inner = Structure("Inner", utils::Vector{
+                                         Member("a", ty.vec3<i32>()),
+                                         Member("b", ty.vec3<f32>()),
+                                     });
+
+    SetupUniformBuffer(utils::Vector{
+        Member("c", ty.array(ty.Of(inner), 4_u)),
+    });
+
+    SetupFunction(utils::Vector{
+        Decl(Var("x",
+                 IndexAccessor(MemberAccessor(IndexAccessor(MemberAccessor("data", "c"), 2_i), "b"),
+                               1_i))),
+    });
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    auto* expected =
+        R"(cbuffer cbuffer_data : register(b1, space1) {
+  uint4 data[8];
+};
+
+void main() {
+  float x = asfloat(data[5].y);
+  return;
+}
+)";
+    EXPECT_EQ(gen.result(), expected);
+}
+
+TEST_F(HlslGeneratorImplTest_MemberAccessor, StorageBuffer_Store_MultiLevel) {
+    // struct Inner {
+    //   a : vec3<i32>,
+    //   b : vec3<f32>,
+    // };
+    // struct Data {
+    //   var c : array<Inner, 4u>,
     // };
     //
     // var<storage> data : Pre;
-    // data.c[2].b = vec3<f32>(1_f, 2_f, 3_f);
+    // data.c[2i].b = vec3<f32>(1_f, 2_f, 3_f);
 
     auto* inner = Structure("Inner", utils::Vector{
-                                         Member("a", ty.vec3<f32>()),
+                                         Member("a", ty.vec3<i32>()),
                                          Member("b", ty.vec3<f32>()),
                                      });
 
     SetupStorageBuffer(utils::Vector{
-        Member("c", ty.array(ty.Of(inner), 4_u, 32)),
+        Member("c", ty.array(ty.Of(inner), 4_u)),
     });
 
     SetupFunction(utils::Vector{
@@ -673,15 +1942,15 @@
 
 TEST_F(HlslGeneratorImplTest_MemberAccessor, StorageBuffer_Store_Swizzle_SingleLetter) {
     // struct Inner {
-    //   a : vec3<i32>;
-    //   b : vec3<f32>;
+    //   a : vec3<i32>,
+    //   b : vec3<f32>,
     // };
     // struct Data {
-    //   var c : array<Inner, 4u>;
+    //   var c : array<Inner, 4u>,
     // };
     //
     // var<storage> data : Pre;
-    // data.c[2].b.y = 1.f;
+    // data.c[2i].b.y = 1.f;
 
     auto* inner = Structure("Inner", utils::Vector{
                                          Member("a", ty.vec3<i32>()),
@@ -689,7 +1958,7 @@
                                      });
 
     SetupStorageBuffer(utils::Vector{
-        Member("c", ty.array(ty.Of(inner), 4_u, 32)),
+        Member("c", ty.array(ty.Of(inner), 4_u)),
     });
 
     SetupFunction(utils::Vector{
diff --git a/src/tint/writer/msl/generator_impl_builtin_test.cc b/src/tint/writer/msl/generator_impl_builtin_test.cc
index c611d6b..7a69a14 100644
--- a/src/tint/writer/msl/generator_impl_builtin_test.cc
+++ b/src/tint/writer/msl/generator_impl_builtin_test.cc
@@ -416,19 +416,19 @@
 
 using namespace metal;
 
-struct modf_result {
+struct modf_result_f32 {
   float fract;
   float whole;
 };
-modf_result tint_modf(float param_0) {
-  modf_result result;
+modf_result_f32 tint_modf(float param_0) {
+  modf_result_f32 result;
   result.fract = modf(param_0, result.whole);
   return result;
 }
 
 kernel void test_function() {
   float const f = 1.5f;
-  modf_result const v = tint_modf(f);
+  modf_result_f32 const v = tint_modf(f);
   return;
 }
 
@@ -478,19 +478,19 @@
 
 using namespace metal;
 
-struct modf_result_vec3 {
+struct modf_result_vec3_f32 {
   float3 fract;
   float3 whole;
 };
-modf_result_vec3 tint_modf(float3 param_0) {
-  modf_result_vec3 result;
+modf_result_vec3_f32 tint_modf(float3 param_0) {
+  modf_result_vec3_f32 result;
   result.fract = modf(param_0, result.whole);
   return result;
 }
 
 kernel void test_function() {
   float3 const f = float3(1.5f, 2.5f, 3.5f);
-  modf_result_vec3 const v = tint_modf(f);
+  modf_result_vec3_f32 const v = tint_modf(f);
   return;
 }
 
@@ -539,12 +539,12 @@
 
 using namespace metal;
 
-struct modf_result {
+struct modf_result_f32 {
   float fract;
   float whole;
 };
 kernel void test_function() {
-  modf_result const v = modf_result{.fract=0.5f, .whole=1.0f};
+  modf_result_f32 const v = modf_result_f32{.fract=0.5f, .whole=1.0f};
   return;
 }
 
@@ -585,12 +585,12 @@
 
 using namespace metal;
 
-struct modf_result_vec3 {
+struct modf_result_vec3_f32 {
   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)};
+  modf_result_vec3_f32 const v = modf_result_vec3_f32{.fract=float3(0.5f), .whole=float3(1.0f, 2.0f, 3.0f)};
   return;
 }
 
@@ -621,9 +621,9 @@
 )");
 }
 
-TEST_F(MslGeneratorImplTest, Frexp_Scalar_f32) {
-    auto* call = Call("frexp", 1_f);
-    WrapInFunction(CallStmt(call));
+TEST_F(MslGeneratorImplTest, Runtime_Frexp_Scalar_f32) {
+    WrapInFunction(Var("f", Expr(1_f)),  //
+                   Var("v", Call("frexp", "f")));
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
@@ -632,29 +632,30 @@
 
 using namespace metal;
 
-struct frexp_result {
+struct frexp_result_f32 {
   float fract;
   int exp;
 };
-frexp_result tint_frexp(float param_0) {
-  frexp_result result;
+frexp_result_f32 tint_frexp(float param_0) {
+  frexp_result_f32 result;
   result.fract = frexp(param_0, result.exp);
   return result;
 }
 
 kernel void test_function() {
-  tint_frexp(1.0f);
+  float f = 1.0f;
+  frexp_result_f32 v = tint_frexp(f);
   return;
 }
 
 )");
 }
 
-TEST_F(MslGeneratorImplTest, Frexp_Scalar_f16) {
+TEST_F(MslGeneratorImplTest, Runtime_Frexp_Scalar_f16) {
     Enable(ast::Extension::kF16);
 
-    auto* call = Call("frexp", 1_h);
-    WrapInFunction(CallStmt(call));
+    WrapInFunction(Var("f", Expr(1_h)),  //
+                   Var("v", Call("frexp", "f")));
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
@@ -674,16 +675,17 @@
 }
 
 kernel void test_function() {
-  tint_frexp(1.0h);
+  half f = 1.0h;
+  frexp_result_f16 v = tint_frexp(f);
   return;
 }
 
 )");
 }
 
-TEST_F(MslGeneratorImplTest, Frexp_Vector_f32) {
-    auto* call = Call("frexp", vec3<f32>());
-    WrapInFunction(CallStmt(call));
+TEST_F(MslGeneratorImplTest, Runtime_Frexp_Vector_f32) {
+    WrapInFunction(Var("f", Expr(vec3<f32>())),  //
+                   Var("v", Call("frexp", "f")));
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
@@ -692,29 +694,30 @@
 
 using namespace metal;
 
-struct frexp_result_vec3 {
+struct frexp_result_vec3_f32 {
   float3 fract;
   int3 exp;
 };
-frexp_result_vec3 tint_frexp(float3 param_0) {
-  frexp_result_vec3 result;
+frexp_result_vec3_f32 tint_frexp(float3 param_0) {
+  frexp_result_vec3_f32 result;
   result.fract = frexp(param_0, result.exp);
   return result;
 }
 
 kernel void test_function() {
-  tint_frexp(float3(0.0f));
+  float3 f = float3(0.0f);
+  frexp_result_vec3_f32 v = tint_frexp(f);
   return;
 }
 
 )");
 }
 
-TEST_F(MslGeneratorImplTest, Frexp_Vector_f16) {
+TEST_F(MslGeneratorImplTest, Runtime_Frexp_Vector_f16) {
     Enable(ast::Extension::kF16);
 
-    auto* call = Call("frexp", vec3<f16>());
-    WrapInFunction(CallStmt(call));
+    WrapInFunction(Var("f", Expr(vec3<f16>())),  //
+                   Var("v", Call("frexp", "f")));
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
@@ -734,7 +737,100 @@
 }
 
 kernel void test_function() {
-  tint_frexp(half3(0.0h));
+  half3 f = half3(0.0h);
+  frexp_result_vec3_f16 v = tint_frexp(f);
+  return;
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Const_Frexp_Scalar_f32) {
+    WrapInFunction(Decl(Let("v", Call("frexp", 1_f))));
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+
+struct frexp_result_f32 {
+  float fract;
+  int exp;
+};
+kernel void test_function() {
+  frexp_result_f32 const v = frexp_result_f32{.fract=0.5f, .exp=1};
+  return;
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Const_Frexp_Scalar_f16) {
+    Enable(ast::Extension::kF16);
+
+    WrapInFunction(Decl(Let("v", Call("frexp", 1_h))));
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+
+struct frexp_result_f16 {
+  half fract;
+  int exp;
+};
+kernel void test_function() {
+  frexp_result_f16 const v = frexp_result_f16{.fract=0.5h, .exp=1};
+  return;
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Const_Frexp_Vector_f32) {
+    WrapInFunction(Decl(Let("v", Call("frexp", vec3<f32>()))));
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+
+struct frexp_result_vec3_f32 {
+  float3 fract;
+  int3 exp;
+};
+kernel void test_function() {
+  frexp_result_vec3_f32 const v = frexp_result_vec3_f32{};
+  return;
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Const_Frexp_Vector_f16) {
+    Enable(ast::Extension::kF16);
+
+    WrapInFunction(Decl(Let("v", Call("frexp", vec3<f16>()))));
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+
+struct frexp_result_vec3_f16 {
+  half3 fract;
+  int3 exp;
+};
+kernel void test_function() {
+  frexp_result_vec3_f16 const v = frexp_result_vec3_f16{};
   return;
 }
 
@@ -751,19 +847,8 @@
     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;
+  float const tint_symbol = 0.5f;
   return;
 }
 
diff --git a/src/tint/writer/spirv/builder.cc b/src/tint/writer/spirv/builder.cc
index 9b1e077..c5e4a38 100644
--- a/src/tint/writer/spirv/builder.cc
+++ b/src/tint/writer/spirv/builder.cc
@@ -3955,11 +3955,7 @@
     if (matrix_type) {
         push_annot(spv::Op::OpMemberDecorate,
                    {Operand(struct_id), Operand(idx), U32Operand(SpvDecorationColMajor)});
-        if (!matrix_type->type()->Is<sem::F32>()) {
-            error_ = "matrix scalar element type must be f32";
-            return 0;
-        }
-        const uint32_t scalar_elem_size = 4;
+        const uint32_t scalar_elem_size = matrix_type->type()->Size();
         const uint32_t effective_row_count = (matrix_type->rows() == 2) ? 2 : 4;
         push_annot(spv::Op::OpMemberDecorate,
                    {Operand(struct_id), Operand(idx), U32Operand(SpvDecorationMatrixStride),
diff --git a/src/tint/writer/spirv/builder_builtin_test.cc b/src/tint/writer/spirv/builder_builtin_test.cc
index c0995e8..caa7d9c 100644
--- a/src/tint/writer/spirv/builder_builtin_test.cc
+++ b/src/tint/writer/spirv/builder_builtin_test.cc
@@ -1653,7 +1653,7 @@
 OpExecutionMode %3 OriginUpperLeft
 OpName %3 "a_func"
 OpName %10 "vec"
-OpName %14 "__modf_result_vec2"
+OpName %14 "__modf_result_vec2_f32"
 OpMemberName %14 0 "fract"
 OpMemberName %14 1 "whole"
 OpMemberDecorate %14 0 Offset 0
@@ -1760,7 +1760,7 @@
 OpEntryPoint Fragment %3 "a_func"
 OpExecutionMode %3 OriginUpperLeft
 OpName %3 "a_func"
-OpName %6 "__modf_result_vec2"
+OpName %6 "__modf_result_vec2_f32"
 OpMemberName %6 0 "fract"
 OpMemberName %6 1 "whole"
 OpMemberDecorate %6 0 Offset 0
@@ -1834,7 +1834,7 @@
     Validate(b);
 }
 
-TEST_F(BuiltinBuilderTest, Call_Frexp_f32) {
+TEST_F(BuiltinBuilderTest, Runtime_Call_Frexp_f32) {
     auto* vec = Var("vec", vec2<f32>(1_f, 2_f));
     auto* expr = Call("frexp", vec);
     Func("a_func", utils::Empty, ty.void_(),
@@ -1857,7 +1857,7 @@
 OpExecutionMode %3 OriginUpperLeft
 OpName %3 "a_func"
 OpName %10 "vec"
-OpName %14 "__frexp_result_vec2"
+OpName %14 "__frexp_result_vec2_f32"
 OpMemberName %14 0 "fract"
 OpMemberName %14 1 "exp"
 OpMemberDecorate %14 0 Offset 0
@@ -1888,7 +1888,7 @@
     Validate(b);
 }
 
-TEST_F(BuiltinBuilderTest, Call_Frexp_f16) {
+TEST_F(BuiltinBuilderTest, Runtime_Call_Frexp_f16) {
     Enable(ast::Extension::kF16);
 
     auto* vec = Var("vec", vec2<f16>(1_h, 2_h));
@@ -1948,15 +1948,10 @@
     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));
+TEST_F(BuiltinBuilderTest, Const_Call_Frexp_f32) {
     Func("a_func", utils::Empty, ty.void_(),
          utils::Vector{
-             Decl(vec),
-             Decl(Let("s", MemberAccessor(Call("frexp", vec), "sig"))),
+             CallStmt(Call("frexp", vec2<f32>(1_f, 2_f))),
          },
          utils::Vector{
              Stage(ast::PipelineStage::kFragment),
@@ -1967,51 +1962,134 @@
     ASSERT_TRUE(b.Build()) << b.error();
     auto got = DumpBuilder(b);
     auto* expect = R"(OpCapability Shader
-%9 = OpExtInstImport "GLSL.std.450"
+%11 = 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"
+OpEntryPoint Fragment %3 "a_func"
+OpExecutionMode %3 OriginUpperLeft
+OpName %3 "a_func"
+OpName %6 "__frexp_result_vec2_f32"
 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
+OpMemberDecorate %6 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
+%8 = OpTypeFloat 32
+%7 = OpTypeVector %8 2
+%10 = OpTypeInt 32 1
+%9 = OpTypeVector %10 2
+%6 = OpTypeStruct %7 %9
+%12 = OpConstant %8 1
+%13 = OpConstant %8 2
+%14 = OpConstantComposite %7 %12 %13
 %3 = OpFunction %2 None %1
 %4 = OpLabel
-%5 = OpExtInst %6 %9 FrexpStruct %10
-%11 = OpCompositeExtract %7 %5 0
+%5 = OpExtInst %6 %11 FrexpStruct %14
 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
+)";
+    EXPECT_EQ(expect, got);
+
+    Validate(b);
+}
+
+TEST_F(BuiltinBuilderTest, Const_Call_Frexp_f16) {
+    Enable(ast::Extension::kF16);
+
+    Func("a_func", utils::Empty, ty.void_(),
+         utils::Vector{
+             CallStmt(Call("frexp", vec2<f16>(1_h, 2_h))),
+         },
+         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
+%11 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %3 "a_func"
+OpExecutionMode %3 OriginUpperLeft
+OpName %3 "a_func"
+OpName %6 "__frexp_result_vec2_f16"
+OpMemberName %6 0 "fract"
+OpMemberName %6 1 "exp"
+OpMemberDecorate %6 0 Offset 0
+OpMemberDecorate %6 1 Offset 8
+%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%8 = OpTypeFloat 16
+%7 = OpTypeVector %8 2
+%10 = OpTypeInt 32 1
+%9 = OpTypeVector %10 2
+%6 = OpTypeStruct %7 %9
+%12 = OpConstant %8 0x1p+0
+%13 = OpConstant %8 0x1p+1
+%14 = OpConstantComposite %7 %12 %13
+%3 = OpFunction %2 None %1
+%4 = OpLabel
+%5 = OpExtInst %6 %11 FrexpStruct %14
+OpReturn
+OpFunctionEnd
+)";
+    EXPECT_EQ(expect, got);
+
+    Validate(b);
+}
+
+// TODO(crbug.com/tint/1757): Remove once deprecation period for `frexp().sig` is over
+TEST_F(BuiltinBuilderTest, Frexp_Sig_Deprecation) {
+    Func("a_func", utils::Empty, ty.void_(),
+         utils::Vector{
+             Decl(Var("vec", vec2<f32>(1_f, 2_f))),
+             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
+%17 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %3 "a_func"
+OpExecutionMode %3 OriginUpperLeft
+OpName %3 "a_func"
+OpName %10 "vec"
+OpName %14 "__frexp_result_vec2_f32"
+OpMemberName %14 0 "fract"
+OpMemberName %14 1 "exp"
+OpMemberDecorate %14 0 Offset 0
+OpMemberDecorate %14 1 Offset 8
+%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%5 = OpTypeVector %6 2
+%7 = OpConstant %6 1
+%8 = OpConstant %6 2
+%9 = OpConstantComposite %5 %7 %8
+%11 = OpTypePointer Function %5
+%12 = OpConstantNull %5
+%16 = OpTypeInt 32 1
+%15 = OpTypeVector %16 2
+%14 = OpTypeStruct %5 %15
+%3 = OpFunction %2 None %1
+%4 = OpLabel
+%10 = OpVariable %11 Function %12
+OpStore %10 %9
+%18 = OpLoad %5 %10
+%13 = OpExtInst %14 %17 FrexpStruct %18
+%19 = OpCompositeExtract %5 %13 0
 OpReturn
 OpFunctionEnd
 )";
diff --git a/src/tint/writer/spirv/builder_type_test.cc b/src/tint/writer/spirv/builder_type_test.cc
index a17dcb5..4377c42 100644
--- a/src/tint/writer/spirv/builder_type_test.cc
+++ b/src/tint/writer/spirv/builder_type_test.cc
@@ -317,7 +317,9 @@
 }
 
 TEST_F(BuilderTest_Type, GenerateStruct) {
-    auto* s = Structure("my_struct", utils::Vector{Member("a", ty.f32())});
+    Enable(ast::Extension::kF16);
+
+    auto* s = Structure("my_struct", utils::Vector{Member("a", ty.f32()), Member("b", ty.f16())});
 
     spirv::Builder& b = Build();
 
@@ -326,17 +328,23 @@
     EXPECT_EQ(id, 1u);
 
     EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
-%1 = OpTypeStruct %2
+%3 = OpTypeFloat 16
+%1 = OpTypeStruct %2 %3
 )");
     EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %1 "my_struct"
 OpMemberName %1 0 "a"
+OpMemberName %1 1 "b"
 )");
 }
 
 TEST_F(BuilderTest_Type, GenerateStruct_DecoratedMembers) {
+    Enable(ast::Extension::kF16);
+
     auto* s = Structure("S", utils::Vector{
                                  Member("a", ty.f32()),
                                  Member("b", ty.f32(), utils::Vector{MemberAlign(8_i)}),
+                                 Member("c", ty.f16(), utils::Vector{MemberAlign(8_u)}),
+                                 Member("d", ty.f16()),
                              });
 
     spirv::Builder& b = Build();
@@ -346,23 +354,34 @@
     EXPECT_EQ(id, 1u);
 
     EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
-%1 = OpTypeStruct %2 %2
+%3 = OpTypeFloat 16
+%1 = OpTypeStruct %2 %2 %3 %3
 )");
     EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %1 "S"
 OpMemberName %1 0 "a"
 OpMemberName %1 1 "b"
+OpMemberName %1 2 "c"
+OpMemberName %1 3 "d"
 )");
     EXPECT_EQ(DumpInstructions(b.annots()), R"(OpMemberDecorate %1 0 Offset 0
 OpMemberDecorate %1 1 Offset 8
+OpMemberDecorate %1 2 Offset 16
+OpMemberDecorate %1 3 Offset 18
 )");
 }
 
-TEST_F(BuilderTest_Type, GenerateStruct_NonLayout_Matrix) {
-    auto* s = Structure("S", utils::Vector{
-                                 Member("a", ty.mat2x2<f32>()),
-                                 Member("b", ty.mat2x3<f32>()),
-                                 Member("c", ty.mat4x4<f32>()),
-                             });
+TEST_F(BuilderTest_Type, GenerateStruct_DecoratedMembers_Matrix) {
+    Enable(ast::Extension::kF16);
+
+    auto* s =
+        Structure("S", utils::Vector{
+                           Member("mat2x2_f32", ty.mat2x2<f32>()),
+                           Member("mat2x3_f32", ty.mat2x3<f32>(), utils::Vector{MemberAlign(64_i)}),
+                           Member("mat4x4_f32", ty.mat4x4<f32>()),
+                           Member("mat2x2_f16", ty.mat2x2<f16>(), utils::Vector{MemberAlign(32_i)}),
+                           Member("mat2x3_f16", ty.mat2x3<f16>()),
+                           Member("mat4x4_f16", ty.mat4x4<f16>(), utils::Vector{MemberAlign(64_i)}),
+                       });
 
     spirv::Builder& b = Build();
 
@@ -377,78 +396,63 @@
 %5 = OpTypeMatrix %6 2
 %8 = OpTypeVector %4 4
 %7 = OpTypeMatrix %8 4
-%1 = OpTypeStruct %2 %5 %7
+%11 = OpTypeFloat 16
+%10 = OpTypeVector %11 2
+%9 = OpTypeMatrix %10 2
+%13 = OpTypeVector %11 3
+%12 = OpTypeMatrix %13 2
+%15 = OpTypeVector %11 4
+%14 = OpTypeMatrix %15 4
+%1 = OpTypeStruct %2 %5 %7 %9 %12 %14
 )");
     EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %1 "S"
-OpMemberName %1 0 "a"
-OpMemberName %1 1 "b"
-OpMemberName %1 2 "c"
+OpMemberName %1 0 "mat2x2_f32"
+OpMemberName %1 1 "mat2x3_f32"
+OpMemberName %1 2 "mat4x4_f32"
+OpMemberName %1 3 "mat2x2_f16"
+OpMemberName %1 4 "mat2x3_f16"
+OpMemberName %1 5 "mat4x4_f16"
 )");
     EXPECT_EQ(DumpInstructions(b.annots()), R"(OpMemberDecorate %1 0 Offset 0
 OpMemberDecorate %1 0 ColMajor
 OpMemberDecorate %1 0 MatrixStride 8
-OpMemberDecorate %1 1 Offset 16
+OpMemberDecorate %1 1 Offset 64
 OpMemberDecorate %1 1 ColMajor
 OpMemberDecorate %1 1 MatrixStride 16
-OpMemberDecorate %1 2 Offset 48
+OpMemberDecorate %1 2 Offset 96
 OpMemberDecorate %1 2 ColMajor
 OpMemberDecorate %1 2 MatrixStride 16
+OpMemberDecorate %1 3 Offset 160
+OpMemberDecorate %1 3 ColMajor
+OpMemberDecorate %1 3 MatrixStride 4
+OpMemberDecorate %1 4 Offset 168
+OpMemberDecorate %1 4 ColMajor
+OpMemberDecorate %1 4 MatrixStride 8
+OpMemberDecorate %1 5 Offset 192
+OpMemberDecorate %1 5 ColMajor
+OpMemberDecorate %1 5 MatrixStride 8
 )");
 }
 
-TEST_F(BuilderTest_Type, GenerateStruct_DecoratedMembers_LayoutMatrix) {
-    // We have to infer layout for matrix when it also has an offset.
-    auto* s = Structure("S", utils::Vector{
-                                 Member("a", ty.mat2x2<f32>()),
-                                 Member("b", ty.mat2x3<f32>()),
-                                 Member("c", ty.mat4x4<f32>()),
-                             });
+TEST_F(BuilderTest_Type, GenerateStruct_DecoratedMembers_ArraysOfMatrix) {
+    Enable(ast::Extension::kF16);
 
-    spirv::Builder& b = Build();
-
-    auto id = b.GenerateTypeIfNeeded(program->TypeOf(s));
-    ASSERT_FALSE(b.has_error()) << b.error();
-    EXPECT_EQ(id, 1u);
-
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32
-%3 = OpTypeVector %4 2
-%2 = OpTypeMatrix %3 2
-%6 = OpTypeVector %4 3
-%5 = OpTypeMatrix %6 2
-%8 = OpTypeVector %4 4
-%7 = OpTypeMatrix %8 4
-%1 = OpTypeStruct %2 %5 %7
-)");
-    EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %1 "S"
-OpMemberName %1 0 "a"
-OpMemberName %1 1 "b"
-OpMemberName %1 2 "c"
-)");
-    EXPECT_EQ(DumpInstructions(b.annots()), R"(OpMemberDecorate %1 0 Offset 0
-OpMemberDecorate %1 0 ColMajor
-OpMemberDecorate %1 0 MatrixStride 8
-OpMemberDecorate %1 1 Offset 16
-OpMemberDecorate %1 1 ColMajor
-OpMemberDecorate %1 1 MatrixStride 16
-OpMemberDecorate %1 2 Offset 48
-OpMemberDecorate %1 2 ColMajor
-OpMemberDecorate %1 2 MatrixStride 16
-)");
-}
-
-TEST_F(BuilderTest_Type, GenerateStruct_DecoratedMembers_LayoutArraysOfMatrix) {
-    // We have to infer layout for matrix when it also has an offset.
-    // The decoration goes on the struct member, even if the matrix is buried
-    // in levels of arrays.
-    auto* arr_mat2x2 = ty.array(ty.mat2x2<f32>(), 1_u);      // Singly nested array
-    auto* arr_arr_mat2x3 = ty.array(ty.mat2x3<f32>(), 1_u);  // Doubly nested array
+    auto* arr_mat2x2_f32 = ty.array(ty.mat2x2<f32>(), 1_u);  // Singly nested array
+    auto* arr_mat2x2_f16 = ty.array(ty.mat2x2<f16>(), 1_u);  // Singly nested array
+    auto* arr_arr_mat2x3_f32 =
+        ty.array(ty.array(ty.mat2x3<f32>(), 1_u), 2_u);  // Doubly nested array
+    auto* arr_arr_mat2x3_f16 =
+        ty.array(ty.array(ty.mat2x3<f16>(), 1_u), 2_u);      // Doubly nested array
     auto* rtarr_mat4x4 = ty.array(ty.mat4x4<f32>());         // Runtime array
 
-    auto* s = Structure("S", utils::Vector{
-                                 Member("a", arr_mat2x2),
-                                 Member("b", arr_arr_mat2x3),
-                                 Member("c", rtarr_mat4x4),
-                             });
+    auto* s = Structure(
+        "S", utils::Vector{
+                 Member("arr_mat2x2_f32", arr_mat2x2_f32),
+                 Member("arr_mat2x2_f16", arr_mat2x2_f16, utils::Vector{MemberAlign(64_i)}),
+                 Member("arr_arr_mat2x3_f32", arr_arr_mat2x3_f32, utils::Vector{MemberAlign(64_i)}),
+                 Member("arr_arr_mat2x3_f16", arr_arr_mat2x3_f16),
+                 Member("rtarr_mat4x4", rtarr_mat4x4),
+             });
 
     spirv::Builder& b = Build();
 
@@ -462,31 +466,53 @@
 %6 = OpTypeInt 32 0
 %7 = OpConstant %6 1
 %2 = OpTypeArray %3 %7
-%10 = OpTypeVector %5 3
+%11 = OpTypeFloat 16
+%10 = OpTypeVector %11 2
 %9 = OpTypeMatrix %10 2
 %8 = OpTypeArray %9 %7
-%13 = OpTypeVector %5 4
-%12 = OpTypeMatrix %13 4
-%11 = OpTypeRuntimeArray %12
-%1 = OpTypeStruct %2 %8 %11
+%15 = OpTypeVector %5 3
+%14 = OpTypeMatrix %15 2
+%13 = OpTypeArray %14 %7
+%16 = OpConstant %6 2
+%12 = OpTypeArray %13 %16
+%20 = OpTypeVector %11 3
+%19 = OpTypeMatrix %20 2
+%18 = OpTypeArray %19 %7
+%17 = OpTypeArray %18 %16
+%23 = OpTypeVector %5 4
+%22 = OpTypeMatrix %23 4
+%21 = OpTypeRuntimeArray %22
+%1 = OpTypeStruct %2 %8 %12 %17 %21
 )");
     EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %1 "S"
-OpMemberName %1 0 "a"
-OpMemberName %1 1 "b"
-OpMemberName %1 2 "c"
+OpMemberName %1 0 "arr_mat2x2_f32"
+OpMemberName %1 1 "arr_mat2x2_f16"
+OpMemberName %1 2 "arr_arr_mat2x3_f32"
+OpMemberName %1 3 "arr_arr_mat2x3_f16"
+OpMemberName %1 4 "rtarr_mat4x4"
 )");
     EXPECT_EQ(DumpInstructions(b.annots()), R"(OpMemberDecorate %1 0 Offset 0
 OpMemberDecorate %1 0 ColMajor
 OpMemberDecorate %1 0 MatrixStride 8
 OpDecorate %2 ArrayStride 16
-OpMemberDecorate %1 1 Offset 16
+OpMemberDecorate %1 1 Offset 64
 OpMemberDecorate %1 1 ColMajor
-OpMemberDecorate %1 1 MatrixStride 16
-OpDecorate %8 ArrayStride 32
-OpMemberDecorate %1 2 Offset 48
+OpMemberDecorate %1 1 MatrixStride 4
+OpDecorate %8 ArrayStride 8
+OpMemberDecorate %1 2 Offset 128
 OpMemberDecorate %1 2 ColMajor
 OpMemberDecorate %1 2 MatrixStride 16
-OpDecorate %11 ArrayStride 64
+OpDecorate %13 ArrayStride 32
+OpDecorate %12 ArrayStride 32
+OpMemberDecorate %1 3 Offset 192
+OpMemberDecorate %1 3 ColMajor
+OpMemberDecorate %1 3 MatrixStride 8
+OpDecorate %18 ArrayStride 16
+OpDecorate %17 ArrayStride 16
+OpMemberDecorate %1 4 Offset 224
+OpMemberDecorate %1 4 ColMajor
+OpMemberDecorate %1 4 MatrixStride 16
+OpDecorate %21 ArrayStride 64
 )");
 }