Import Tint changes from Dawn

Changes:
  - 526e4b8b57dbf4bca8142bcb15d046c5c48009ad [spirv-reader] Handle pointer to vector component by James Price <jrprice@google.com>
  - 03ecbbf5caa0ebc68d95b7023c62bddeb81f39a7 [ir] Add vector-element-ptr validation capability by James Price <jrprice@google.com>
  - bd27f9c60dd261b6f685f5d5674cc68964c6e298 [spirv-reader] Fix test helper failure returns by James Price <jrprice@google.com>
  - 96b7fdaf73e3815e3519cf8cc957be972e9c0f87 [tint][ir][DirectVariableAccess] Preserve original fn names by Ben Clayton <bclayton@google.com>
  - f4686675192881b3f18a8c6f708ba67edf442543 [tint][wgsl] Add unrestricted_pointer_parameters feature by Ben Clayton <bclayton@google.com>
  - 2d501c5575e62e0a813c077d8fa6182bc7cc4496 [tint] Fix emission of module-scope struct initializers by Ben Clayton <bclayton@google.com>
GitOrigin-RevId: 526e4b8b57dbf4bca8142bcb15d046c5c48009ad
Change-Id: I6c74c1e239b69c83d16e63a300be6f5d70914e50
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/170320
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/api/BUILD.bazel b/src/tint/api/BUILD.bazel
index 32a7773..9f218bd 100644
--- a/src/tint/api/BUILD.bazel
+++ b/src/tint/api/BUILD.bazel
@@ -52,7 +52,6 @@
     "//src/tint/lang/core/ir",
     "//src/tint/lang/core/type",
     "//src/tint/lang/hlsl/writer/common",
-    "//src/tint/lang/spirv/reader/common",
     "//src/tint/lang/wgsl",
     "//src/tint/lang/wgsl/ast",
     "//src/tint/lang/wgsl/common",
@@ -93,6 +92,7 @@
   }) + select({
     ":tint_build_spv_reader": [
       "//src/tint/lang/spirv/reader",
+      "//src/tint/lang/spirv/reader/common",
     ],
     "//conditions:default": [],
   }) + select({
diff --git a/src/tint/api/BUILD.cmake b/src/tint/api/BUILD.cmake
index af664e6..9551324 100644
--- a/src/tint/api/BUILD.cmake
+++ b/src/tint/api/BUILD.cmake
@@ -54,7 +54,6 @@
   tint_lang_core_ir
   tint_lang_core_type
   tint_lang_hlsl_writer_common
-  tint_lang_spirv_reader_common
   tint_lang_wgsl
   tint_lang_wgsl_ast
   tint_lang_wgsl_common
@@ -100,6 +99,7 @@
 if(TINT_BUILD_SPV_READER)
   tint_target_add_dependencies(tint_api lib
     tint_lang_spirv_reader
+    tint_lang_spirv_reader_common
   )
 endif(TINT_BUILD_SPV_READER)
 
diff --git a/src/tint/api/BUILD.gn b/src/tint/api/BUILD.gn
index ff3f483..80382c0 100644
--- a/src/tint/api/BUILD.gn
+++ b/src/tint/api/BUILD.gn
@@ -51,7 +51,6 @@
     "${tint_src_dir}/lang/core/ir",
     "${tint_src_dir}/lang/core/type",
     "${tint_src_dir}/lang/hlsl/writer/common",
-    "${tint_src_dir}/lang/spirv/reader/common",
     "${tint_src_dir}/lang/wgsl",
     "${tint_src_dir}/lang/wgsl/ast",
     "${tint_src_dir}/lang/wgsl/common",
@@ -93,7 +92,10 @@
   }
 
   if (tint_build_spv_reader) {
-    deps += [ "${tint_src_dir}/lang/spirv/reader" ]
+    deps += [
+      "${tint_src_dir}/lang/spirv/reader",
+      "${tint_src_dir}/lang/spirv/reader/common",
+    ]
   }
 
   if (tint_build_spv_writer) {
diff --git a/src/tint/cmd/bench/BUILD.bazel b/src/tint/cmd/bench/BUILD.bazel
index a273bc9..743f284 100644
--- a/src/tint/cmd/bench/BUILD.bazel
+++ b/src/tint/cmd/bench/BUILD.bazel
@@ -49,7 +49,6 @@
     "//src/tint/lang/core/constant",
     "//src/tint/lang/core/ir",
     "//src/tint/lang/core/type",
-    "//src/tint/lang/spirv/reader/common",
     "//src/tint/lang/wgsl",
     "//src/tint/lang/wgsl/ast",
     "//src/tint/lang/wgsl/common",
@@ -74,6 +73,7 @@
   ] + select({
     ":tint_build_spv_reader": [
       "//src/tint/lang/spirv/reader",
+      "//src/tint/lang/spirv/reader/common",
     ],
     "//conditions:default": [],
   }) + select({
diff --git a/src/tint/cmd/bench/BUILD.cmake b/src/tint/cmd/bench/BUILD.cmake
index ee4b122..cdc5971 100644
--- a/src/tint/cmd/bench/BUILD.cmake
+++ b/src/tint/cmd/bench/BUILD.cmake
@@ -126,7 +126,6 @@
   tint_lang_core_constant
   tint_lang_core_ir
   tint_lang_core_type
-  tint_lang_spirv_reader_common
   tint_lang_wgsl
   tint_lang_wgsl_ast
   tint_lang_wgsl_common
@@ -156,6 +155,7 @@
 if(TINT_BUILD_SPV_READER)
   tint_target_add_dependencies(tint_cmd_bench_bench bench
     tint_lang_spirv_reader
+    tint_lang_spirv_reader_common
   )
 endif(TINT_BUILD_SPV_READER)
 
diff --git a/src/tint/cmd/bench/BUILD.gn b/src/tint/cmd/bench/BUILD.gn
index 7eb287d..38bdc6a 100644
--- a/src/tint/cmd/bench/BUILD.gn
+++ b/src/tint/cmd/bench/BUILD.gn
@@ -54,7 +54,6 @@
       "${tint_src_dir}/lang/core/constant",
       "${tint_src_dir}/lang/core/ir",
       "${tint_src_dir}/lang/core/type",
-      "${tint_src_dir}/lang/spirv/reader/common",
       "${tint_src_dir}/lang/wgsl",
       "${tint_src_dir}/lang/wgsl/ast",
       "${tint_src_dir}/lang/wgsl/common",
@@ -78,7 +77,10 @@
     ]
 
     if (tint_build_spv_reader) {
-      deps += [ "${tint_src_dir}/lang/spirv/reader" ]
+      deps += [
+        "${tint_src_dir}/lang/spirv/reader",
+        "${tint_src_dir}/lang/spirv/reader/common",
+      ]
     }
 
     if (tint_build_wgsl_reader) {
diff --git a/src/tint/cmd/common/BUILD.bazel b/src/tint/cmd/common/BUILD.bazel
index c768f73..affb277 100644
--- a/src/tint/cmd/common/BUILD.bazel
+++ b/src/tint/cmd/common/BUILD.bazel
@@ -53,7 +53,6 @@
     "//src/tint/lang/core/constant",
     "//src/tint/lang/core/ir",
     "//src/tint/lang/core/type",
-    "//src/tint/lang/spirv/reader/common",
     "//src/tint/lang/wgsl",
     "//src/tint/lang/wgsl/ast",
     "//src/tint/lang/wgsl/common",
@@ -78,6 +77,7 @@
   ] + select({
     ":tint_build_spv_reader": [
       "//src/tint/lang/spirv/reader",
+      "//src/tint/lang/spirv/reader/common",
     ],
     "//conditions:default": [],
   }) + select({
diff --git a/src/tint/cmd/common/BUILD.cmake b/src/tint/cmd/common/BUILD.cmake
index 43d472f..2c0d823 100644
--- a/src/tint/cmd/common/BUILD.cmake
+++ b/src/tint/cmd/common/BUILD.cmake
@@ -52,7 +52,6 @@
   tint_lang_core_constant
   tint_lang_core_ir
   tint_lang_core_type
-  tint_lang_spirv_reader_common
   tint_lang_wgsl
   tint_lang_wgsl_ast
   tint_lang_wgsl_common
@@ -79,6 +78,7 @@
 if(TINT_BUILD_SPV_READER)
   tint_target_add_dependencies(tint_cmd_common lib
     tint_lang_spirv_reader
+    tint_lang_spirv_reader_common
   )
 endif(TINT_BUILD_SPV_READER)
 
diff --git a/src/tint/cmd/common/BUILD.gn b/src/tint/cmd/common/BUILD.gn
index 4b1f5e3..6026bf5 100644
--- a/src/tint/cmd/common/BUILD.gn
+++ b/src/tint/cmd/common/BUILD.gn
@@ -56,7 +56,6 @@
     "${tint_src_dir}/lang/core/constant",
     "${tint_src_dir}/lang/core/ir",
     "${tint_src_dir}/lang/core/type",
-    "${tint_src_dir}/lang/spirv/reader/common",
     "${tint_src_dir}/lang/wgsl",
     "${tint_src_dir}/lang/wgsl/ast",
     "${tint_src_dir}/lang/wgsl/common",
@@ -81,7 +80,10 @@
   ]
 
   if (tint_build_spv_reader) {
-    deps += [ "${tint_src_dir}/lang/spirv/reader" ]
+    deps += [
+      "${tint_src_dir}/lang/spirv/reader",
+      "${tint_src_dir}/lang/spirv/reader/common",
+    ]
   }
 
   if (tint_build_spv_reader || tint_build_spv_writer) {
diff --git a/src/tint/cmd/fuzz/ir/fuzz.cc b/src/tint/cmd/fuzz/ir/fuzz.cc
index 5fa170f..8fbf4cb 100644
--- a/src/tint/cmd/fuzz/ir/fuzz.cc
+++ b/src/tint/cmd/fuzz/ir/fuzz.cc
@@ -46,7 +46,6 @@
 bool IsUnsupported(const ast::Enable* enable) {
     for (auto ext : enable->extensions) {
         switch (ext->name) {
-            case tint::wgsl::Extension::kChromiumExperimentalFullPtrParameters:
             case tint::wgsl::Extension::kChromiumExperimentalPixelLocal:
             case tint::wgsl::Extension::kChromiumExperimentalPushConstant:
             case tint::wgsl::Extension::kChromiumInternalDualSourceBlending:
diff --git a/src/tint/cmd/info/BUILD.bazel b/src/tint/cmd/info/BUILD.bazel
index c19a5da..6d0b8f9 100644
--- a/src/tint/cmd/info/BUILD.bazel
+++ b/src/tint/cmd/info/BUILD.bazel
@@ -47,7 +47,6 @@
     "//src/tint/lang/core",
     "//src/tint/lang/core/constant",
     "//src/tint/lang/core/type",
-    "//src/tint/lang/spirv/reader/common",
     "//src/tint/lang/wgsl",
     "//src/tint/lang/wgsl/ast",
     "//src/tint/lang/wgsl/common",
@@ -70,6 +69,11 @@
     "//src/tint/utils/text",
     "//src/tint/utils/traits",
   ] + select({
+    ":tint_build_spv_reader": [
+      "//src/tint/lang/spirv/reader/common",
+    ],
+    "//conditions:default": [],
+  }) + select({
     ":tint_build_spv_reader_or_tint_build_spv_writer": [
       "@spirv_tools",
     ],
diff --git a/src/tint/cmd/info/BUILD.cmake b/src/tint/cmd/info/BUILD.cmake
index 74d28e0..dda56b9 100644
--- a/src/tint/cmd/info/BUILD.cmake
+++ b/src/tint/cmd/info/BUILD.cmake
@@ -48,7 +48,6 @@
   tint_lang_core
   tint_lang_core_constant
   tint_lang_core_type
-  tint_lang_spirv_reader_common
   tint_lang_wgsl
   tint_lang_wgsl_ast
   tint_lang_wgsl_common
@@ -72,6 +71,12 @@
   tint_utils_traits
 )
 
+if(TINT_BUILD_SPV_READER)
+  tint_target_add_dependencies(tint_cmd_info_cmd cmd
+    tint_lang_spirv_reader_common
+  )
+endif(TINT_BUILD_SPV_READER)
+
 if(TINT_BUILD_SPV_READER OR TINT_BUILD_SPV_WRITER)
   tint_target_add_external_dependencies(tint_cmd_info_cmd cmd
     "spirv-tools"
diff --git a/src/tint/cmd/info/BUILD.gn b/src/tint/cmd/info/BUILD.gn
index 6f3a945..5f06fcd 100644
--- a/src/tint/cmd/info/BUILD.gn
+++ b/src/tint/cmd/info/BUILD.gn
@@ -47,7 +47,6 @@
     "${tint_src_dir}/lang/core",
     "${tint_src_dir}/lang/core/constant",
     "${tint_src_dir}/lang/core/type",
-    "${tint_src_dir}/lang/spirv/reader/common",
     "${tint_src_dir}/lang/wgsl",
     "${tint_src_dir}/lang/wgsl/ast",
     "${tint_src_dir}/lang/wgsl/common",
@@ -71,6 +70,10 @@
     "${tint_src_dir}/utils/traits",
   ]
 
+  if (tint_build_spv_reader) {
+    deps += [ "${tint_src_dir}/lang/spirv/reader/common" ]
+  }
+
   if (tint_build_spv_reader || tint_build_spv_writer) {
     deps += [
       "${tint_spirv_tools_dir}:spvtools_headers",
diff --git a/src/tint/cmd/loopy/BUILD.bazel b/src/tint/cmd/loopy/BUILD.bazel
index e0b4ebe..1b0859f 100644
--- a/src/tint/cmd/loopy/BUILD.bazel
+++ b/src/tint/cmd/loopy/BUILD.bazel
@@ -51,7 +51,6 @@
     "//src/tint/lang/core/ir",
     "//src/tint/lang/core/type",
     "//src/tint/lang/hlsl/writer/common",
-    "//src/tint/lang/spirv/reader/common",
     "//src/tint/lang/wgsl",
     "//src/tint/lang/wgsl/ast",
     "//src/tint/lang/wgsl/common",
@@ -95,6 +94,7 @@
   }) + select({
     ":tint_build_spv_reader": [
       "//src/tint/lang/spirv/reader",
+      "//src/tint/lang/spirv/reader/common",
     ],
     "//conditions:default": [],
   }) + select({
diff --git a/src/tint/cmd/loopy/BUILD.cmake b/src/tint/cmd/loopy/BUILD.cmake
index abf2e07..2597a47 100644
--- a/src/tint/cmd/loopy/BUILD.cmake
+++ b/src/tint/cmd/loopy/BUILD.cmake
@@ -52,7 +52,6 @@
   tint_lang_core_ir
   tint_lang_core_type
   tint_lang_hlsl_writer_common
-  tint_lang_spirv_reader_common
   tint_lang_wgsl
   tint_lang_wgsl_ast
   tint_lang_wgsl_common
@@ -101,6 +100,7 @@
 if(TINT_BUILD_SPV_READER)
   tint_target_add_dependencies(tint_cmd_loopy_cmd cmd
     tint_lang_spirv_reader
+    tint_lang_spirv_reader_common
   )
 endif(TINT_BUILD_SPV_READER)
 
diff --git a/src/tint/cmd/loopy/BUILD.gn b/src/tint/cmd/loopy/BUILD.gn
index 3241429..5176168 100644
--- a/src/tint/cmd/loopy/BUILD.gn
+++ b/src/tint/cmd/loopy/BUILD.gn
@@ -51,7 +51,6 @@
     "${tint_src_dir}/lang/core/ir",
     "${tint_src_dir}/lang/core/type",
     "${tint_src_dir}/lang/hlsl/writer/common",
-    "${tint_src_dir}/lang/spirv/reader/common",
     "${tint_src_dir}/lang/wgsl",
     "${tint_src_dir}/lang/wgsl/ast",
     "${tint_src_dir}/lang/wgsl/common",
@@ -96,7 +95,10 @@
   }
 
   if (tint_build_spv_reader) {
-    deps += [ "${tint_src_dir}/lang/spirv/reader" ]
+    deps += [
+      "${tint_src_dir}/lang/spirv/reader",
+      "${tint_src_dir}/lang/spirv/reader/common",
+    ]
   }
 
   if (tint_build_spv_writer) {
diff --git a/src/tint/cmd/test/BUILD.bazel b/src/tint/cmd/test/BUILD.bazel
index a44e992..01d6cf6 100644
--- a/src/tint/cmd/test/BUILD.bazel
+++ b/src/tint/cmd/test/BUILD.bazel
@@ -52,6 +52,7 @@
     "//src/tint/lang/core:test",
     "//src/tint/lang/msl/ir:test",
     "//src/tint/lang/spirv/ir:test",
+    "//src/tint/lang/spirv/reader/lower:test",
     "//src/tint/lang/wgsl/ast:test",
     "//src/tint/lang/wgsl/helpers:test",
     "//src/tint/lang/wgsl/program:test",
@@ -122,7 +123,9 @@
     "//conditions:default": [],
   }) + select({
     ":tint_build_spv_reader": [
+      "//src/tint/lang/spirv/reader/common:test",
       "//src/tint/lang/spirv/reader/parser:test",
+      "//src/tint/lang/spirv/reader:test",
     ],
     "//conditions:default": [],
   }) + select({
diff --git a/src/tint/cmd/test/BUILD.cmake b/src/tint/cmd/test/BUILD.cmake
index e79c221..d81fdcb 100644
--- a/src/tint/cmd/test/BUILD.cmake
+++ b/src/tint/cmd/test/BUILD.cmake
@@ -53,6 +53,7 @@
   tint_lang_core_test
   tint_lang_msl_ir_test
   tint_lang_spirv_ir_test
+  tint_lang_spirv_reader_lower_test
   tint_lang_wgsl_ast_test
   tint_lang_wgsl_helpers_test
   tint_lang_wgsl_program_test
@@ -135,7 +136,9 @@
 
 if(TINT_BUILD_SPV_READER)
   tint_target_add_dependencies(tint_cmd_test_test_cmd test_cmd
+    tint_lang_spirv_reader_common_test
     tint_lang_spirv_reader_parser_test
+    tint_lang_spirv_reader_test
   )
 endif(TINT_BUILD_SPV_READER)
 
diff --git a/src/tint/cmd/test/BUILD.gn b/src/tint/cmd/test/BUILD.gn
index 55c146b..abb1fd2 100644
--- a/src/tint/cmd/test/BUILD.gn
+++ b/src/tint/cmd/test/BUILD.gn
@@ -58,6 +58,7 @@
       "${tint_src_dir}/lang/core/type:unittests",
       "${tint_src_dir}/lang/msl/ir:unittests",
       "${tint_src_dir}/lang/spirv/ir:unittests",
+      "${tint_src_dir}/lang/spirv/reader/lower:unittests",
       "${tint_src_dir}/lang/wgsl:unittests",
       "${tint_src_dir}/lang/wgsl/ast:unittests",
       "${tint_src_dir}/lang/wgsl/helpers:unittests",
@@ -128,7 +129,11 @@
     }
 
     if (tint_build_spv_reader) {
-      deps += [ "${tint_src_dir}/lang/spirv/reader/parser:unittests" ]
+      deps += [
+        "${tint_src_dir}/lang/spirv/reader:unittests",
+        "${tint_src_dir}/lang/spirv/reader/common:unittests",
+        "${tint_src_dir}/lang/spirv/reader/parser:unittests",
+      ]
     }
 
     if (tint_build_spv_reader && tint_build_wgsl_reader &&
diff --git a/src/tint/cmd/tint/BUILD.bazel b/src/tint/cmd/tint/BUILD.bazel
index 934ff47..fbc1286 100644
--- a/src/tint/cmd/tint/BUILD.bazel
+++ b/src/tint/cmd/tint/BUILD.bazel
@@ -51,7 +51,6 @@
     "//src/tint/lang/core/ir",
     "//src/tint/lang/core/type",
     "//src/tint/lang/hlsl/writer/common",
-    "//src/tint/lang/spirv/reader/common",
     "//src/tint/lang/wgsl",
     "//src/tint/lang/wgsl/ast",
     "//src/tint/lang/wgsl/ast/transform",
@@ -104,6 +103,11 @@
     ],
     "//conditions:default": [],
   }) + select({
+    ":tint_build_spv_reader": [
+      "//src/tint/lang/spirv/reader/common",
+    ],
+    "//conditions:default": [],
+  }) + select({
     ":tint_build_spv_reader_or_tint_build_spv_writer": [
       "@spirv_tools",
     ],
diff --git a/src/tint/cmd/tint/BUILD.cmake b/src/tint/cmd/tint/BUILD.cmake
index 3924e7d..9a02dfe 100644
--- a/src/tint/cmd/tint/BUILD.cmake
+++ b/src/tint/cmd/tint/BUILD.cmake
@@ -52,7 +52,6 @@
   tint_lang_core_ir
   tint_lang_core_type
   tint_lang_hlsl_writer_common
-  tint_lang_spirv_reader_common
   tint_lang_wgsl
   tint_lang_wgsl_ast
   tint_lang_wgsl_ast_transform
@@ -110,6 +109,12 @@
   )
 endif(TINT_BUILD_MSL_WRITER)
 
+if(TINT_BUILD_SPV_READER)
+  tint_target_add_dependencies(tint_cmd_tint_cmd cmd
+    tint_lang_spirv_reader_common
+  )
+endif(TINT_BUILD_SPV_READER)
+
 if(TINT_BUILD_SPV_READER OR TINT_BUILD_SPV_WRITER)
   tint_target_add_external_dependencies(tint_cmd_tint_cmd cmd
     "spirv-tools"
diff --git a/src/tint/cmd/tint/BUILD.gn b/src/tint/cmd/tint/BUILD.gn
index b9fca30..5d0447f 100644
--- a/src/tint/cmd/tint/BUILD.gn
+++ b/src/tint/cmd/tint/BUILD.gn
@@ -51,7 +51,6 @@
     "${tint_src_dir}/lang/core/ir",
     "${tint_src_dir}/lang/core/type",
     "${tint_src_dir}/lang/hlsl/writer/common",
-    "${tint_src_dir}/lang/spirv/reader/common",
     "${tint_src_dir}/lang/wgsl",
     "${tint_src_dir}/lang/wgsl/ast",
     "${tint_src_dir}/lang/wgsl/ast/transform",
@@ -107,6 +106,10 @@
     ]
   }
 
+  if (tint_build_spv_reader) {
+    deps += [ "${tint_src_dir}/lang/spirv/reader/common" ]
+  }
+
   if (tint_build_spv_reader || tint_build_spv_writer) {
     deps += [
       "${tint_spirv_tools_dir}:spvtools_headers",
diff --git a/src/tint/lang/core/ir/transform/direct_variable_access.cc b/src/tint/lang/core/ir/transform/direct_variable_access.cc
index e62d52b..95a601e 100644
--- a/src/tint/lang/core/ir/transform/direct_variable_access.cc
+++ b/src/tint/lang/core/ir/transform/direct_variable_access.cc
@@ -260,10 +260,12 @@
     /// transforming. These functions will be replaced with variants based on the access shapes.
     void GatherFnsThatNeedForking() {
         for (auto& fn : ir.functions) {
-            for (auto* param : fn->Params()) {
-                if (ParamNeedsTransforming(param)) {
-                    need_forking.Add(fn, fn_info_allocator.Create());
-                    break;
+            if (fn->Alive()) {
+                for (auto* param : fn->Params()) {
+                    if (ParamNeedsTransforming(param)) {
+                        need_forking.Add(fn, fn_info_allocator.Create());
+                        break;
+                    }
                 }
             }
         }
@@ -361,15 +363,9 @@
                 auto* variant_fn = CloneContext{ir}.Clone(target);
                 (*target_info)->ordered_variants.Push(variant_fn);
 
-                // Build a unique name for the variant.
-                if (auto fn_name = ir.NameOf(variant_fn); fn_name.IsValid()) {
-                    StringStream variant_name;
-                    variant_name << fn_name.NameView();
-                    auto params = signature.Keys().Sort();
-                    for (auto param_idx : params) {
-                        variant_name << "_" << AccessShapeName(*signature.Get(param_idx));
-                    }
-                    ir.SetName(variant_fn, variant_name.str());
+                // Copy the original name for the variant
+                if (auto fn_name = ir.NameOf(fn)) {
+                    ir.SetName(fn, fn_name);
                 }
 
                 // Create an entry for the variant, and add it to the queue of variants that need to
@@ -587,37 +583,6 @@
         }
     }
 
-    /// @returns a string describing the given AccessShape, used to suffix the generated function
-    /// variants.
-    std::string AccessShapeName(const AccessShape& shape) {
-        StringStream ss;
-
-        if (auto* global = std::get_if<RootModuleScopeVar>(&shape.root)) {
-            ss << ir.NameOf(global->var).NameView();
-        } else {
-            ss << "P";
-        }
-
-        for (auto& op : shape.ops) {
-            ss << "_";
-
-            if (std::holds_alternative<IndexAccess>(op)) {
-                /// The op uses an index taken from an array parameter.
-                ss << "X";
-                continue;
-            }
-
-            if (auto* access = std::get_if<MemberAccess>(&op); TINT_LIKELY(access)) {
-                ss << access->member->Name().NameView();
-                continue;
-            }
-
-            TINT_ICE() << "unhandled variant for access chain";
-            break;
-        }
-        return ss.str();
-    }
-
     /// @return true if @p param is a pointer parameter that requires transforming, based on the
     /// address space and transform options.
     /// @param param the function parameter
diff --git a/src/tint/lang/core/ir/transform/direct_variable_access_test.cc b/src/tint/lang/core/ir/transform/direct_variable_access_test.cc
index e6244da..4919a26 100644
--- a/src/tint/lang/core/ir/transform/direct_variable_access_test.cc
+++ b/src/tint/lang/core/ir/transform/direct_variable_access_test.cc
@@ -430,7 +430,7 @@
   %U:ptr<uniform, array<array<array<vec4<i32>, 8>, 8>, 8>, read> = var @binding_point(0, 0)
 }
 
-%a_U_X_X_X = func(%pre:i32, %p_indices:array<u32, 3>, %post:i32):vec4<i32> -> %b2 {
+%a = func(%pre:i32, %p_indices:array<u32, 3>, %post:i32):vec4<i32> -> %b2 {
   %b2 = block {
     %6:u32 = access %p_indices, 0u
     %7:u32 = access %p_indices, 1u
@@ -446,23 +446,23 @@
     %13:u32 = convert 2i
     %14:u32 = convert 1i
     %15:array<u32, 3> = construct %14, %13, %12
-    %16:vec4<i32> = call %a_U_X_X_X, 10i, %15, 20i
+    %16:vec4<i32> = call %a, 10i, %15, 20i
     ret
   }
 }
-%c_U = func():void -> %b4 {
+%c = func():void -> %b4 {
   %b4 = block {
     %18:u32 = convert 3i
     %19:u32 = convert 2i
     %20:u32 = convert 1i
     %21:array<u32, 3> = construct %20, %19, %18
-    %22:vec4<i32> = call %a_U_X_X_X, 10i, %21, 20i
+    %22:vec4<i32> = call %a, 10i, %21, 20i
     ret
   }
 }
 %d = func():void -> %b5 {
   %b5 = block {
-    %24:void = call %c_U
+    %24:void = call %c
     ret
   }
 }
@@ -644,7 +644,7 @@
     ret %14
   }
 }
-%a_U_X_X_X = func(%pre:i32, %p_indices:array<u32, 3>, %post:i32):vec4<i32> -> %b5 {
+%a = func(%pre:i32, %p_indices:array<u32, 3>, %post:i32):vec4<i32> -> %b5 {
   %b5 = block {
     %19:u32 = access %p_indices, 0u
     %20:u32 = access %p_indices, 1u
@@ -663,11 +663,11 @@
     %29:u32 = convert %27
     %30:u32 = convert %25
     %31:array<u32, 3> = construct %30, %28, %29
-    %32:vec4<i32> = call %a_U_X_X_X, 10i, %31, 20i
+    %32:vec4<i32> = call %a, 10i, %31, 20i
     ret
   }
 }
-%c_U = func():void -> %b7 {
+%c = func():void -> %b7 {
   %b7 = block {
     %34:i32 = call %first
     %35:i32 = call %second
@@ -676,13 +676,13 @@
     %38:u32 = convert %36
     %39:u32 = convert %34
     %40:array<u32, 3> = construct %39, %37, %38
-    %41:vec4<i32> = call %a_U_X_X_X, 10i, %40, 20i
+    %41:vec4<i32> = call %a, 10i, %40, 20i
     ret
   }
 }
 %d = func():void -> %b8 {
   %b8 = block {
-    %43:void = call %c_U
+    %43:void = call %c
     ret
   }
 }
@@ -751,7 +751,7 @@
   %U:ptr<uniform, i32, read> = var @binding_point(0, 0)
 }
 
-%a_U = func(%pre:i32, %post:i32):i32 -> %b2 {
+%a = func(%pre:i32, %post:i32):i32 -> %b2 {
   %b2 = block {
     %5:ptr<uniform, i32, read> = access %U
     %6:i32 = load %5
@@ -760,7 +760,7 @@
 }
 %b = func():void -> %b3 {
   %b3 = block {
-    %8:i32 = call %a_U, 10i, 20i
+    %8:i32 = call %a, 10i, 20i
     ret
   }
 }
@@ -824,7 +824,7 @@
   %U:ptr<uniform, array<vec4<i32>, 8>, read> = var @binding_point(0, 0)
 }
 
-%a_U_X = func(%pre:i32, %p_indices:array<u32, 1>, %post:i32):vec4<i32> -> %b2 {
+%a = func(%pre:i32, %p_indices:array<u32, 1>, %post:i32):vec4<i32> -> %b2 {
   %b2 = block {
     %6:u32 = access %p_indices, 0u
     %7:ptr<uniform, vec4<i32>, read> = access %U, %6
@@ -837,7 +837,7 @@
     %I:i32 = let 3i
     %11:u32 = convert %I
     %12:array<u32, 1> = construct %11
-    %13:vec4<i32> = call %a_U_X, 10i, %12, 20i
+    %13:vec4<i32> = call %a, 10i, %12, 20i
     ret
   }
 }
@@ -1037,7 +1037,7 @@
   %U:ptr<uniform, Outer, read> = var @binding_point(0, 0)
 }
 
-%f0_U_mat_X = func(%p_indices:array<u32, 1>):f32 -> %b2 {
+%f0 = func(%p_indices:array<u32, 1>):f32 -> %b2 {
   %b2 = block {
     %4:u32 = access %p_indices, 0u
     %5:ptr<uniform, vec4<f32>, read> = access %U, 1u, %4
@@ -1045,7 +1045,7 @@
     ret %6
   }
 }
-%f0_U_arr_X_mat_X = func(%p_indices_1:array<u32, 2>):f32 -> %b3 {  # %p_indices_1: 'p_indices'
+%f0_1 = func(%p_indices_1:array<u32, 2>):f32 -> %b3 {  # %f0_1: 'f0', %p_indices_1: 'p_indices'
   %b3 = block {
     %9:u32 = access %p_indices_1, 0u
     %10:u32 = access %p_indices_1, 1u
@@ -1054,32 +1054,32 @@
     ret %12
   }
 }
-%f1_U_mat = func():f32 -> %b4 {
+%f1 = func():f32 -> %b4 {
   %b4 = block {
     %res:ptr<function, f32, read_write> = var
     %15:u32 = convert 1i
     %16:array<u32, 1> = construct %15
-    %17:f32 = call %f0_U_mat_X, %16
+    %17:f32 = call %f0, %16
     %18:f32 = load %res
     %19:f32 = add %18, %17
     store %res, %19
     %20:u32 = convert 1i
     %21:array<u32, 1> = construct %20
-    %22:f32 = call %f0_U_mat_X, %21
+    %22:f32 = call %f0, %21
     %23:f32 = load %res
     %24:f32 = add %23, %22
     store %res, %24
     %25:u32 = convert 2i
     %26:u32 = convert 1i
     %27:array<u32, 2> = construct %25, %26
-    %28:f32 = call %f0_U_arr_X_mat_X, %27
+    %28:f32 = call %f0_1, %27
     %29:f32 = load %res
     %30:f32 = add %29, %28
     store %res, %30
     %31:u32 = convert 2i
     %32:u32 = convert 1i
     %33:array<u32, 2> = construct %31, %32
-    %34:f32 = call %f0_U_arr_X_mat_X, %33
+    %34:f32 = call %f0_1, %33
     %35:f32 = load %res
     %36:f32 = add %35, %34
     store %res, %36
@@ -1087,33 +1087,33 @@
     ret %37
   }
 }
-%f1_U_arr_X_mat = func(%p_indices_2:array<u32, 1>):f32 -> %b5 {  # %p_indices_2: 'p_indices'
+%f1_1 = func(%p_indices_2:array<u32, 1>):f32 -> %b5 {  # %f1_1: 'f1', %p_indices_2: 'p_indices'
   %b5 = block {
     %40:u32 = access %p_indices_2, 0u
     %res_1:ptr<function, f32, read_write> = var  # %res_1: 'res'
     %42:u32 = convert 1i
     %43:array<u32, 2> = construct %40, %42
-    %44:f32 = call %f0_U_arr_X_mat_X, %43
+    %44:f32 = call %f0_1, %43
     %45:f32 = load %res_1
     %46:f32 = add %45, %44
     store %res_1, %46
     %47:u32 = convert 1i
     %48:array<u32, 2> = construct %40, %47
-    %49:f32 = call %f0_U_arr_X_mat_X, %48
+    %49:f32 = call %f0_1, %48
     %50:f32 = load %res_1
     %51:f32 = add %50, %49
     store %res_1, %51
     %52:u32 = convert 2i
     %53:u32 = convert 1i
     %54:array<u32, 2> = construct %52, %53
-    %55:f32 = call %f0_U_arr_X_mat_X, %54
+    %55:f32 = call %f0_1, %54
     %56:f32 = load %res_1
     %57:f32 = add %56, %55
     store %res_1, %57
     %58:u32 = convert 2i
     %59:u32 = convert 1i
     %60:array<u32, 2> = construct %58, %59
-    %61:f32 = call %f0_U_arr_X_mat_X, %60
+    %61:f32 = call %f0_1, %60
     %62:f32 = load %res_1
     %63:f32 = add %62, %61
     store %res_1, %63
@@ -1121,33 +1121,33 @@
     ret %64
   }
 }
-%f2_U_arr_X = func(%p_indices_3:array<u32, 1>):f32 -> %b6 {  # %p_indices_3: 'p_indices'
+%f2 = func(%p_indices_3:array<u32, 1>):f32 -> %b6 {  # %p_indices_3: 'p_indices'
   %b6 = block {
     %67:u32 = access %p_indices_3, 0u
     %68:array<u32, 1> = construct %67
-    %69:f32 = call %f1_U_arr_X_mat, %68
+    %69:f32 = call %f1_1, %68
     ret %69
   }
 }
-%f3_U_arr_U_mat = func():f32 -> %b7 {
+%f3 = func():f32 -> %b7 {
   %b7 = block {
     %71:u32 = convert 3i
     %72:array<u32, 1> = construct %71
-    %73:f32 = call %f2_U_arr_X, %72
-    %74:f32 = call %f1_U_mat
+    %73:f32 = call %f2, %72
+    %74:f32 = call %f1
     %75:f32 = add %73, %74
     ret %75
   }
 }
-%f4_U = func():f32 -> %b8 {
+%f4 = func():f32 -> %b8 {
   %b8 = block {
-    %77:f32 = call %f3_U_arr_U_mat
+    %77:f32 = call %f3
     ret %77
   }
 }
 %b = func():void -> %b9 {
   %b9 = block {
-    %79:f32 = call %f4_U
+    %79:f32 = call %f4
     ret
   }
 }
@@ -1242,7 +1242,7 @@
   %U:ptr<uniform, array<array<array<vec4<i32>, 5>, 5>, 5>, read> = var @binding_point(0, 0)
 }
 
-%f2_U_X_X = func(%p_indices:array<u32, 2>):vec4<i32> -> %b2 {
+%f2 = func(%p_indices:array<u32, 2>):vec4<i32> -> %b2 {
   %b2 = block {
     %4:u32 = access %p_indices, 0u
     %5:u32 = access %p_indices, 1u
@@ -1252,24 +1252,24 @@
     ret %8
   }
 }
-%f1_U_X = func(%p_indices_1:array<u32, 1>):vec4<i32> -> %b3 {  # %p_indices_1: 'p_indices'
+%f1 = func(%p_indices_1:array<u32, 1>):vec4<i32> -> %b3 {  # %p_indices_1: 'p_indices'
   %b3 = block {
     %11:u32 = access %p_indices_1, 0u
     %12:array<u32, 2> = construct %11, 2u
-    %13:vec4<i32> = call %f2_U_X_X, %12
+    %13:vec4<i32> = call %f2, %12
     ret %13
   }
 }
-%f0_U = func():vec4<i32> -> %b4 {
+%f0 = func():vec4<i32> -> %b4 {
   %b4 = block {
     %15:array<u32, 1> = construct 1u
-    %16:vec4<i32> = call %f1_U_X, %15
+    %16:vec4<i32> = call %f1, %15
     ret %16
   }
 }
 %main = func():void -> %b5 {
   %b5 = block {
-    %18:vec4<i32> = call %f0_U
+    %18:vec4<i32> = call %f0
     ret
   }
 }
@@ -1352,7 +1352,7 @@
   %S:ptr<storage, str, read> = var @binding_point(0, 0)
 }
 
-%a_S_i = func(%pre:i32, %post:i32):i32 -> %b2 {
+%a = func(%pre:i32, %post:i32):i32 -> %b2 {
   %b2 = block {
     %5:ptr<storage, i32, read> = access %S, 0u
     %6:i32 = load %5
@@ -1361,7 +1361,7 @@
 }
 %b = func():void -> %b3 {
   %b3 = block {
-    %8:i32 = call %a_S_i, 10i, 20i
+    %8:i32 = call %a, 10i, 20i
     ret
   }
 }
@@ -1439,7 +1439,7 @@
   %S:ptr<storage, str, read_write> = var @binding_point(0, 0)
 }
 
-%a_S_arr = func(%pre:i32, %post:i32):void -> %b2 {
+%a = func(%pre:i32, %post:i32):void -> %b2 {
   %b2 = block {
     %5:ptr<storage, array<i32, 4>, read_write> = access %S, 0u
     store %5, array<i32, 4>(0i)
@@ -1448,7 +1448,7 @@
 }
 %b = func():void -> %b3 {
   %b3 = block {
-    %7:void = call %a_S_arr, 10i, 20i
+    %7:void = call %a, 10i, 20i
     ret
   }
 }
@@ -1515,7 +1515,7 @@
   %S:ptr<storage, array<vec4<i32>, 8>, read_write> = var @binding_point(0, 0)
 }
 
-%a_S_X = func(%pre:i32, %p_indices:array<u32, 1>, %post:i32):void -> %b2 {
+%a = func(%pre:i32, %p_indices:array<u32, 1>, %post:i32):void -> %b2 {
   %b2 = block {
     %6:u32 = access %p_indices, 0u
     %7:ptr<storage, vec4<i32>, read_write> = access %S, %6
@@ -1528,7 +1528,7 @@
     %I:i32 = let 3i
     %10:u32 = convert %I
     %11:array<u32, 1> = construct %10
-    %12:void = call %a_S_X, 10i, %11, 20i
+    %12:void = call %a, 10i, %11, 20i
     ret
   }
 }
@@ -1728,7 +1728,7 @@
   %S:ptr<storage, Outer, read> = var @binding_point(0, 0)
 }
 
-%f0_S_mat_X = func(%p_indices:array<u32, 1>):f32 -> %b2 {
+%f0 = func(%p_indices:array<u32, 1>):f32 -> %b2 {
   %b2 = block {
     %4:u32 = access %p_indices, 0u
     %5:ptr<storage, vec4<f32>, read> = access %S, 1u, %4
@@ -1736,7 +1736,7 @@
     ret %6
   }
 }
-%f0_S_arr_X_mat_X = func(%p_indices_1:array<u32, 2>):f32 -> %b3 {  # %p_indices_1: 'p_indices'
+%f0_1 = func(%p_indices_1:array<u32, 2>):f32 -> %b3 {  # %f0_1: 'f0', %p_indices_1: 'p_indices'
   %b3 = block {
     %9:u32 = access %p_indices_1, 0u
     %10:u32 = access %p_indices_1, 1u
@@ -1745,32 +1745,32 @@
     ret %12
   }
 }
-%f1_S_mat = func():f32 -> %b4 {
+%f1 = func():f32 -> %b4 {
   %b4 = block {
     %res:ptr<function, f32, read_write> = var
     %15:u32 = convert 1i
     %16:array<u32, 1> = construct %15
-    %17:f32 = call %f0_S_mat_X, %16
+    %17:f32 = call %f0, %16
     %18:f32 = load %res
     %19:f32 = add %18, %17
     store %res, %19
     %20:u32 = convert 1i
     %21:array<u32, 1> = construct %20
-    %22:f32 = call %f0_S_mat_X, %21
+    %22:f32 = call %f0, %21
     %23:f32 = load %res
     %24:f32 = add %23, %22
     store %res, %24
     %25:u32 = convert 2i
     %26:u32 = convert 1i
     %27:array<u32, 2> = construct %25, %26
-    %28:f32 = call %f0_S_arr_X_mat_X, %27
+    %28:f32 = call %f0_1, %27
     %29:f32 = load %res
     %30:f32 = add %29, %28
     store %res, %30
     %31:u32 = convert 2i
     %32:u32 = convert 1i
     %33:array<u32, 2> = construct %31, %32
-    %34:f32 = call %f0_S_arr_X_mat_X, %33
+    %34:f32 = call %f0_1, %33
     %35:f32 = load %res
     %36:f32 = add %35, %34
     store %res, %36
@@ -1778,33 +1778,33 @@
     ret %37
   }
 }
-%f1_S_arr_X_mat = func(%p_indices_2:array<u32, 1>):f32 -> %b5 {  # %p_indices_2: 'p_indices'
+%f1_1 = func(%p_indices_2:array<u32, 1>):f32 -> %b5 {  # %f1_1: 'f1', %p_indices_2: 'p_indices'
   %b5 = block {
     %40:u32 = access %p_indices_2, 0u
     %res_1:ptr<function, f32, read_write> = var  # %res_1: 'res'
     %42:u32 = convert 1i
     %43:array<u32, 2> = construct %40, %42
-    %44:f32 = call %f0_S_arr_X_mat_X, %43
+    %44:f32 = call %f0_1, %43
     %45:f32 = load %res_1
     %46:f32 = add %45, %44
     store %res_1, %46
     %47:u32 = convert 1i
     %48:array<u32, 2> = construct %40, %47
-    %49:f32 = call %f0_S_arr_X_mat_X, %48
+    %49:f32 = call %f0_1, %48
     %50:f32 = load %res_1
     %51:f32 = add %50, %49
     store %res_1, %51
     %52:u32 = convert 2i
     %53:u32 = convert 1i
     %54:array<u32, 2> = construct %52, %53
-    %55:f32 = call %f0_S_arr_X_mat_X, %54
+    %55:f32 = call %f0_1, %54
     %56:f32 = load %res_1
     %57:f32 = add %56, %55
     store %res_1, %57
     %58:u32 = convert 2i
     %59:u32 = convert 1i
     %60:array<u32, 2> = construct %58, %59
-    %61:f32 = call %f0_S_arr_X_mat_X, %60
+    %61:f32 = call %f0_1, %60
     %62:f32 = load %res_1
     %63:f32 = add %62, %61
     store %res_1, %63
@@ -1812,33 +1812,33 @@
     ret %64
   }
 }
-%f2_S_arr_X = func(%p_indices_3:array<u32, 1>):f32 -> %b6 {  # %p_indices_3: 'p_indices'
+%f2 = func(%p_indices_3:array<u32, 1>):f32 -> %b6 {  # %p_indices_3: 'p_indices'
   %b6 = block {
     %67:u32 = access %p_indices_3, 0u
     %68:array<u32, 1> = construct %67
-    %69:f32 = call %f1_S_arr_X_mat, %68
+    %69:f32 = call %f1_1, %68
     ret %69
   }
 }
-%f3_S_arr_S_mat = func():f32 -> %b7 {
+%f3 = func():f32 -> %b7 {
   %b7 = block {
     %71:u32 = convert 3i
     %72:array<u32, 1> = construct %71
-    %73:f32 = call %f2_S_arr_X, %72
-    %74:f32 = call %f1_S_mat
+    %73:f32 = call %f2, %72
+    %74:f32 = call %f1
     %75:f32 = add %73, %74
     ret %75
   }
 }
-%f4_S = func():f32 -> %b8 {
+%f4 = func():f32 -> %b8 {
   %b8 = block {
-    %77:f32 = call %f3_S_arr_S_mat
+    %77:f32 = call %f3
     ret %77
   }
 }
 %b = func():void -> %b9 {
   %b9 = block {
-    %79:f32 = call %f4_S
+    %79:f32 = call %f4
     ret
   }
 }
@@ -1933,7 +1933,7 @@
   %U:ptr<storage, array<array<array<vec4<i32>, 5>, 5>, 5>, read_write> = var @binding_point(0, 0)
 }
 
-%f2_U_X_X = func(%p_indices:array<u32, 2>):vec4<i32> -> %b2 {
+%f2 = func(%p_indices:array<u32, 2>):vec4<i32> -> %b2 {
   %b2 = block {
     %4:u32 = access %p_indices, 0u
     %5:u32 = access %p_indices, 1u
@@ -1943,24 +1943,24 @@
     ret %8
   }
 }
-%f1_U_X = func(%p_indices_1:array<u32, 1>):vec4<i32> -> %b3 {  # %p_indices_1: 'p_indices'
+%f1 = func(%p_indices_1:array<u32, 1>):vec4<i32> -> %b3 {  # %p_indices_1: 'p_indices'
   %b3 = block {
     %11:u32 = access %p_indices_1, 0u
     %12:array<u32, 2> = construct %11, 2u
-    %13:vec4<i32> = call %f2_U_X_X, %12
+    %13:vec4<i32> = call %f2, %12
     ret %13
   }
 }
-%f0_U = func():vec4<i32> -> %b4 {
+%f0 = func():vec4<i32> -> %b4 {
   %b4 = block {
     %15:array<u32, 1> = construct 1u
-    %16:vec4<i32> = call %f1_U_X, %15
+    %16:vec4<i32> = call %f1, %15
     ret %16
   }
 }
 %main = func():void -> %b5 {
   %b5 = block {
-    %18:vec4<i32> = call %f0_U
+    %18:vec4<i32> = call %f0
     ret
   }
 }
@@ -2030,7 +2030,7 @@
   %W:ptr<workgroup, array<vec4<i32>, 8>, read_write> = var
 }
 
-%a_W_X = func(%pre:i32, %p_indices:array<u32, 1>, %post:i32):vec4<i32> -> %b2 {
+%a = func(%pre:i32, %p_indices:array<u32, 1>, %post:i32):vec4<i32> -> %b2 {
   %b2 = block {
     %6:u32 = access %p_indices, 0u
     %7:ptr<workgroup, vec4<i32>, read_write> = access %W, %6
@@ -2042,7 +2042,7 @@
   %b3 = block {
     %10:u32 = convert 3i
     %11:array<u32, 1> = construct %10
-    %12:vec4<i32> = call %a_W_X, 10i, %11, 20i
+    %12:vec4<i32> = call %a, 10i, %11, 20i
     ret
   }
 }
@@ -2106,7 +2106,7 @@
   %W:ptr<workgroup, array<vec4<i32>, 8>, read_write> = var
 }
 
-%a_W_X = func(%pre:i32, %p_indices:array<u32, 1>, %post:i32):void -> %b2 {
+%a = func(%pre:i32, %p_indices:array<u32, 1>, %post:i32):void -> %b2 {
   %b2 = block {
     %6:u32 = access %p_indices, 0u
     %7:ptr<workgroup, vec4<i32>, read_write> = access %W, %6
@@ -2118,7 +2118,7 @@
   %b3 = block {
     %9:u32 = convert 3i
     %10:array<u32, 1> = construct %9
-    %11:void = call %a_W_X, 10i, %10, 20i
+    %11:void = call %a, 10i, %10, 20i
     ret
   }
 }
@@ -2317,7 +2317,7 @@
   %W:ptr<workgroup, Outer, read_write> = var
 }
 
-%f0_W_mat_X = func(%p_indices:array<u32, 1>):f32 -> %b2 {
+%f0 = func(%p_indices:array<u32, 1>):f32 -> %b2 {
   %b2 = block {
     %4:u32 = access %p_indices, 0u
     %5:ptr<workgroup, vec4<f32>, read_write> = access %W, 1u, %4
@@ -2325,7 +2325,7 @@
     ret %6
   }
 }
-%f0_W_arr_X_mat_X = func(%p_indices_1:array<u32, 2>):f32 -> %b3 {  # %p_indices_1: 'p_indices'
+%f0_1 = func(%p_indices_1:array<u32, 2>):f32 -> %b3 {  # %f0_1: 'f0', %p_indices_1: 'p_indices'
   %b3 = block {
     %9:u32 = access %p_indices_1, 0u
     %10:u32 = access %p_indices_1, 1u
@@ -2334,32 +2334,32 @@
     ret %12
   }
 }
-%f1_W_mat = func():f32 -> %b4 {
+%f1 = func():f32 -> %b4 {
   %b4 = block {
     %res:ptr<function, f32, read_write> = var
     %15:u32 = convert 1i
     %16:array<u32, 1> = construct %15
-    %17:f32 = call %f0_W_mat_X, %16
+    %17:f32 = call %f0, %16
     %18:f32 = load %res
     %19:f32 = add %18, %17
     store %res, %19
     %20:u32 = convert 1i
     %21:array<u32, 1> = construct %20
-    %22:f32 = call %f0_W_mat_X, %21
+    %22:f32 = call %f0, %21
     %23:f32 = load %res
     %24:f32 = add %23, %22
     store %res, %24
     %25:u32 = convert 2i
     %26:u32 = convert 1i
     %27:array<u32, 2> = construct %25, %26
-    %28:f32 = call %f0_W_arr_X_mat_X, %27
+    %28:f32 = call %f0_1, %27
     %29:f32 = load %res
     %30:f32 = add %29, %28
     store %res, %30
     %31:u32 = convert 2i
     %32:u32 = convert 1i
     %33:array<u32, 2> = construct %31, %32
-    %34:f32 = call %f0_W_arr_X_mat_X, %33
+    %34:f32 = call %f0_1, %33
     %35:f32 = load %res
     %36:f32 = add %35, %34
     store %res, %36
@@ -2367,33 +2367,33 @@
     ret %37
   }
 }
-%f1_W_arr_X_mat = func(%p_indices_2:array<u32, 1>):f32 -> %b5 {  # %p_indices_2: 'p_indices'
+%f1_1 = func(%p_indices_2:array<u32, 1>):f32 -> %b5 {  # %f1_1: 'f1', %p_indices_2: 'p_indices'
   %b5 = block {
     %40:u32 = access %p_indices_2, 0u
     %res_1:ptr<function, f32, read_write> = var  # %res_1: 'res'
     %42:u32 = convert 1i
     %43:array<u32, 2> = construct %40, %42
-    %44:f32 = call %f0_W_arr_X_mat_X, %43
+    %44:f32 = call %f0_1, %43
     %45:f32 = load %res_1
     %46:f32 = add %45, %44
     store %res_1, %46
     %47:u32 = convert 1i
     %48:array<u32, 2> = construct %40, %47
-    %49:f32 = call %f0_W_arr_X_mat_X, %48
+    %49:f32 = call %f0_1, %48
     %50:f32 = load %res_1
     %51:f32 = add %50, %49
     store %res_1, %51
     %52:u32 = convert 2i
     %53:u32 = convert 1i
     %54:array<u32, 2> = construct %52, %53
-    %55:f32 = call %f0_W_arr_X_mat_X, %54
+    %55:f32 = call %f0_1, %54
     %56:f32 = load %res_1
     %57:f32 = add %56, %55
     store %res_1, %57
     %58:u32 = convert 2i
     %59:u32 = convert 1i
     %60:array<u32, 2> = construct %58, %59
-    %61:f32 = call %f0_W_arr_X_mat_X, %60
+    %61:f32 = call %f0_1, %60
     %62:f32 = load %res_1
     %63:f32 = add %62, %61
     store %res_1, %63
@@ -2401,33 +2401,33 @@
     ret %64
   }
 }
-%f2_W_arr_X = func(%p_indices_3:array<u32, 1>):f32 -> %b6 {  # %p_indices_3: 'p_indices'
+%f2 = func(%p_indices_3:array<u32, 1>):f32 -> %b6 {  # %p_indices_3: 'p_indices'
   %b6 = block {
     %67:u32 = access %p_indices_3, 0u
     %68:array<u32, 1> = construct %67
-    %69:f32 = call %f1_W_arr_X_mat, %68
+    %69:f32 = call %f1_1, %68
     ret %69
   }
 }
-%f3_W_arr_W_mat = func():f32 -> %b7 {
+%f3 = func():f32 -> %b7 {
   %b7 = block {
     %71:u32 = convert 3i
     %72:array<u32, 1> = construct %71
-    %73:f32 = call %f2_W_arr_X, %72
-    %74:f32 = call %f1_W_mat
+    %73:f32 = call %f2, %72
+    %74:f32 = call %f1
     %75:f32 = add %73, %74
     ret %75
   }
 }
-%f4_W = func():f32 -> %b8 {
+%f4 = func():f32 -> %b8 {
   %b8 = block {
-    %77:f32 = call %f3_W_arr_W_mat
+    %77:f32 = call %f3
     ret %77
   }
 }
 %b = func():void -> %b9 {
   %b9 = block {
-    %79:f32 = call %f4_W
+    %79:f32 = call %f4
     ret
   }
 }
@@ -2522,7 +2522,7 @@
   %U:ptr<workgroup, array<array<array<vec4<i32>, 5>, 5>, 5>, read_write> = var @binding_point(0, 0)
 }
 
-%f2_U_X_X = func(%p_indices:array<u32, 2>):vec4<i32> -> %b2 {
+%f2 = func(%p_indices:array<u32, 2>):vec4<i32> -> %b2 {
   %b2 = block {
     %4:u32 = access %p_indices, 0u
     %5:u32 = access %p_indices, 1u
@@ -2532,24 +2532,24 @@
     ret %8
   }
 }
-%f1_U_X = func(%p_indices_1:array<u32, 1>):vec4<i32> -> %b3 {  # %p_indices_1: 'p_indices'
+%f1 = func(%p_indices_1:array<u32, 1>):vec4<i32> -> %b3 {  # %p_indices_1: 'p_indices'
   %b3 = block {
     %11:u32 = access %p_indices_1, 0u
     %12:array<u32, 2> = construct %11, 2u
-    %13:vec4<i32> = call %f2_U_X_X, %12
+    %13:vec4<i32> = call %f2, %12
     ret %13
   }
 }
-%f0_U = func():vec4<i32> -> %b4 {
+%f0 = func():vec4<i32> -> %b4 {
   %b4 = block {
     %15:array<u32, 1> = construct 1u
-    %16:vec4<i32> = call %f1_U_X, %15
+    %16:vec4<i32> = call %f1, %15
     ret %16
   }
 }
 %main = func():void -> %b5 {
   %b5 = block {
-    %18:vec4<i32> = call %f0_U
+    %18:vec4<i32> = call %f0
     ret
   }
 }
@@ -2617,7 +2617,7 @@
   %P:ptr<private, i32, read_write> = var
 }
 
-%a_P = func(%pre:i32, %post:i32):i32 -> %b2 {
+%a = func(%pre:i32, %post:i32):i32 -> %b2 {
   %b2 = block {
     %5:ptr<private, i32, read_write> = access %P
     %6:i32 = load %5
@@ -2626,7 +2626,7 @@
 }
 %b = func():void -> %b3 {
   %b3 = block {
-    %8:i32 = call %a_P, 10i, 20i
+    %8:i32 = call %a, 10i, 20i
     ret
   }
 }
@@ -2688,7 +2688,7 @@
   %P:ptr<private, i32, read_write> = var
 }
 
-%a_P = func(%pre:i32, %post:i32):void -> %b2 {
+%a = func(%pre:i32, %post:i32):void -> %b2 {
   %b2 = block {
     %5:ptr<private, i32, read_write> = access %P
     store %5, 42i
@@ -2697,7 +2697,7 @@
 }
 %b = func():void -> %b3 {
   %b3 = block {
-    %7:void = call %a_P, 10i, 20i
+    %7:void = call %a, 10i, 20i
     ret
   }
 }
@@ -2770,7 +2770,7 @@
   %P:ptr<private, str, read_write> = var
 }
 
-%a_P_i = func(%pre:i32, %post:i32):i32 -> %b2 {
+%a = func(%pre:i32, %post:i32):i32 -> %b2 {
   %b2 = block {
     %5:ptr<private, i32, read_write> = access %P, 0u
     %6:i32 = load %5
@@ -2779,7 +2779,7 @@
 }
 %b = func():void -> %b3 {
   %b3 = block {
-    %8:i32 = call %a_P_i, 10i, 20i
+    %8:i32 = call %a, 10i, 20i
     ret
   }
 }
@@ -2916,7 +2916,7 @@
   %P:ptr<private, str, read_write> = var
 }
 
-%a_P_arr = func(%pre:i32, %post:i32):void -> %b2 {
+%a = func(%pre:i32, %post:i32):void -> %b2 {
   %b2 = block {
     %5:ptr<private, array<i32, 4>, read_write> = access %P, 0u
     store %5, array<i32, 4>(0i)
@@ -2925,7 +2925,7 @@
 }
 %b = func():void -> %b3 {
   %b3 = block {
-    %7:void = call %a_P_arr, 10i, 20i
+    %7:void = call %a, 10i, 20i
     ret
   }
 }
@@ -3082,21 +3082,21 @@
   %Pa:ptr<private, array<i32, 4>, read_write> = var
 }
 
-%a_Pi = func(%pre:i32, %post:i32):i32 -> %b2 {
+%a = func(%pre:i32, %post:i32):i32 -> %b2 {
   %b2 = block {
     %7:ptr<private, i32, read_write> = access %Pi
     %8:i32 = load %7
     ret %8
   }
 }
-%a_Ps_i = func(%pre_1:i32, %post_1:i32):i32 -> %b3 {  # %pre_1: 'pre', %post_1: 'post'
+%a_1 = func(%pre_1:i32, %post_1:i32):i32 -> %b3 {  # %a_1: 'a', %pre_1: 'pre', %post_1: 'post'
   %b3 = block {
     %12:ptr<private, i32, read_write> = access %Ps, 0u
     %13:i32 = load %12
     ret %13
   }
 }
-%a_Pa_X = func(%pre_2:i32, %p_indices:array<u32, 1>, %post_2:i32):i32 -> %b4 {  # %pre_2: 'pre', %post_2: 'post'
+%a_2 = func(%pre_2:i32, %p_indices:array<u32, 1>, %post_2:i32):i32 -> %b4 {  # %a_2: 'a', %pre_2: 'pre', %post_2: 'post'
   %b4 = block {
     %18:u32 = access %p_indices, 0u
     %19:ptr<private, i32, read_write> = access %Pa, %18
@@ -3106,11 +3106,11 @@
 }
 %b = func():void -> %b5 {
   %b5 = block {
-    %22:i32 = call %a_Pi, 10i, 20i
-    %23:i32 = call %a_Ps_i, 30i, 40i
+    %22:i32 = call %a, 10i, 20i
+    %23:i32 = call %a_1, 30i, 40i
     %24:u32 = convert 2i
     %25:array<u32, 1> = construct %24
-    %26:i32 = call %a_Pa_X, 50i, %25, 60i
+    %26:i32 = call %a_2, 50i, %25, 60i
     ret
   }
 }
@@ -3387,7 +3387,7 @@
   %P:ptr<private, Outer, read_write> = var
 }
 
-%f0_P_mat_X = func(%p_indices:array<u32, 1>):f32 -> %b2 {
+%f0 = func(%p_indices:array<u32, 1>):f32 -> %b2 {
   %b2 = block {
     %4:u32 = access %p_indices, 0u
     %5:ptr<private, vec4<f32>, read_write> = access %P, 1u, %4
@@ -3395,7 +3395,7 @@
     ret %6
   }
 }
-%f0_P_arr_X_mat_X = func(%p_indices_1:array<u32, 2>):f32 -> %b3 {  # %p_indices_1: 'p_indices'
+%f0_1 = func(%p_indices_1:array<u32, 2>):f32 -> %b3 {  # %f0_1: 'f0', %p_indices_1: 'p_indices'
   %b3 = block {
     %9:u32 = access %p_indices_1, 0u
     %10:u32 = access %p_indices_1, 1u
@@ -3404,32 +3404,32 @@
     ret %12
   }
 }
-%f1_P_mat = func():f32 -> %b4 {
+%f1 = func():f32 -> %b4 {
   %b4 = block {
     %res:ptr<function, f32, read_write> = var
     %15:u32 = convert 1i
     %16:array<u32, 1> = construct %15
-    %17:f32 = call %f0_P_mat_X, %16
+    %17:f32 = call %f0, %16
     %18:f32 = load %res
     %19:f32 = add %18, %17
     store %res, %19
     %20:u32 = convert 1i
     %21:array<u32, 1> = construct %20
-    %22:f32 = call %f0_P_mat_X, %21
+    %22:f32 = call %f0, %21
     %23:f32 = load %res
     %24:f32 = add %23, %22
     store %res, %24
     %25:u32 = convert 2i
     %26:u32 = convert 1i
     %27:array<u32, 2> = construct %25, %26
-    %28:f32 = call %f0_P_arr_X_mat_X, %27
+    %28:f32 = call %f0_1, %27
     %29:f32 = load %res
     %30:f32 = add %29, %28
     store %res, %30
     %31:u32 = convert 2i
     %32:u32 = convert 1i
     %33:array<u32, 2> = construct %31, %32
-    %34:f32 = call %f0_P_arr_X_mat_X, %33
+    %34:f32 = call %f0_1, %33
     %35:f32 = load %res
     %36:f32 = add %35, %34
     store %res, %36
@@ -3437,33 +3437,33 @@
     ret %37
   }
 }
-%f1_P_arr_X_mat = func(%p_indices_2:array<u32, 1>):f32 -> %b5 {  # %p_indices_2: 'p_indices'
+%f1_1 = func(%p_indices_2:array<u32, 1>):f32 -> %b5 {  # %f1_1: 'f1', %p_indices_2: 'p_indices'
   %b5 = block {
     %40:u32 = access %p_indices_2, 0u
     %res_1:ptr<function, f32, read_write> = var  # %res_1: 'res'
     %42:u32 = convert 1i
     %43:array<u32, 2> = construct %40, %42
-    %44:f32 = call %f0_P_arr_X_mat_X, %43
+    %44:f32 = call %f0_1, %43
     %45:f32 = load %res_1
     %46:f32 = add %45, %44
     store %res_1, %46
     %47:u32 = convert 1i
     %48:array<u32, 2> = construct %40, %47
-    %49:f32 = call %f0_P_arr_X_mat_X, %48
+    %49:f32 = call %f0_1, %48
     %50:f32 = load %res_1
     %51:f32 = add %50, %49
     store %res_1, %51
     %52:u32 = convert 2i
     %53:u32 = convert 1i
     %54:array<u32, 2> = construct %52, %53
-    %55:f32 = call %f0_P_arr_X_mat_X, %54
+    %55:f32 = call %f0_1, %54
     %56:f32 = load %res_1
     %57:f32 = add %56, %55
     store %res_1, %57
     %58:u32 = convert 2i
     %59:u32 = convert 1i
     %60:array<u32, 2> = construct %58, %59
-    %61:f32 = call %f0_P_arr_X_mat_X, %60
+    %61:f32 = call %f0_1, %60
     %62:f32 = load %res_1
     %63:f32 = add %62, %61
     store %res_1, %63
@@ -3471,33 +3471,33 @@
     ret %64
   }
 }
-%f2_P_arr_X = func(%p_indices_3:array<u32, 1>):f32 -> %b6 {  # %p_indices_3: 'p_indices'
+%f2 = func(%p_indices_3:array<u32, 1>):f32 -> %b6 {  # %p_indices_3: 'p_indices'
   %b6 = block {
     %67:u32 = access %p_indices_3, 0u
     %68:array<u32, 1> = construct %67
-    %69:f32 = call %f1_P_arr_X_mat, %68
+    %69:f32 = call %f1_1, %68
     ret %69
   }
 }
-%f3_P_arr_P_mat = func():f32 -> %b7 {
+%f3 = func():f32 -> %b7 {
   %b7 = block {
     %71:u32 = convert 3i
     %72:array<u32, 1> = construct %71
-    %73:f32 = call %f2_P_arr_X, %72
-    %74:f32 = call %f1_P_mat
+    %73:f32 = call %f2, %72
+    %74:f32 = call %f1
     %75:f32 = add %73, %74
     ret %75
   }
 }
-%f4_P = func():f32 -> %b8 {
+%f4 = func():f32 -> %b8 {
   %b8 = block {
-    %77:f32 = call %f3_P_arr_P_mat
+    %77:f32 = call %f3
     ret %77
   }
 }
 %b = func():void -> %b9 {
   %b9 = block {
-    %79:f32 = call %f4_P
+    %79:f32 = call %f4
     ret
   }
 }
@@ -3773,7 +3773,7 @@
   %P:ptr<private, array<array<array<vec4<i32>, 5>, 5>, 5>, read_write> = var
 }
 
-%f2_P_X_X = func(%p_indices:array<u32, 2>):vec4<i32> -> %b2 {
+%f2 = func(%p_indices:array<u32, 2>):vec4<i32> -> %b2 {
   %b2 = block {
     %4:u32 = access %p_indices, 0u
     %5:u32 = access %p_indices, 1u
@@ -3783,24 +3783,24 @@
     ret %8
   }
 }
-%f1_P_X = func(%p_indices_1:array<u32, 1>):vec4<i32> -> %b3 {  # %p_indices_1: 'p_indices'
+%f1 = func(%p_indices_1:array<u32, 1>):vec4<i32> -> %b3 {  # %p_indices_1: 'p_indices'
   %b3 = block {
     %11:u32 = access %p_indices_1, 0u
     %12:array<u32, 2> = construct %11, 2u
-    %13:vec4<i32> = call %f2_P_X_X, %12
+    %13:vec4<i32> = call %f2, %12
     ret %13
   }
 }
-%f0_P = func():vec4<i32> -> %b4 {
+%f0 = func():vec4<i32> -> %b4 {
   %b4 = block {
     %15:array<u32, 1> = construct 1u
-    %16:vec4<i32> = call %f1_P_X, %15
+    %16:vec4<i32> = call %f1, %15
     ret %16
   }
 }
 %main = func():void -> %b5 {
   %b5 = block {
-    %18:vec4<i32> = call %f0_P
+    %18:vec4<i32> = call %f0
     ret
   }
 }
@@ -3972,7 +3972,7 @@
     EXPECT_EQ(src, str());
 
     auto* expect = R"(
-%a_P = func(%pre:i32, %p_root:ptr<function, i32, read_write>, %post:i32):i32 -> %b1 {
+%a = func(%pre:i32, %p_root:ptr<function, i32, read_write>, %post:i32):i32 -> %b1 {
   %b1 = block {
     %5:ptr<function, i32, read_write> = access %p_root
     %6:i32 = load %5
@@ -3982,7 +3982,7 @@
 %b = func():void -> %b2 {
   %b2 = block {
     %F:ptr<function, i32, read_write> = var
-    %9:i32 = call %a_P, 10i, %F, 20i
+    %9:i32 = call %a, 10i, %F, 20i
     ret
   }
 }
@@ -4032,7 +4032,7 @@
     EXPECT_EQ(src, str());
 
     auto* expect = R"(
-%a_P = func(%pre:i32, %p_root:ptr<function, i32, read_write>, %post:i32):void -> %b1 {
+%a = func(%pre:i32, %p_root:ptr<function, i32, read_write>, %post:i32):void -> %b1 {
   %b1 = block {
     %5:ptr<function, i32, read_write> = access %p_root
     store %5, 42i
@@ -4042,7 +4042,7 @@
 %b = func():void -> %b2 {
   %b2 = block {
     %F:ptr<function, i32, read_write> = var
-    %8:void = call %a_P, 10i, %F, 20i
+    %8:void = call %a, 10i, %F, 20i
     ret
   }
 }
@@ -4103,7 +4103,7 @@
   i:i32 @offset(0)
 }
 
-%a_P_i = func(%pre:i32, %p_root:ptr<function, str, read_write>, %post:i32):i32 -> %b1 {
+%a = func(%pre:i32, %p_root:ptr<function, str, read_write>, %post:i32):i32 -> %b1 {
   %b1 = block {
     %5:ptr<function, i32, read_write> = access %p_root, 0u
     %6:i32 = load %5
@@ -4113,7 +4113,7 @@
 %b = func():void -> %b2 {
   %b2 = block {
     %F:ptr<function, str, read_write> = var
-    %9:i32 = call %a_P_i, 10i, %F, 20i
+    %9:i32 = call %a, 10i, %F, 20i
     ret
   }
 }
@@ -4178,7 +4178,7 @@
   arr:array<i32, 4> @offset(0)
 }
 
-%a_P_arr = func(%pre:i32, %p_root:ptr<function, str, read_write>, %post:i32):void -> %b1 {
+%a = func(%pre:i32, %p_root:ptr<function, str, read_write>, %post:i32):void -> %b1 {
   %b1 = block {
     %5:ptr<function, array<i32, 4>, read_write> = access %p_root, 0u
     store %5, array<i32, 4>(0i)
@@ -4188,7 +4188,7 @@
 %b = func():void -> %b2 {
   %b2 = block {
     %F:ptr<function, str, read_write> = var
-    %8:void = call %a_P_arr, 10i, %F, 20i
+    %8:void = call %a, 10i, %F, 20i
     ret
   }
 }
@@ -4265,21 +4265,21 @@
   i:i32 @offset(0)
 }
 
-%a_P = func(%pre:i32, %p_root:ptr<function, i32, read_write>, %post:i32):i32 -> %b1 {
+%a = func(%pre:i32, %p_root:ptr<function, i32, read_write>, %post:i32):i32 -> %b1 {
   %b1 = block {
     %5:ptr<function, i32, read_write> = access %p_root
     %6:i32 = load %5
     ret %6
   }
 }
-%a_P_i = func(%pre_1:i32, %p_root_1:ptr<function, str, read_write>, %post_1:i32):i32 -> %b2 {  # %pre_1: 'pre', %p_root_1: 'p_root', %post_1: 'post'
+%a_1 = func(%pre_1:i32, %p_root_1:ptr<function, str, read_write>, %post_1:i32):i32 -> %b2 {  # %a_1: 'a', %pre_1: 'pre', %p_root_1: 'p_root', %post_1: 'post'
   %b2 = block {
     %11:ptr<function, i32, read_write> = access %p_root_1, 0u
     %12:i32 = load %11
     ret %12
   }
 }
-%a_P_X = func(%pre_2:i32, %p_root_2:ptr<function, array<i32, 4>, read_write>, %p_indices:array<u32, 1>, %post_2:i32):i32 -> %b3 {  # %pre_2: 'pre', %p_root_2: 'p_root', %post_2: 'post'
+%a_2 = func(%pre_2:i32, %p_root_2:ptr<function, array<i32, 4>, read_write>, %p_indices:array<u32, 1>, %post_2:i32):i32 -> %b3 {  # %a_2: 'a', %pre_2: 'pre', %p_root_2: 'p_root', %post_2: 'post'
   %b3 = block {
     %18:u32 = access %p_indices, 0u
     %19:ptr<function, i32, read_write> = access %p_root_2, %18
@@ -4292,11 +4292,11 @@
     %Fi:ptr<function, i32, read_write> = var
     %Fs:ptr<function, str, read_write> = var
     %Fa:ptr<function, array<i32, 4>, read_write> = var
-    %25:i32 = call %a_P, 10i, %Fi, 20i
-    %26:i32 = call %a_P_i, 30i, %Fs, 40i
+    %25:i32 = call %a, 10i, %Fi, 20i
+    %26:i32 = call %a_1, 30i, %Fs, 40i
     %27:u32 = convert 2i
     %28:array<u32, 1> = construct %27
-    %29:i32 = call %a_P_X, 50i, %Fa, %28, 60i
+    %29:i32 = call %a_2, 50i, %Fa, %28, 60i
     ret
   }
 }
@@ -4570,7 +4570,7 @@
   mat:mat3x4<f32> @offset(192)
 }
 
-%f0_P_arr_X_mat_X = func(%p_root:ptr<function, Outer, read_write>, %p_indices:array<u32, 2>):f32 -> %b1 {
+%f0 = func(%p_root:ptr<function, Outer, read_write>, %p_indices:array<u32, 2>):f32 -> %b1 {
   %b1 = block {
     %4:u32 = access %p_indices, 0u
     %5:u32 = access %p_indices, 1u
@@ -4579,19 +4579,19 @@
     ret %7
   }
 }
-%f1_P_arr_X_mat = func(%p_root_1:ptr<function, Outer, read_write>, %p_indices_1:array<u32, 1>):f32 -> %b2 {  # %p_root_1: 'p_root', %p_indices_1: 'p_indices'
+%f1 = func(%p_root_1:ptr<function, Outer, read_write>, %p_indices_1:array<u32, 1>):f32 -> %b2 {  # %p_root_1: 'p_root', %p_indices_1: 'p_indices'
   %b2 = block {
     %11:u32 = access %p_indices_1, 0u
     %res:ptr<function, f32, read_write> = var
     %13:u32 = convert 1i
     %14:array<u32, 2> = construct %11, %13
-    %15:f32 = call %f0_P_arr_X_mat_X, %p_root_1, %14
+    %15:f32 = call %f0, %p_root_1, %14
     %16:f32 = load %res
     %17:f32 = add %16, %15
     store %res, %17
     %18:u32 = convert 1i
     %19:array<u32, 2> = construct %11, %18
-    %20:f32 = call %f0_P_arr_X_mat_X, %p_root_1, %19
+    %20:f32 = call %f0, %p_root_1, %19
     %21:f32 = load %res
     %22:f32 = add %21, %20
     store %res, %22
@@ -4599,32 +4599,32 @@
     ret %23
   }
 }
-%f2_P_arr_X = func(%p_root_2:ptr<function, Outer, read_write>, %p_indices_2:array<u32, 1>):f32 -> %b3 {  # %p_root_2: 'p_root', %p_indices_2: 'p_indices'
+%f2 = func(%p_root_2:ptr<function, Outer, read_write>, %p_indices_2:array<u32, 1>):f32 -> %b3 {  # %p_root_2: 'p_root', %p_indices_2: 'p_indices'
   %b3 = block {
     %27:u32 = access %p_indices_2, 0u
     %28:array<u32, 1> = construct %27
-    %29:f32 = call %f1_P_arr_X_mat, %p_root_2, %28
+    %29:f32 = call %f1, %p_root_2, %28
     ret %29
   }
 }
-%f3_P_arr = func(%p_root_3:ptr<function, Outer, read_write>):f32 -> %b4 {  # %p_root_3: 'p_root'
+%f3 = func(%p_root_3:ptr<function, Outer, read_write>):f32 -> %b4 {  # %p_root_3: 'p_root'
   %b4 = block {
     %32:u32 = convert 3i
     %33:array<u32, 1> = construct %32
-    %34:f32 = call %f2_P_arr_X, %p_root_3, %33
+    %34:f32 = call %f2, %p_root_3, %33
     ret %34
   }
 }
-%f4_P = func(%p_root_4:ptr<function, Outer, read_write>):f32 -> %b5 {  # %p_root_4: 'p_root'
+%f4 = func(%p_root_4:ptr<function, Outer, read_write>):f32 -> %b5 {  # %p_root_4: 'p_root'
   %b5 = block {
-    %37:f32 = call %f3_P_arr, %p_root_4
+    %37:f32 = call %f3, %p_root_4
     ret %37
   }
 }
 %b = func():void -> %b6 {
   %b6 = block {
     %F:ptr<function, Outer, read_write> = var
-    %40:f32 = call %f4_P, %F
+    %40:f32 = call %f4, %F
     ret
   }
 }
@@ -4859,7 +4859,7 @@
     EXPECT_EQ(src, str());
 
     auto* expect = R"(
-%f2_P_X_X = func(%p_root:ptr<function, array<array<array<vec4<i32>, 5>, 5>, 5>, read_write>, %p_indices:array<u32, 2>):vec4<i32> -> %b1 {
+%f2 = func(%p_root:ptr<function, array<array<array<vec4<i32>, 5>, 5>, 5>, read_write>, %p_indices:array<u32, 2>):vec4<i32> -> %b1 {
   %b1 = block {
     %4:u32 = access %p_indices, 0u
     %5:u32 = access %p_indices, 1u
@@ -4869,25 +4869,25 @@
     ret %8
   }
 }
-%f1_P_X = func(%p_root_1:ptr<function, array<array<array<vec4<i32>, 5>, 5>, 5>, read_write>, %p_indices_1:array<u32, 1>):vec4<i32> -> %b2 {  # %p_root_1: 'p_root', %p_indices_1: 'p_indices'
+%f1 = func(%p_root_1:ptr<function, array<array<array<vec4<i32>, 5>, 5>, 5>, read_write>, %p_indices_1:array<u32, 1>):vec4<i32> -> %b2 {  # %p_root_1: 'p_root', %p_indices_1: 'p_indices'
   %b2 = block {
     %12:u32 = access %p_indices_1, 0u
     %13:array<u32, 2> = construct %12, 2u
-    %14:vec4<i32> = call %f2_P_X_X, %p_root_1, %13
+    %14:vec4<i32> = call %f2, %p_root_1, %13
     ret %14
   }
 }
-%f0_P = func(%p_root_2:ptr<function, array<array<array<vec4<i32>, 5>, 5>, 5>, read_write>):vec4<i32> -> %b3 {  # %p_root_2: 'p_root'
+%f0 = func(%p_root_2:ptr<function, array<array<array<vec4<i32>, 5>, 5>, 5>, read_write>):vec4<i32> -> %b3 {  # %p_root_2: 'p_root'
   %b3 = block {
     %17:array<u32, 1> = construct 1u
-    %18:vec4<i32> = call %f1_P_X, %p_root_2, %17
+    %18:vec4<i32> = call %f1, %p_root_2, %17
     ret %18
   }
 }
 %main = func():void -> %b4 {
   %b4 = block {
     %F:ptr<function, array<array<array<vec4<i32>, 5>, 5>, 5>, read_write> = var
-    %21:vec4<i32> = call %f0_P, %F
+    %21:vec4<i32> = call %f0, %F
     ret
   }
 }
@@ -5033,7 +5033,7 @@
   %S:ptr<storage, array<f32>, read_write> = var @binding_point(0, 0)
 }
 
-%len_S = func():u32 -> %b2 {
+%len = func():u32 -> %b2 {
   %b2 = block {
     %3:ptr<storage, array<f32>, read_write> = access %S
     %4:u32 = arrayLength %3
@@ -5042,7 +5042,7 @@
 }
 %b = func():void -> %b3 {
   %b3 = block {
-    %6:u32 = call %len_S
+    %6:u32 = call %len
     ret
   }
 }
@@ -5100,7 +5100,7 @@
   %W:ptr<workgroup, atomic<i32>, read_write> = var
 }
 
-%load_W = func():i32 -> %b2 {
+%load = func():i32 -> %b2 {
   %b2 = block {
     %3:ptr<workgroup, atomic<i32>, read_write> = access %W
     %4:i32 = atomicLoad %3
@@ -5109,7 +5109,7 @@
 }
 %b = func():void -> %b3 {
   %b3 = block {
-    %6:i32 = call %load_W
+    %6:i32 = call %load
     ret
   }
 }
@@ -5380,21 +5380,21 @@
   %W_arr_arr:ptr<workgroup, array<array<vec4<i32>, 8>, 4>, read_write> = var
 }
 
-%fn_u_U = func():vec4<i32> -> %b2 {
+%fn_u = func():vec4<i32> -> %b2 {
   %b2 = block {
     %14:ptr<uniform, vec4<i32>, read> = access %U
     %15:vec4<i32> = load %14
     ret %15
   }
 }
-%fn_u_U_str_i = func():vec4<i32> -> %b3 {
+%fn_u_1 = func():vec4<i32> -> %b3 {  # %fn_u_1: 'fn_u'
   %b3 = block {
     %17:ptr<uniform, vec4<i32>, read> = access %U_str, 0u
     %18:vec4<i32> = load %17
     ret %18
   }
 }
-%fn_u_U_arr_X = func(%p_indices:array<u32, 1>):vec4<i32> -> %b4 {
+%fn_u_2 = func(%p_indices:array<u32, 1>):vec4<i32> -> %b4 {  # %fn_u_2: 'fn_u'
   %b4 = block {
     %21:u32 = access %p_indices, 0u
     %22:ptr<uniform, vec4<i32>, read> = access %U_arr, %21
@@ -5402,7 +5402,7 @@
     ret %23
   }
 }
-%fn_u_U_arr_arr_X_X = func(%p_indices_1:array<u32, 2>):vec4<i32> -> %b5 {  # %p_indices_1: 'p_indices'
+%fn_u_3 = func(%p_indices_1:array<u32, 2>):vec4<i32> -> %b5 {  # %fn_u_3: 'fn_u', %p_indices_1: 'p_indices'
   %b5 = block {
     %26:u32 = access %p_indices_1, 0u
     %27:u32 = access %p_indices_1, 1u
@@ -5411,21 +5411,21 @@
     ret %29
   }
 }
-%fn_s_S = func():vec4<i32> -> %b6 {
+%fn_s = func():vec4<i32> -> %b6 {
   %b6 = block {
     %31:ptr<storage, vec4<i32>, read> = access %S
     %32:vec4<i32> = load %31
     ret %32
   }
 }
-%fn_s_S_str_i = func():vec4<i32> -> %b7 {
+%fn_s_1 = func():vec4<i32> -> %b7 {  # %fn_s_1: 'fn_s'
   %b7 = block {
     %34:ptr<storage, vec4<i32>, read> = access %S_str, 0u
     %35:vec4<i32> = load %34
     ret %35
   }
 }
-%fn_s_S_arr_X = func(%p_indices_2:array<u32, 1>):vec4<i32> -> %b8 {  # %p_indices_2: 'p_indices'
+%fn_s_2 = func(%p_indices_2:array<u32, 1>):vec4<i32> -> %b8 {  # %fn_s_2: 'fn_s', %p_indices_2: 'p_indices'
   %b8 = block {
     %38:u32 = access %p_indices_2, 0u
     %39:ptr<storage, vec4<i32>, read> = access %S_arr, %38
@@ -5433,7 +5433,7 @@
     ret %40
   }
 }
-%fn_s_S_arr_arr_X_X = func(%p_indices_3:array<u32, 2>):vec4<i32> -> %b9 {  # %p_indices_3: 'p_indices'
+%fn_s_3 = func(%p_indices_3:array<u32, 2>):vec4<i32> -> %b9 {  # %fn_s_3: 'fn_s', %p_indices_3: 'p_indices'
   %b9 = block {
     %43:u32 = access %p_indices_3, 0u
     %44:u32 = access %p_indices_3, 1u
@@ -5442,21 +5442,21 @@
     ret %46
   }
 }
-%fn_w_W = func():vec4<i32> -> %b10 {
+%fn_w = func():vec4<i32> -> %b10 {
   %b10 = block {
     %48:ptr<workgroup, vec4<i32>, read_write> = access %W
     %49:vec4<i32> = load %48
     ret %49
   }
 }
-%fn_w_W_str_i = func():vec4<i32> -> %b11 {
+%fn_w_1 = func():vec4<i32> -> %b11 {  # %fn_w_1: 'fn_w'
   %b11 = block {
     %51:ptr<workgroup, vec4<i32>, read_write> = access %W_str, 0u
     %52:vec4<i32> = load %51
     ret %52
   }
 }
-%fn_w_W_arr_X = func(%p_indices_4:array<u32, 1>):vec4<i32> -> %b12 {  # %p_indices_4: 'p_indices'
+%fn_w_2 = func(%p_indices_4:array<u32, 1>):vec4<i32> -> %b12 {  # %fn_w_2: 'fn_w', %p_indices_4: 'p_indices'
   %b12 = block {
     %55:u32 = access %p_indices_4, 0u
     %56:ptr<workgroup, vec4<i32>, read_write> = access %W_arr, %55
@@ -5464,7 +5464,7 @@
     ret %57
   }
 }
-%fn_w_W_arr_arr_X_X = func(%p_indices_5:array<u32, 2>):vec4<i32> -> %b13 {  # %p_indices_5: 'p_indices'
+%fn_w_3 = func(%p_indices_5:array<u32, 2>):vec4<i32> -> %b13 {  # %fn_w_3: 'fn_w', %p_indices_5: 'p_indices'
   %b13 = block {
     %60:u32 = access %p_indices_5, 0u
     %61:u32 = access %p_indices_5, 1u
@@ -5477,87 +5477,87 @@
   %b14 = block {
     %I:i32 = let 3i
     %J:i32 = let 4i
-    %u:vec4<i32> = call %fn_u_U
-    %u_str:vec4<i32> = call %fn_u_U_str_i
+    %u:vec4<i32> = call %fn_u
+    %u_str:vec4<i32> = call %fn_u_1
     %69:u32 = convert 0i
     %70:array<u32, 1> = construct %69
-    %u_arr0:vec4<i32> = call %fn_u_U_arr_X, %70
+    %u_arr0:vec4<i32> = call %fn_u_2, %70
     %72:u32 = convert 1i
     %73:array<u32, 1> = construct %72
-    %u_arr1:vec4<i32> = call %fn_u_U_arr_X, %73
+    %u_arr1:vec4<i32> = call %fn_u_2, %73
     %75:u32 = convert %I
     %76:array<u32, 1> = construct %75
-    %u_arrI:vec4<i32> = call %fn_u_U_arr_X, %76
+    %u_arrI:vec4<i32> = call %fn_u_2, %76
     %78:u32 = convert 1i
     %79:u32 = convert 0i
     %80:array<u32, 2> = construct %78, %79
-    %u_arr1_arr0:vec4<i32> = call %fn_u_U_arr_arr_X_X, %80
+    %u_arr1_arr0:vec4<i32> = call %fn_u_3, %80
     %82:u32 = convert 2i
     %83:u32 = convert %I
     %84:array<u32, 2> = construct %82, %83
-    %u_arr2_arrI:vec4<i32> = call %fn_u_U_arr_arr_X_X, %84
+    %u_arr2_arrI:vec4<i32> = call %fn_u_3, %84
     %86:u32 = convert %I
     %87:u32 = convert 2i
     %88:array<u32, 2> = construct %86, %87
-    %u_arrI_arr2:vec4<i32> = call %fn_u_U_arr_arr_X_X, %88
+    %u_arrI_arr2:vec4<i32> = call %fn_u_3, %88
     %90:u32 = convert %I
     %91:u32 = convert %J
     %92:array<u32, 2> = construct %90, %91
-    %u_arrI_arrJ:vec4<i32> = call %fn_u_U_arr_arr_X_X, %92
-    %s:vec4<i32> = call %fn_s_S
-    %s_str:vec4<i32> = call %fn_s_S_str_i
+    %u_arrI_arrJ:vec4<i32> = call %fn_u_3, %92
+    %s:vec4<i32> = call %fn_s
+    %s_str:vec4<i32> = call %fn_s_1
     %96:u32 = convert 0i
     %97:array<u32, 1> = construct %96
-    %s_arr0:vec4<i32> = call %fn_s_S_arr_X, %97
+    %s_arr0:vec4<i32> = call %fn_s_2, %97
     %99:u32 = convert 1i
     %100:array<u32, 1> = construct %99
-    %s_arr1:vec4<i32> = call %fn_s_S_arr_X, %100
+    %s_arr1:vec4<i32> = call %fn_s_2, %100
     %102:u32 = convert %I
     %103:array<u32, 1> = construct %102
-    %s_arrI:vec4<i32> = call %fn_s_S_arr_X, %103
+    %s_arrI:vec4<i32> = call %fn_s_2, %103
     %105:u32 = convert 1i
     %106:u32 = convert 0i
     %107:array<u32, 2> = construct %105, %106
-    %s_arr1_arr0:vec4<i32> = call %fn_s_S_arr_arr_X_X, %107
+    %s_arr1_arr0:vec4<i32> = call %fn_s_3, %107
     %109:u32 = convert 2i
     %110:u32 = convert %I
     %111:array<u32, 2> = construct %109, %110
-    %s_arr2_arrI:vec4<i32> = call %fn_s_S_arr_arr_X_X, %111
+    %s_arr2_arrI:vec4<i32> = call %fn_s_3, %111
     %113:u32 = convert %I
     %114:u32 = convert 2i
     %115:array<u32, 2> = construct %113, %114
-    %s_arrI_arr2:vec4<i32> = call %fn_s_S_arr_arr_X_X, %115
+    %s_arrI_arr2:vec4<i32> = call %fn_s_3, %115
     %117:u32 = convert %I
     %118:u32 = convert %J
     %119:array<u32, 2> = construct %117, %118
-    %s_arrI_arrJ:vec4<i32> = call %fn_s_S_arr_arr_X_X, %119
-    %w:vec4<i32> = call %fn_w_W
-    %w_str:vec4<i32> = call %fn_w_W_str_i
+    %s_arrI_arrJ:vec4<i32> = call %fn_s_3, %119
+    %w:vec4<i32> = call %fn_w
+    %w_str:vec4<i32> = call %fn_w_1
     %123:u32 = convert 0i
     %124:array<u32, 1> = construct %123
-    %w_arr0:vec4<i32> = call %fn_w_W_arr_X, %124
+    %w_arr0:vec4<i32> = call %fn_w_2, %124
     %126:u32 = convert 1i
     %127:array<u32, 1> = construct %126
-    %w_arr1:vec4<i32> = call %fn_w_W_arr_X, %127
+    %w_arr1:vec4<i32> = call %fn_w_2, %127
     %129:u32 = convert %I
     %130:array<u32, 1> = construct %129
-    %w_arrI:vec4<i32> = call %fn_w_W_arr_X, %130
+    %w_arrI:vec4<i32> = call %fn_w_2, %130
     %132:u32 = convert 1i
     %133:u32 = convert 0i
     %134:array<u32, 2> = construct %132, %133
-    %w_arr1_arr0:vec4<i32> = call %fn_w_W_arr_arr_X_X, %134
+    %w_arr1_arr0:vec4<i32> = call %fn_w_3, %134
     %136:u32 = convert 2i
     %137:u32 = convert %I
     %138:array<u32, 2> = construct %136, %137
-    %w_arr2_arrI:vec4<i32> = call %fn_w_W_arr_arr_X_X, %138
+    %w_arr2_arrI:vec4<i32> = call %fn_w_3, %138
     %140:u32 = convert %I
     %141:u32 = convert 2i
     %142:array<u32, 2> = construct %140, %141
-    %w_arrI_arr2:vec4<i32> = call %fn_w_W_arr_arr_X_X, %142
+    %w_arrI_arr2:vec4<i32> = call %fn_w_3, %142
     %144:u32 = convert %I
     %145:u32 = convert %J
     %146:array<u32, 2> = construct %144, %145
-    %w_arrI_arrJ:vec4<i32> = call %fn_w_W_arr_arr_X_X, %146
+    %w_arrI_arrJ:vec4<i32> = call %fn_w_3, %146
     ret
   }
 }
@@ -5657,7 +5657,7 @@
     ret %i
   }
 }
-%b_S_X = func(%p_indices:array<u32, 1>):i32 -> %b3 {
+%b = func(%p_indices:array<u32, 1>):i32 -> %b3 {
   %b3 = block {
     %6:u32 = access %p_indices, 0u
     %7:ptr<storage, array<array<array<i32, 9>, 9>, 9>, read> = access %S, %6
@@ -5681,7 +5681,7 @@
   %b4 = block {
     %22:u32 = convert 42i
     %23:array<u32, 1> = construct %22
-    %v:i32 = call %b_S_X, %23
+    %v:i32 = call %b, %23
     ret
   }
 }
@@ -5777,7 +5777,7 @@
   %S:ptr<storage, array<array<array<array<i32, 9>, 9>, 9>, 50>, read> = var @binding_point(0, 0)
 }
 
-%a_S_X_X_X_X = func(%pre:i32, %i_indices:array<u32, 4>, %post:i32):i32 -> %b2 {
+%a = func(%pre:i32, %i_indices:array<u32, 4>, %post:i32):i32 -> %b2 {
   %b2 = block {
     %6:u32 = access %i_indices, 0u
     %7:u32 = access %i_indices, 1u
@@ -5788,29 +5788,29 @@
     ret %11
   }
 }
-%b_S_X = func(%p_indices:array<u32, 1>):i32 -> %b3 {
+%b = func(%p_indices:array<u32, 1>):i32 -> %b3 {
   %b3 = block {
     %14:u32 = access %p_indices, 0u
     %15:u32 = convert 0i
     %16:u32 = convert 1i
     %17:u32 = convert 2i
     %18:array<u32, 4> = construct %14, %15, %16, %17
-    %19:i32 = call %a_S_X_X_X_X, 20i, %18, 30i
+    %19:i32 = call %a, 20i, %18, 30i
     %20:u32 = convert 3i
     %21:u32 = convert 4i
     %22:u32 = convert 5i
     %23:array<u32, 4> = construct %14, %20, %21, %22
-    %24:i32 = call %a_S_X_X_X_X, 40i, %23, 50i
+    %24:i32 = call %a, 40i, %23, 50i
     %25:u32 = convert 6i
     %26:u32 = convert 7i
     %27:u32 = convert 8i
     %28:array<u32, 4> = construct %14, %25, %26, %27
-    %29:i32 = call %a_S_X_X_X_X, 60i, %28, 70i
+    %29:i32 = call %a, 60i, %28, 70i
     %30:u32 = convert %19
     %31:u32 = convert %24
     %32:u32 = convert %29
     %33:array<u32, 4> = construct %14, %30, %31, %32
-    %34:i32 = call %a_S_X_X_X_X, 10i, %33, 80i
+    %34:i32 = call %a, 10i, %33, 80i
     ret %34
   }
 }
@@ -5818,7 +5818,7 @@
   %b4 = block {
     %36:u32 = convert 42i
     %37:array<u32, 1> = construct %36
-    %v:i32 = call %b_S_X, %37
+    %v:i32 = call %b, %37
     ret
   }
 }
@@ -5919,7 +5919,7 @@
     ret %i
   }
 }
-%b_S_X_U_X = func(%s_indices:array<u32, 1>, %u_indices:array<u32, 1>):i32 -> %b3 {
+%b = func(%s_indices:array<u32, 1>, %u_indices:array<u32, 1>):i32 -> %b3 {
   %b3 = block {
     %8:u32 = access %s_indices, 0u
     %9:ptr<storage, array<array<i32, 9>, 9>, read> = access %S, %8
@@ -5943,7 +5943,7 @@
     %23:array<u32, 1> = construct %22
     %24:u32 = convert 24i
     %25:array<u32, 1> = construct %24
-    %v:i32 = call %b_S_X_U_X, %23, %25
+    %v:i32 = call %b, %23, %25
     ret
   }
 }
diff --git a/src/tint/lang/core/ir/transform/direct_variable_access_wgsl_test.cc b/src/tint/lang/core/ir/transform/direct_variable_access_wgsl_test.cc
index 49780b7..77b799c 100644
--- a/src/tint/lang/core/ir/transform/direct_variable_access_wgsl_test.cc
+++ b/src/tint/lang/core/ir/transform/direct_variable_access_wgsl_test.cc
@@ -59,7 +59,9 @@
 
 class DirectVariableAccessTest : public TransformTestBase<testing::Test> {
   public:
-    std::string Run(std::string in, const DirectVariableAccessOptions& options = {}) {
+    std::string Run(std::string in,
+                    const DirectVariableAccessOptions& transform_options = {},
+                    const wgsl::writer::ProgramOptions program_options = {}) {
         wgsl::reader::Options parser_options;
         parser_options.allowed_features = wgsl::AllowedFeatures::Everything();
         Source::File file{"test", in};
@@ -73,7 +75,7 @@
             return "ProgramToIR() failed:\n" + module.Failure().reason.str();
         }
 
-        auto res = DirectVariableAccess(module.Get(), options);
+        auto res = DirectVariableAccess(module.Get(), transform_options);
         if (res != Success) {
             return "DirectVariableAccess failed:\n" + res.Failure().reason.str();
         }
@@ -84,9 +86,6 @@
             return "wgsl::writer::Raise failed:\n" + res.Failure().reason.str();
         }
 
-        wgsl::writer::ProgramOptions program_options;
-        program_options.allowed_features.extensions.insert(
-            wgsl::Extension::kChromiumExperimentalFullPtrParameters);
         auto program_out = wgsl::writer::IRToProgram(module.Get(), program_options);
         if (!program_out.IsValid()) {
             return "wgsl::writer::IRToProgram() failed: \n" + program_out.Diagnostics().str() +
@@ -116,8 +115,6 @@
 
 TEST_F(IR_DirectVariableAccessWgslTest_RemoveUncalled, PtrUniform) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<private> keep_me : i32 = 42i;
 
 fn u(pre : i32, p : ptr<uniform, i32>, post : i32) -> i32 {
@@ -137,8 +134,6 @@
 
 TEST_F(IR_DirectVariableAccessWgslTest_RemoveUncalled, PtrStorage) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<private> keep_me : i32 = 42i;
 
 fn s(pre : i32, p : ptr<storage, i32>, post : i32) -> i32 {
@@ -157,8 +152,6 @@
 
 TEST_F(IR_DirectVariableAccessWgslTest_RemoveUncalled, PtrWorkgroup) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<private> keep_me : i32 = 42i;
 
 fn w(pre : i32, p : ptr<workgroup, i32>, post : i32) -> i32 {
@@ -255,8 +248,6 @@
 
 TEST_F(IR_DirectVariableAccessWgslTest_PtrChains, ConstantIndices) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<uniform> U : array<array<array<vec4<i32>, 8>, 8>, 8>;
 
 fn a(pre : i32, p : ptr<uniform, vec4<i32>>, post : i32) -> vec4<i32> {
@@ -288,20 +279,20 @@
         R"(
 @group(0) @binding(0) var<uniform> U : array<array<array<vec4<i32>, 8u>, 8u>, 8u>;
 
-fn a_U_X_X_X(pre : i32, p_indices : array<u32, 3u>, post : i32) -> vec4<i32> {
+fn a(pre : i32, p_indices : array<u32, 3u>, post : i32) -> vec4<i32> {
   return U[p_indices[0u]][p_indices[1u]][p_indices[2u]];
 }
 
 fn b() {
-  a_U_X_X_X(10i, array<u32, 3u>(u32(1i), u32(2i), u32(3i)), 20i);
+  a(10i, array<u32, 3u>(u32(1i), u32(2i), u32(3i)), 20i);
 }
 
-fn c_U() {
-  a_U_X_X_X(10i, array<u32, 3u>(u32(1i), u32(2i), u32(3i)), 20i);
+fn c() {
+  a(10i, array<u32, 3u>(u32(1i), u32(2i), u32(3i)), 20i);
 }
 
 fn d() {
-  c_U();
+  c();
 }
 )";
 
@@ -312,8 +303,6 @@
 
 TEST_F(IR_DirectVariableAccessWgslTest_PtrChains, DynamicIndices) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<uniform> U : array<array<array<vec4<i32>, 8>, 8>, 8>;
 
 var<private> i : i32;
@@ -373,24 +362,24 @@
   return i;
 }
 
-fn a_U_X_X_X(pre : i32, p_indices : array<u32, 3u>, post : i32) -> vec4<i32> {
+fn a(pre : i32, p_indices : array<u32, 3u>, post : i32) -> vec4<i32> {
   return U[p_indices[0u]][p_indices[1u]][p_indices[2u]];
 }
 
 fn b() {
   let v = first();
   let v_1 = second();
-  a_U_X_X_X(10i, array<u32, 3u>(u32(v), u32(v_1), u32(third())), 20i);
+  a(10i, array<u32, 3u>(u32(v), u32(v_1), u32(third())), 20i);
 }
 
-fn c_U() {
+fn c() {
   let v_2 = first();
   let v_3 = second();
-  a_U_X_X_X(10i, array<u32, 3u>(u32(v_2), u32(v_3), u32(third())), 20i);
+  a(10i, array<u32, 3u>(u32(v_2), u32(v_3), u32(third())), 20i);
 }
 
 fn d() {
-  c_U();
+  c();
 }
 )";
 
@@ -401,8 +390,6 @@
 
 TEST_F(IR_DirectVariableAccessWgslTest_PtrChains, DynamicIndicesForLoopInit) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<uniform> U : array<array<vec4<i32>, 8>, 8>;
 
 var<private> i : i32;
@@ -451,24 +438,24 @@
   return i;
 }
 
-fn a_U_X_X(pre : i32, p_indices : array<u32, 2u>, post : i32) -> vec4<i32> {
+fn a(pre : i32, p_indices : array<u32, 2u>, post : i32) -> vec4<i32> {
   return U[p_indices[0u]][p_indices[1u]];
 }
 
 fn b() {
   for(let v = first(); true; ) {
-    a_U_X_X(10i, array<u32, 2u>(u32(v), u32(second())), 20i);
+    a(10i, array<u32, 2u>(u32(v), u32(second())), 20i);
   }
 }
 
-fn c_U() {
+fn c() {
   for(let v_1 = first(); true; ) {
-    a_U_X_X(10i, array<u32, 2u>(u32(v_1), u32(second())), 20i);
+    a(10i, array<u32, 2u>(u32(v_1), u32(second())), 20i);
   }
 }
 
 fn d() {
-  c_U();
+  c();
 }
 )";
 
@@ -479,8 +466,6 @@
 
 TEST_F(IR_DirectVariableAccessWgslTest_PtrChains, DynamicIndicesForLoopCond) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<uniform> U : array<array<vec4<i32>, 8>, 8>;
 
 var<private> i : i32;
@@ -531,28 +516,28 @@
   return i;
 }
 
-fn a_U_X_X(pre : i32, p_indices : array<u32, 2u>, post : i32) -> vec4<i32> {
+fn a(pre : i32, p_indices : array<u32, 2u>, post : i32) -> vec4<i32> {
   return U[p_indices[0u]][p_indices[1u]];
 }
 
 fn b() {
   let v = first();
   let v_1 = second();
-  while((a_U_X_X(10i, array<u32, 2u>(u32(v), u32(v_1)), 20i).x < 4i)) {
+  while((a(10i, array<u32, 2u>(u32(v), u32(v_1)), 20i).x < 4i)) {
     let body = 1i;
   }
 }
 
-fn c_U() {
+fn c() {
   let v_2 = first();
   let v_3 = second();
-  while((a_U_X_X(10i, array<u32, 2u>(u32(v_2), u32(v_3)), 20i).x < 4i)) {
+  while((a(10i, array<u32, 2u>(u32(v_2), u32(v_3)), 20i).x < 4i)) {
     let body = 1i;
   }
 }
 
 fn d() {
-  c_U();
+  c();
 }
 )";
 
@@ -563,8 +548,6 @@
 
 TEST_F(IR_DirectVariableAccessWgslTest_PtrChains, DynamicIndicesForLoopCont) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<uniform> U : array<array<vec4<i32>, 8>, 8>;
 
 var<private> i : i32;
@@ -615,28 +598,28 @@
   return i;
 }
 
-fn a_U_X_X(pre : i32, p_indices : array<u32, 2u>, post : i32) -> vec4<i32> {
+fn a(pre : i32, p_indices : array<u32, 2u>, post : i32) -> vec4<i32> {
   return U[p_indices[0u]][p_indices[1u]];
 }
 
 fn b() {
   let v = first();
   let v_1 = second();
-  for(var i : i32 = 0i; (i < 3i); a_U_X_X(10i, array<u32, 2u>(u32(v), u32(v_1)), 20i)) {
+  for(var i : i32 = 0i; (i < 3i); a(10i, array<u32, 2u>(u32(v), u32(v_1)), 20i)) {
     i = (i + 1i);
   }
 }
 
-fn c_U() {
+fn c() {
   let v_2 = first();
   let v_3 = second();
-  for(var i : i32 = 0i; (i < 3i); a_U_X_X(10i, array<u32, 2u>(u32(v_2), u32(v_3)), 20i)) {
+  for(var i : i32 = 0i; (i < 3i); a(10i, array<u32, 2u>(u32(v_2), u32(v_3)), 20i)) {
     i = (i + 1i);
   }
 }
 
 fn d() {
-  c_U();
+  c();
 }
 )";
 
@@ -647,8 +630,6 @@
 
 TEST_F(IR_DirectVariableAccessWgslTest_PtrChains, DynamicIndicesWhileCond) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<uniform> U : array<array<vec4<i32>, 8>, 8>;
 
 var<private> i : i32;
@@ -699,28 +680,28 @@
   return i;
 }
 
-fn a_U_X_X(pre : i32, p_indices : array<u32, 2u>, post : i32) -> vec4<i32> {
+fn a(pre : i32, p_indices : array<u32, 2u>, post : i32) -> vec4<i32> {
   return U[p_indices[0u]][p_indices[1u]];
 }
 
 fn b() {
   let v = first();
   let v_1 = second();
-  while((a_U_X_X(10i, array<u32, 2u>(u32(v), u32(v_1)), 20i).x < 4i)) {
+  while((a(10i, array<u32, 2u>(u32(v), u32(v_1)), 20i).x < 4i)) {
     let body = 1i;
   }
 }
 
-fn c_U() {
+fn c() {
   let v_2 = first();
   let v_3 = second();
-  while((a_U_X_X(10i, array<u32, 2u>(u32(v_2), u32(v_3)), 20i).x < 4i)) {
+  while((a(10i, array<u32, 2u>(u32(v_2), u32(v_3)), 20i).x < 4i)) {
     let body = 1i;
   }
 }
 
 fn d() {
-  c_U();
+  c();
 }
 )";
 
@@ -739,8 +720,6 @@
 
 TEST_F(IR_DirectVariableAccessWgslTest_UniformAS, Param_ptr_i32_read) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<uniform> U : i32;
 
 fn a(pre : i32, p : ptr<uniform, i32>, post : i32) -> i32 {
@@ -755,12 +734,12 @@
     auto* expect = R"(
 @group(0) @binding(0) var<uniform> U : i32;
 
-fn a_U(pre : i32, post : i32) -> i32 {
+fn a(pre : i32, post : i32) -> i32 {
   return U;
 }
 
 fn b() {
-  a_U(10i, 20i);
+  a(10i, 20i);
 }
 )";
 
@@ -771,8 +750,6 @@
 
 TEST_F(IR_DirectVariableAccessWgslTest_UniformAS, Param_ptr_vec4i32_Via_array_DynamicRead) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<uniform> U : array<vec4<i32>, 8>;
 
 fn a(pre : i32, p : ptr<uniform, vec4<i32>>, post : i32) -> vec4<i32> {
@@ -788,13 +765,13 @@
     auto* expect = R"(
 @group(0) @binding(0) var<uniform> U : array<vec4<i32>, 8u>;
 
-fn a_U_X(pre : i32, p_indices : array<u32, 1u>, post : i32) -> vec4<i32> {
+fn a(pre : i32, p_indices : array<u32, 1u>, post : i32) -> vec4<i32> {
   return U[p_indices[0u]];
 }
 
 fn b() {
   let I = 3i;
-  a_U_X(10i, array<u32, 1u>(u32(I)), 20i);
+  a(10i, array<u32, 1u>(u32(I)), 20i);
 }
 )";
 
@@ -805,8 +782,6 @@
 
 TEST_F(IR_DirectVariableAccessWgslTest_UniformAS, CallChaining) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct Inner {
   mat : mat3x4<f32>,
 };
@@ -878,55 +853,55 @@
 
 @group(0) @binding(0) var<uniform> U : Outer;
 
-fn f0_U_mat_X(p_indices : array<u32, 1u>) -> f32 {
+fn f0(p_indices : array<u32, 1u>) -> f32 {
   return U.mat[p_indices[0u]].x;
 }
 
-fn f0_U_arr_X_mat_X(p_indices : array<u32, 2u>) -> f32 {
+fn f0_1(p_indices : array<u32, 2u>) -> f32 {
   return U.arr[p_indices[0u]].mat[p_indices[1u]].x;
 }
 
-fn f1_U_mat() -> f32 {
+fn f1() -> f32 {
   var res : f32;
-  let v = f0_U_mat_X(array<u32, 1u>(u32(1i)));
+  let v = f0(array<u32, 1u>(u32(1i)));
   res = (res + v);
-  let v_1 = f0_U_mat_X(array<u32, 1u>(u32(1i)));
+  let v_1 = f0(array<u32, 1u>(u32(1i)));
   res = (res + v_1);
-  let v_2 = f0_U_arr_X_mat_X(array<u32, 2u>(u32(2i), u32(1i)));
+  let v_2 = f0_1(array<u32, 2u>(u32(2i), u32(1i)));
   res = (res + v_2);
-  let v_3 = f0_U_arr_X_mat_X(array<u32, 2u>(u32(2i), u32(1i)));
+  let v_3 = f0_1(array<u32, 2u>(u32(2i), u32(1i)));
   res = (res + v_3);
   return res;
 }
 
-fn f1_U_arr_X_mat(p_indices : array<u32, 1u>) -> f32 {
+fn f1_1(p_indices : array<u32, 1u>) -> f32 {
   let v_4 = p_indices[0u];
   var res : f32;
-  let v_5 = f0_U_arr_X_mat_X(array<u32, 2u>(v_4, u32(1i)));
+  let v_5 = f0_1(array<u32, 2u>(v_4, u32(1i)));
   res = (res + v_5);
-  let v_6 = f0_U_arr_X_mat_X(array<u32, 2u>(v_4, u32(1i)));
+  let v_6 = f0_1(array<u32, 2u>(v_4, u32(1i)));
   res = (res + v_6);
-  let v_7 = f0_U_arr_X_mat_X(array<u32, 2u>(u32(2i), u32(1i)));
+  let v_7 = f0_1(array<u32, 2u>(u32(2i), u32(1i)));
   res = (res + v_7);
-  let v_8 = f0_U_arr_X_mat_X(array<u32, 2u>(u32(2i), u32(1i)));
+  let v_8 = f0_1(array<u32, 2u>(u32(2i), u32(1i)));
   res = (res + v_8);
   return res;
 }
 
-fn f2_U_arr_X(p_indices : array<u32, 1u>) -> f32 {
-  return f1_U_arr_X_mat(array<u32, 1u>(p_indices[0u]));
+fn f2(p_indices : array<u32, 1u>) -> f32 {
+  return f1_1(array<u32, 1u>(p_indices[0u]));
 }
 
-fn f3_U_arr_U_mat() -> f32 {
-  return (f2_U_arr_X(array<u32, 1u>(u32(3i))) + f1_U_mat());
+fn f3() -> f32 {
+  return (f2(array<u32, 1u>(u32(3i))) + f1());
 }
 
-fn f4_U() -> f32 {
-  return f3_U_arr_U_mat();
+fn f4() -> f32 {
+  return f3();
 }
 
 fn b() {
-  f4_U();
+  f4();
 }
 )";
 
@@ -946,8 +921,6 @@
 
 TEST_F(IR_DirectVariableAccessWgslTest_StorageAS, Param_ptr_i32_Via_struct_read) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct str {
   i : i32,
 };
@@ -970,12 +943,12 @@
 
 @group(0) @binding(0) var<storage, read> S : str;
 
-fn a_S_i(pre : i32, post : i32) -> i32 {
+fn a(pre : i32, post : i32) -> i32 {
   return S.i;
 }
 
 fn b() {
-  a_S_i(10i, 20i);
+  a(10i, 20i);
 }
 )";
 
@@ -986,8 +959,6 @@
 
 TEST_F(IR_DirectVariableAccessWgslTest_StorageAS, Param_ptr_arr_i32_Via_struct_write) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct str {
   arr : array<i32, 4>,
 };
@@ -1010,12 +981,12 @@
 
 @group(0) @binding(0) var<storage, read_write> S : str;
 
-fn a_S_arr(pre : i32, post : i32) {
+fn a(pre : i32, post : i32) {
   S.arr = array<i32, 4u>();
 }
 
 fn b() {
-  a_S_arr(10i, 20i);
+  a(10i, 20i);
 }
 )";
 
@@ -1026,8 +997,6 @@
 
 TEST_F(IR_DirectVariableAccessWgslTest_StorageAS, Param_ptr_vec4i32_Via_array_DynamicWrite) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage, read_write> S : array<vec4<i32>, 8>;
 
 fn a(pre : i32, p : ptr<storage, vec4<i32>, read_write>, post : i32) {
@@ -1043,13 +1012,13 @@
     auto* expect = R"(
 @group(0) @binding(0) var<storage, read_write> S : array<vec4<i32>, 8u>;
 
-fn a_S_X(pre : i32, p_indices : array<u32, 1u>, post : i32) {
+fn a(pre : i32, p_indices : array<u32, 1u>, post : i32) {
   S[p_indices[0u]] = vec4<i32>();
 }
 
 fn b() {
   let I = 3i;
-  a_S_X(10i, array<u32, 1u>(u32(I)), 20i);
+  a(10i, array<u32, 1u>(u32(I)), 20i);
 }
 )";
 
@@ -1060,8 +1029,6 @@
 
 TEST_F(IR_DirectVariableAccessWgslTest_StorageAS, CallChaining) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct Inner {
   mat : mat3x4<f32>,
 };
@@ -1133,55 +1100,55 @@
 
 @group(0) @binding(0) var<storage, read> S : Outer;
 
-fn f0_S_mat_X(p_indices : array<u32, 1u>) -> f32 {
+fn f0(p_indices : array<u32, 1u>) -> f32 {
   return S.mat[p_indices[0u]].x;
 }
 
-fn f0_S_arr_X_mat_X(p_indices : array<u32, 2u>) -> f32 {
+fn f0_1(p_indices : array<u32, 2u>) -> f32 {
   return S.arr[p_indices[0u]].mat[p_indices[1u]].x;
 }
 
-fn f1_S_mat() -> f32 {
+fn f1() -> f32 {
   var res : f32;
-  let v = f0_S_mat_X(array<u32, 1u>(u32(1i)));
+  let v = f0(array<u32, 1u>(u32(1i)));
   res = (res + v);
-  let v_1 = f0_S_mat_X(array<u32, 1u>(u32(1i)));
+  let v_1 = f0(array<u32, 1u>(u32(1i)));
   res = (res + v_1);
-  let v_2 = f0_S_arr_X_mat_X(array<u32, 2u>(u32(2i), u32(1i)));
+  let v_2 = f0_1(array<u32, 2u>(u32(2i), u32(1i)));
   res = (res + v_2);
-  let v_3 = f0_S_arr_X_mat_X(array<u32, 2u>(u32(2i), u32(1i)));
+  let v_3 = f0_1(array<u32, 2u>(u32(2i), u32(1i)));
   res = (res + v_3);
   return res;
 }
 
-fn f1_S_arr_X_mat(p_indices : array<u32, 1u>) -> f32 {
+fn f1_1(p_indices : array<u32, 1u>) -> f32 {
   let v_4 = p_indices[0u];
   var res : f32;
-  let v_5 = f0_S_arr_X_mat_X(array<u32, 2u>(v_4, u32(1i)));
+  let v_5 = f0_1(array<u32, 2u>(v_4, u32(1i)));
   res = (res + v_5);
-  let v_6 = f0_S_arr_X_mat_X(array<u32, 2u>(v_4, u32(1i)));
+  let v_6 = f0_1(array<u32, 2u>(v_4, u32(1i)));
   res = (res + v_6);
-  let v_7 = f0_S_arr_X_mat_X(array<u32, 2u>(u32(2i), u32(1i)));
+  let v_7 = f0_1(array<u32, 2u>(u32(2i), u32(1i)));
   res = (res + v_7);
-  let v_8 = f0_S_arr_X_mat_X(array<u32, 2u>(u32(2i), u32(1i)));
+  let v_8 = f0_1(array<u32, 2u>(u32(2i), u32(1i)));
   res = (res + v_8);
   return res;
 }
 
-fn f2_S_arr_X(p_indices : array<u32, 1u>) -> f32 {
-  return f1_S_arr_X_mat(array<u32, 1u>(p_indices[0u]));
+fn f2(p_indices : array<u32, 1u>) -> f32 {
+  return f1_1(array<u32, 1u>(p_indices[0u]));
 }
 
-fn f3_S_arr_S_mat() -> f32 {
-  return (f2_S_arr_X(array<u32, 1u>(u32(3i))) + f1_S_mat());
+fn f3() -> f32 {
+  return (f2(array<u32, 1u>(u32(3i))) + f1());
 }
 
-fn f4_S() -> f32 {
-  return f3_S_arr_S_mat();
+fn f4() -> f32 {
+  return f3();
 }
 
 fn b() {
-  f4_S();
+  f4();
 }
 )";
 
@@ -1201,8 +1168,6 @@
 
 TEST_F(IR_DirectVariableAccessWgslTest_WorkgroupAS, Param_ptr_vec4i32_Via_array_StaticRead) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<workgroup> W : array<vec4<i32>, 8>;
 
 fn a(pre : i32, p : ptr<workgroup, vec4<i32>>, post : i32) -> vec4<i32> {
@@ -1217,12 +1182,12 @@
     auto* expect = R"(
 var<workgroup> W : array<vec4<i32>, 8u>;
 
-fn a_W_X(pre : i32, p_indices : array<u32, 1u>, post : i32) -> vec4<i32> {
+fn a(pre : i32, p_indices : array<u32, 1u>, post : i32) -> vec4<i32> {
   return W[p_indices[0u]];
 }
 
 fn b() {
-  a_W_X(10i, array<u32, 1u>(u32(3i)), 20i);
+  a(10i, array<u32, 1u>(u32(3i)), 20i);
 }
 )";
 
@@ -1233,8 +1198,6 @@
 
 TEST_F(IR_DirectVariableAccessWgslTest_WorkgroupAS, Param_ptr_vec4i32_Via_array_StaticWrite) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<workgroup> W : array<vec4<i32>, 8>;
 
 fn a(pre : i32, p : ptr<workgroup, vec4<i32>>, post : i32) {
@@ -1249,12 +1212,12 @@
     auto* expect = R"(
 var<workgroup> W : array<vec4<i32>, 8u>;
 
-fn a_W_X(pre : i32, p_indices : array<u32, 1u>, post : i32) {
+fn a(pre : i32, p_indices : array<u32, 1u>, post : i32) {
   W[p_indices[0u]] = vec4<i32>();
 }
 
 fn b() {
-  a_W_X(10i, array<u32, 1u>(u32(3i)), 20i);
+  a(10i, array<u32, 1u>(u32(3i)), 20i);
 }
 )";
 
@@ -1265,8 +1228,6 @@
 
 TEST_F(IR_DirectVariableAccessWgslTest_WorkgroupAS, CallChaining) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct Inner {
   mat : mat3x4<f32>,
 };
@@ -1338,55 +1299,55 @@
 
 var<workgroup> W : Outer;
 
-fn f0_W_mat_X(p_indices : array<u32, 1u>) -> f32 {
+fn f0(p_indices : array<u32, 1u>) -> f32 {
   return W.mat[p_indices[0u]].x;
 }
 
-fn f0_W_arr_X_mat_X(p_indices : array<u32, 2u>) -> f32 {
+fn f0_1(p_indices : array<u32, 2u>) -> f32 {
   return W.arr[p_indices[0u]].mat[p_indices[1u]].x;
 }
 
-fn f1_W_mat() -> f32 {
+fn f1() -> f32 {
   var res : f32;
-  let v = f0_W_mat_X(array<u32, 1u>(u32(1i)));
+  let v = f0(array<u32, 1u>(u32(1i)));
   res = (res + v);
-  let v_1 = f0_W_mat_X(array<u32, 1u>(u32(1i)));
+  let v_1 = f0(array<u32, 1u>(u32(1i)));
   res = (res + v_1);
-  let v_2 = f0_W_arr_X_mat_X(array<u32, 2u>(u32(2i), u32(1i)));
+  let v_2 = f0_1(array<u32, 2u>(u32(2i), u32(1i)));
   res = (res + v_2);
-  let v_3 = f0_W_arr_X_mat_X(array<u32, 2u>(u32(2i), u32(1i)));
+  let v_3 = f0_1(array<u32, 2u>(u32(2i), u32(1i)));
   res = (res + v_3);
   return res;
 }
 
-fn f1_W_arr_X_mat(p_indices : array<u32, 1u>) -> f32 {
+fn f1_1(p_indices : array<u32, 1u>) -> f32 {
   let v_4 = p_indices[0u];
   var res : f32;
-  let v_5 = f0_W_arr_X_mat_X(array<u32, 2u>(v_4, u32(1i)));
+  let v_5 = f0_1(array<u32, 2u>(v_4, u32(1i)));
   res = (res + v_5);
-  let v_6 = f0_W_arr_X_mat_X(array<u32, 2u>(v_4, u32(1i)));
+  let v_6 = f0_1(array<u32, 2u>(v_4, u32(1i)));
   res = (res + v_6);
-  let v_7 = f0_W_arr_X_mat_X(array<u32, 2u>(u32(2i), u32(1i)));
+  let v_7 = f0_1(array<u32, 2u>(u32(2i), u32(1i)));
   res = (res + v_7);
-  let v_8 = f0_W_arr_X_mat_X(array<u32, 2u>(u32(2i), u32(1i)));
+  let v_8 = f0_1(array<u32, 2u>(u32(2i), u32(1i)));
   res = (res + v_8);
   return res;
 }
 
-fn f2_W_arr_X(p_indices : array<u32, 1u>) -> f32 {
-  return f1_W_arr_X_mat(array<u32, 1u>(p_indices[0u]));
+fn f2(p_indices : array<u32, 1u>) -> f32 {
+  return f1_1(array<u32, 1u>(p_indices[0u]));
 }
 
-fn f3_W_arr_W_mat() -> f32 {
-  return (f2_W_arr_X(array<u32, 1u>(u32(3i))) + f1_W_mat());
+fn f3() -> f32 {
+  return (f2(array<u32, 1u>(u32(3i))) + f1());
 }
 
-fn f4_W() -> f32 {
-  return f3_W_arr_W_mat();
+fn f4() -> f32 {
+  return f3();
 }
 
 fn b() {
-  f4_W();
+  f4();
 }
 )";
 
@@ -1406,8 +1367,6 @@
 
 TEST_F(IR_DirectVariableAccessWgslTest_PrivateAS, Enabled_Param_ptr_i32_read) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 fn a(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
   return *(p);
 }
@@ -1422,12 +1381,12 @@
     auto* expect = R"(
 var<private> P : i32;
 
-fn a_P(pre : i32, post : i32) -> i32 {
+fn a(pre : i32, post : i32) -> i32 {
   return P;
 }
 
 fn b() {
-  a_P(10i, 20i);
+  a(10i, 20i);
 }
 )";
 
@@ -1438,8 +1397,6 @@
 
 TEST_F(IR_DirectVariableAccessWgslTest_PrivateAS, Enabled_Param_ptr_i32_write) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 fn a(pre : i32, p : ptr<private, i32>, post : i32) {
   *(p) = 42;
 }
@@ -1454,12 +1411,12 @@
     auto* expect = R"(
 var<private> P : i32;
 
-fn a_P(pre : i32, post : i32) {
+fn a(pre : i32, post : i32) {
   P = 42i;
 }
 
 fn b() {
-  a_P(10i, 20i);
+  a(10i, 20i);
 }
 )";
 
@@ -1470,8 +1427,6 @@
 
 TEST_F(IR_DirectVariableAccessWgslTest_PrivateAS, Enabled_Param_ptr_i32_Via_struct_read) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct str {
   i : i32,
 };
@@ -1494,12 +1449,12 @@
 
 var<private> P : str;
 
-fn a_P_i(pre : i32, post : i32) -> i32 {
+fn a(pre : i32, post : i32) -> i32 {
   return P.i;
 }
 
 fn b() {
-  a_P_i(10i, 20i);
+  a(10i, 20i);
 }
 )";
 
@@ -1510,8 +1465,6 @@
 
 TEST_F(IR_DirectVariableAccessWgslTest_PrivateAS, Disabled_Param_ptr_i32_Via_struct_read) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct str {
   i : i32,
 }
@@ -1529,15 +1482,16 @@
 
     auto* expect = src;
 
-    auto got = Run(src);
+    wgsl::writer::ProgramOptions program_options;
+    program_options.allowed_features.features.emplace(
+        wgsl::LanguageFeature::kUnrestrictedPointerParameters);
+    auto got = Run(src, /* transform_options */ {}, program_options);
 
     EXPECT_EQ(expect, got);
 }
 
 TEST_F(IR_DirectVariableAccessWgslTest_PrivateAS, Enabled_Param_ptr_arr_i32_Via_struct_write) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct str {
   arr : array<i32, 4>,
 };
@@ -1560,12 +1514,12 @@
 
 var<private> P : str;
 
-fn a_P_arr(pre : i32, post : i32) {
+fn a(pre : i32, post : i32) {
   P.arr = array<i32, 4u>();
 }
 
 fn b() {
-  a_P_arr(10i, 20i);
+  a(10i, 20i);
 }
 )";
 
@@ -1576,8 +1530,6 @@
 
 TEST_F(IR_DirectVariableAccessWgslTest_PrivateAS, Disabled_Param_ptr_arr_i32_Via_struct_write) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct str {
   arr : array<i32, 4u>,
 }
@@ -1595,15 +1547,16 @@
 
     auto* expect = src;
 
-    auto got = Run(src);
+    wgsl::writer::ProgramOptions program_options;
+    program_options.allowed_features.features.emplace(
+        wgsl::LanguageFeature::kUnrestrictedPointerParameters);
+    auto got = Run(src, /* transform_options */ {}, program_options);
 
     EXPECT_EQ(expect, got);
 }
 
 TEST_F(IR_DirectVariableAccessWgslTest_PrivateAS, Enabled_Param_ptr_i32_mixed) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct str {
   i : i32,
 };
@@ -1634,22 +1587,22 @@
 
 var<private> Pa : array<i32, 4u>;
 
-fn a_Pi(pre : i32, post : i32) -> i32 {
+fn a(pre : i32, post : i32) -> i32 {
   return Pi;
 }
 
-fn a_Ps_i(pre : i32, post : i32) -> i32 {
+fn a_1(pre : i32, post : i32) -> i32 {
   return Ps.i;
 }
 
-fn a_Pa_X(pre : i32, p_indices : array<u32, 1u>, post : i32) -> i32 {
+fn a_2(pre : i32, p_indices : array<u32, 1u>, post : i32) -> i32 {
   return Pa[p_indices[0u]];
 }
 
 fn b() {
-  a_Pi(10i, 20i);
-  a_Ps_i(30i, 40i);
-  a_Pa_X(50i, array<u32, 1u>(u32(2i)), 60i);
+  a(10i, 20i);
+  a_1(30i, 40i);
+  a_2(50i, array<u32, 1u>(u32(2i)), 60i);
 }
 )";
 
@@ -1660,8 +1613,6 @@
 
 TEST_F(IR_DirectVariableAccessWgslTest_PrivateAS, Disabled_Param_ptr_i32_mixed) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<private> Pi : i32;
 
 struct str {
@@ -1685,15 +1636,16 @@
 
     auto* expect = src;
 
-    auto got = Run(src);
+    wgsl::writer::ProgramOptions program_options;
+    program_options.allowed_features.features.emplace(
+        wgsl::LanguageFeature::kUnrestrictedPointerParameters);
+    auto got = Run(src, /* transform_options */ {}, program_options);
 
     EXPECT_EQ(expect, got);
 }
 
 TEST_F(IR_DirectVariableAccessWgslTest_PrivateAS, Enabled_CallChaining) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct Inner {
   mat : mat3x4<f32>,
 };
@@ -1765,55 +1717,55 @@
 
 var<private> P : Outer;
 
-fn f0_P_mat_X(p_indices : array<u32, 1u>) -> f32 {
+fn f0(p_indices : array<u32, 1u>) -> f32 {
   return P.mat[p_indices[0u]].x;
 }
 
-fn f0_P_arr_X_mat_X(p_indices : array<u32, 2u>) -> f32 {
+fn f0_1(p_indices : array<u32, 2u>) -> f32 {
   return P.arr[p_indices[0u]].mat[p_indices[1u]].x;
 }
 
-fn f1_P_mat() -> f32 {
+fn f1() -> f32 {
   var res : f32;
-  let v = f0_P_mat_X(array<u32, 1u>(u32(1i)));
+  let v = f0(array<u32, 1u>(u32(1i)));
   res = (res + v);
-  let v_1 = f0_P_mat_X(array<u32, 1u>(u32(1i)));
+  let v_1 = f0(array<u32, 1u>(u32(1i)));
   res = (res + v_1);
-  let v_2 = f0_P_arr_X_mat_X(array<u32, 2u>(u32(2i), u32(1i)));
+  let v_2 = f0_1(array<u32, 2u>(u32(2i), u32(1i)));
   res = (res + v_2);
-  let v_3 = f0_P_arr_X_mat_X(array<u32, 2u>(u32(2i), u32(1i)));
+  let v_3 = f0_1(array<u32, 2u>(u32(2i), u32(1i)));
   res = (res + v_3);
   return res;
 }
 
-fn f1_P_arr_X_mat(p_indices : array<u32, 1u>) -> f32 {
+fn f1_1(p_indices : array<u32, 1u>) -> f32 {
   let v_4 = p_indices[0u];
   var res : f32;
-  let v_5 = f0_P_arr_X_mat_X(array<u32, 2u>(v_4, u32(1i)));
+  let v_5 = f0_1(array<u32, 2u>(v_4, u32(1i)));
   res = (res + v_5);
-  let v_6 = f0_P_arr_X_mat_X(array<u32, 2u>(v_4, u32(1i)));
+  let v_6 = f0_1(array<u32, 2u>(v_4, u32(1i)));
   res = (res + v_6);
-  let v_7 = f0_P_arr_X_mat_X(array<u32, 2u>(u32(2i), u32(1i)));
+  let v_7 = f0_1(array<u32, 2u>(u32(2i), u32(1i)));
   res = (res + v_7);
-  let v_8 = f0_P_arr_X_mat_X(array<u32, 2u>(u32(2i), u32(1i)));
+  let v_8 = f0_1(array<u32, 2u>(u32(2i), u32(1i)));
   res = (res + v_8);
   return res;
 }
 
-fn f2_P_arr_X(p_indices : array<u32, 1u>) -> f32 {
-  return f1_P_arr_X_mat(array<u32, 1u>(p_indices[0u]));
+fn f2(p_indices : array<u32, 1u>) -> f32 {
+  return f1_1(array<u32, 1u>(p_indices[0u]));
 }
 
-fn f3_P_arr_P_mat() -> f32 {
-  return (f2_P_arr_X(array<u32, 1u>(u32(3i))) + f1_P_mat());
+fn f3() -> f32 {
+  return (f2(array<u32, 1u>(u32(3i))) + f1());
 }
 
-fn f4_P() -> f32 {
-  return f3_P_arr_P_mat();
+fn f4() -> f32 {
+  return f3();
 }
 
 fn b() {
-  f4_P();
+  f4();
 }
 )";
 
@@ -1824,8 +1776,6 @@
 
 TEST_F(IR_DirectVariableAccessWgslTest_PrivateAS, Disabled_CallChaining) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct Inner {
   mat : mat3x4<f32>,
 }
@@ -1877,7 +1827,10 @@
 
     auto* expect = src;
 
-    auto got = Run(src);
+    wgsl::writer::ProgramOptions program_options;
+    program_options.allowed_features.features.emplace(
+        wgsl::LanguageFeature::kUnrestrictedPointerParameters);
+    auto got = Run(src, /* transform_options */ {}, program_options);
 
     EXPECT_EQ(expect, got);
 }
@@ -1909,8 +1862,6 @@
 
 TEST_F(IR_DirectVariableAccessWgslTest_FunctionAS, Enabled_Param_ptr_i32_read) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 fn a(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
   return *(p);
 }
@@ -1922,13 +1873,13 @@
 )";
 
     auto* expect = R"(
-fn a_P(pre : i32, p_root : ptr<function, i32>, post : i32) -> i32 {
+fn a(pre : i32, p_root : ptr<function, i32>, post : i32) -> i32 {
   return *(p_root);
 }
 
 fn b() {
   var F : i32;
-  a_P(10i, &(F), 20i);
+  a(10i, &(F), 20i);
 }
 )";
 
@@ -1939,8 +1890,6 @@
 
 TEST_F(IR_DirectVariableAccessWgslTest_FunctionAS, Enabled_Param_ptr_i32_write) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 fn a(pre : i32, p : ptr<function, i32>, post : i32) {
   *(p) = 42;
 }
@@ -1952,13 +1901,13 @@
 )";
 
     auto* expect = R"(
-fn a_P(pre : i32, p_root : ptr<function, i32>, post : i32) {
+fn a(pre : i32, p_root : ptr<function, i32>, post : i32) {
   *(p_root) = 42i;
 }
 
 fn b() {
   var F : i32;
-  a_P(10i, &(F), 20i);
+  a(10i, &(F), 20i);
 }
 )";
 
@@ -1969,8 +1918,6 @@
 
 TEST_F(IR_DirectVariableAccessWgslTest_FunctionAS, Enabled_Param_ptr_i32_Via_struct_read) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct str {
   i : i32,
 };
@@ -1990,13 +1937,13 @@
   i : i32,
 }
 
-fn a_P_i(pre : i32, p_root : ptr<function, str>, post : i32) -> i32 {
+fn a(pre : i32, p_root : ptr<function, str>, post : i32) -> i32 {
   return (*(p_root)).i;
 }
 
 fn b() {
   var F : str;
-  a_P_i(10i, &(F), 20i);
+  a(10i, &(F), 20i);
 }
 )";
 
@@ -2007,8 +1954,6 @@
 
 TEST_F(IR_DirectVariableAccessWgslTest_FunctionAS, Enabled_Param_ptr_arr_i32_Via_struct_write) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct str {
   arr : array<i32, 4>,
 };
@@ -2028,13 +1973,13 @@
   arr : array<i32, 4u>,
 }
 
-fn a_P_arr(pre : i32, p_root : ptr<function, str>, post : i32) {
+fn a(pre : i32, p_root : ptr<function, str>, post : i32) {
   (*(p_root)).arr = array<i32, 4u>();
 }
 
 fn b() {
   var F : str;
-  a_P_arr(10i, &(F), 20i);
+  a(10i, &(F), 20i);
 }
 )";
 
@@ -2045,8 +1990,6 @@
 
 TEST_F(IR_DirectVariableAccessWgslTest_FunctionAS, Enabled_Param_ptr_i32_mixed) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct str {
   i : i32,
 };
@@ -2067,7 +2010,7 @@
 )";
 
     auto* expect = R"(
-fn a_P(pre : i32, p_root : ptr<function, i32>, post : i32) -> i32 {
+fn a(pre : i32, p_root : ptr<function, i32>, post : i32) -> i32 {
   return *(p_root);
 }
 
@@ -2075,11 +2018,11 @@
   i : i32,
 }
 
-fn a_P_i(pre : i32, p_root : ptr<function, str>, post : i32) -> i32 {
+fn a_1(pre : i32, p_root : ptr<function, str>, post : i32) -> i32 {
   return (*(p_root)).i;
 }
 
-fn a_P_X(pre : i32, p_root : ptr<function, array<i32, 4u>>, p_indices : array<u32, 1u>, post : i32) -> i32 {
+fn a_2(pre : i32, p_root : ptr<function, array<i32, 4u>>, p_indices : array<u32, 1u>, post : i32) -> i32 {
   return (*(p_root))[p_indices[0u]];
 }
 
@@ -2087,9 +2030,9 @@
   var Fi : i32;
   var Fs : str;
   var Fa : array<i32, 4u>;
-  a_P(10i, &(Fi), 20i);
-  a_P_i(30i, &(Fs), 40i);
-  a_P_X(50i, &(Fa), array<u32, 1u>(u32(2i)), 60i);
+  a(10i, &(Fi), 20i);
+  a_1(30i, &(Fs), 40i);
+  a_2(50i, &(Fa), array<u32, 1u>(u32(2i)), 60i);
 }
 )";
 
@@ -2100,8 +2043,6 @@
 
 TEST_F(IR_DirectVariableAccessWgslTest_FunctionAS, Disabled_Param_ptr_i32_Via_struct_read) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 fn a(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
   return *(p);
 }
@@ -2118,15 +2059,16 @@
 
     auto* expect = src;
 
-    auto got = Run(src);
+    wgsl::writer::ProgramOptions program_options;
+    program_options.allowed_features.features.emplace(
+        wgsl::LanguageFeature::kUnrestrictedPointerParameters);
+    auto got = Run(src, /* transform_options */ {}, program_options);
 
     EXPECT_EQ(expect, got);
 }
 
 TEST_F(IR_DirectVariableAccessWgslTest_FunctionAS, Disabled_Param_ptr_arr_i32_Via_struct_write) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 fn a(pre : i32, p : ptr<function, array<i32, 4u>>, post : i32) {
   *(p) = array<i32, 4u>();
 }
@@ -2143,7 +2085,10 @@
 
     auto* expect = src;
 
-    auto got = Run(src);
+    wgsl::writer::ProgramOptions program_options;
+    program_options.allowed_features.features.emplace(
+        wgsl::LanguageFeature::kUnrestrictedPointerParameters);
+    auto got = Run(src, /* transform_options */ {}, program_options);
 
     EXPECT_EQ(expect, got);
 }
@@ -2159,8 +2104,6 @@
 
 TEST_F(IR_DirectVariableAccessWgslTest_BuiltinFn, ArrayLength) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage> S : array<f32>;
 
 fn len(p : ptr<storage, array<f32>>) -> u32 {
@@ -2175,12 +2118,12 @@
     auto* expect = R"(
 @group(0) @binding(0) var<storage, read> S : array<f32>;
 
-fn len_S() -> u32 {
+fn len() -> u32 {
   return arrayLength(&(S));
 }
 
 fn f() {
-  let n = len_S();
+  let n = len();
 }
 )";
 
@@ -2191,8 +2134,6 @@
 
 TEST_F(IR_DirectVariableAccessWgslTest_BuiltinFn, WorkgroupUniformLoad) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<workgroup> W : f32;
 
 fn load(p : ptr<workgroup, f32>) -> f32 {
@@ -2207,12 +2148,12 @@
     auto* expect = R"(
 var<workgroup> W : f32;
 
-fn load_W() -> f32 {
+fn load() -> f32 {
   return workgroupUniformLoad(&(W));
 }
 
 fn f() {
-  let v = load_W();
+  let v = load();
 }
 )";
 
@@ -2232,8 +2173,6 @@
 
 TEST_F(IR_DirectVariableAccessWgslTest_Complex, Param_ptr_mixed_vec4i32_ViaMultiple) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct str {
   i : vec4<i32>,
 };
@@ -2330,84 +2269,84 @@
 
 var<workgroup> W_arr_arr : array<array<vec4<i32>, 8u>, 4u>;
 
-fn fn_u_U() -> vec4<i32> {
+fn fn_u() -> vec4<i32> {
   return U;
 }
 
-fn fn_u_U_str_i() -> vec4<i32> {
+fn fn_u_1() -> vec4<i32> {
   return U_str.i;
 }
 
-fn fn_u_U_arr_X(p_indices : array<u32, 1u>) -> vec4<i32> {
+fn fn_u_2(p_indices : array<u32, 1u>) -> vec4<i32> {
   return U_arr[p_indices[0u]];
 }
 
-fn fn_u_U_arr_arr_X_X(p_indices : array<u32, 2u>) -> vec4<i32> {
+fn fn_u_3(p_indices : array<u32, 2u>) -> vec4<i32> {
   return U_arr_arr[p_indices[0u]][p_indices[1u]];
 }
 
-fn fn_s_S() -> vec4<i32> {
+fn fn_s() -> vec4<i32> {
   return S;
 }
 
-fn fn_s_S_str_i() -> vec4<i32> {
+fn fn_s_1() -> vec4<i32> {
   return S_str.i;
 }
 
-fn fn_s_S_arr_X(p_indices : array<u32, 1u>) -> vec4<i32> {
+fn fn_s_2(p_indices : array<u32, 1u>) -> vec4<i32> {
   return S_arr[p_indices[0u]];
 }
 
-fn fn_s_S_arr_arr_X_X(p_indices : array<u32, 2u>) -> vec4<i32> {
+fn fn_s_3(p_indices : array<u32, 2u>) -> vec4<i32> {
   return S_arr_arr[p_indices[0u]][p_indices[1u]];
 }
 
-fn fn_w_W() -> vec4<i32> {
+fn fn_w() -> vec4<i32> {
   return W;
 }
 
-fn fn_w_W_str_i() -> vec4<i32> {
+fn fn_w_1() -> vec4<i32> {
   return W_str.i;
 }
 
-fn fn_w_W_arr_X(p_indices : array<u32, 1u>) -> vec4<i32> {
+fn fn_w_2(p_indices : array<u32, 1u>) -> vec4<i32> {
   return W_arr[p_indices[0u]];
 }
 
-fn fn_w_W_arr_arr_X_X(p_indices : array<u32, 2u>) -> vec4<i32> {
+fn fn_w_3(p_indices : array<u32, 2u>) -> vec4<i32> {
   return W_arr_arr[p_indices[0u]][p_indices[1u]];
 }
 
 fn b() {
   let I = 3i;
   let J = 4i;
-  let u = fn_u_U();
-  let u_str = fn_u_U_str_i();
-  let u_arr0 = fn_u_U_arr_X(array<u32, 1u>(u32(0i)));
-  let u_arr1 = fn_u_U_arr_X(array<u32, 1u>(u32(1i)));
-  let u_arrI = fn_u_U_arr_X(array<u32, 1u>(u32(I)));
-  let u_arr1_arr0 = fn_u_U_arr_arr_X_X(array<u32, 2u>(u32(1i), u32(0i)));
-  let u_arr2_arrI = fn_u_U_arr_arr_X_X(array<u32, 2u>(u32(2i), u32(I)));
-  let u_arrI_arr2 = fn_u_U_arr_arr_X_X(array<u32, 2u>(u32(I), u32(2i)));
-  let u_arrI_arrJ = fn_u_U_arr_arr_X_X(array<u32, 2u>(u32(I), u32(J)));
-  let s = fn_s_S();
-  let s_str = fn_s_S_str_i();
-  let s_arr0 = fn_s_S_arr_X(array<u32, 1u>(u32(0i)));
-  let s_arr1 = fn_s_S_arr_X(array<u32, 1u>(u32(1i)));
-  let s_arrI = fn_s_S_arr_X(array<u32, 1u>(u32(I)));
-  let s_arr1_arr0 = fn_s_S_arr_arr_X_X(array<u32, 2u>(u32(1i), u32(0i)));
-  let s_arr2_arrI = fn_s_S_arr_arr_X_X(array<u32, 2u>(u32(2i), u32(I)));
-  let s_arrI_arr2 = fn_s_S_arr_arr_X_X(array<u32, 2u>(u32(I), u32(2i)));
-  let s_arrI_arrJ = fn_s_S_arr_arr_X_X(array<u32, 2u>(u32(I), u32(J)));
-  let w = fn_w_W();
-  let w_str = fn_w_W_str_i();
-  let w_arr0 = fn_w_W_arr_X(array<u32, 1u>(u32(0i)));
-  let w_arr1 = fn_w_W_arr_X(array<u32, 1u>(u32(1i)));
-  let w_arrI = fn_w_W_arr_X(array<u32, 1u>(u32(I)));
-  let w_arr1_arr0 = fn_w_W_arr_arr_X_X(array<u32, 2u>(u32(1i), u32(0i)));
-  let w_arr2_arrI = fn_w_W_arr_arr_X_X(array<u32, 2u>(u32(2i), u32(I)));
-  let w_arrI_arr2 = fn_w_W_arr_arr_X_X(array<u32, 2u>(u32(I), u32(2i)));
-  let w_arrI_arrJ = fn_w_W_arr_arr_X_X(array<u32, 2u>(u32(I), u32(J)));
+  let u = fn_u();
+  let u_str = fn_u_1();
+  let u_arr0 = fn_u_2(array<u32, 1u>(u32(0i)));
+  let u_arr1 = fn_u_2(array<u32, 1u>(u32(1i)));
+  let u_arrI = fn_u_2(array<u32, 1u>(u32(I)));
+  let u_arr1_arr0 = fn_u_3(array<u32, 2u>(u32(1i), u32(0i)));
+  let u_arr2_arrI = fn_u_3(array<u32, 2u>(u32(2i), u32(I)));
+  let u_arrI_arr2 = fn_u_3(array<u32, 2u>(u32(I), u32(2i)));
+  let u_arrI_arrJ = fn_u_3(array<u32, 2u>(u32(I), u32(J)));
+  let s = fn_s();
+  let s_str = fn_s_1();
+  let s_arr0 = fn_s_2(array<u32, 1u>(u32(0i)));
+  let s_arr1 = fn_s_2(array<u32, 1u>(u32(1i)));
+  let s_arrI = fn_s_2(array<u32, 1u>(u32(I)));
+  let s_arr1_arr0 = fn_s_3(array<u32, 2u>(u32(1i), u32(0i)));
+  let s_arr2_arrI = fn_s_3(array<u32, 2u>(u32(2i), u32(I)));
+  let s_arrI_arr2 = fn_s_3(array<u32, 2u>(u32(I), u32(2i)));
+  let s_arrI_arrJ = fn_s_3(array<u32, 2u>(u32(I), u32(J)));
+  let w = fn_w();
+  let w_str = fn_w_1();
+  let w_arr0 = fn_w_2(array<u32, 1u>(u32(0i)));
+  let w_arr1 = fn_w_2(array<u32, 1u>(u32(1i)));
+  let w_arrI = fn_w_2(array<u32, 1u>(u32(I)));
+  let w_arr1_arr0 = fn_w_3(array<u32, 2u>(u32(1i), u32(0i)));
+  let w_arr2_arrI = fn_w_3(array<u32, 2u>(u32(2i), u32(I)));
+  let w_arrI_arr2 = fn_w_3(array<u32, 2u>(u32(I), u32(2i)));
+  let w_arrI_arrJ = fn_w_3(array<u32, 2u>(u32(I), u32(J)));
 }
 )";
 
@@ -2418,8 +2357,6 @@
 
 TEST_F(IR_DirectVariableAccessWgslTest_Complex, Indexing) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage> S : array<array<array<array<i32, 9>, 9>, 9>, 50>;
 
 fn a(i : i32) -> i32 { return i; }
@@ -2442,13 +2379,13 @@
   return i;
 }
 
-fn b_S_X(p_indices : array<u32, 1u>) -> i32 {
+fn b(p_indices : array<u32, 1u>) -> i32 {
   let v = &(S[p_indices[0u]]);
   return (*(v))[a((*(v))[0i][1i][2i])][a((*(v))[a(3i)][4i][5i])][a((*(v))[6i][a(7i)][8i])];
 }
 
 fn c() {
-  let v = b_S_X(array<u32, 1u>(u32(42i)));
+  let v = b(array<u32, 1u>(u32(42i)));
 }
 )";
 
@@ -2459,8 +2396,6 @@
 
 TEST_F(IR_DirectVariableAccessWgslTest_Complex, IndexingInPtrCall) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage> S : array<array<array<array<i32, 9>, 9>, 9>, 50>;
 
 fn a(pre : i32, i : ptr<storage, i32>, post : i32) -> i32 {
@@ -2481,19 +2416,19 @@
     auto* expect = R"(
 @group(0) @binding(0) var<storage, read> S : array<array<array<array<i32, 9u>, 9u>, 9u>, 50u>;
 
-fn a_S_X_X_X_X(pre : i32, i_indices : array<u32, 4u>, post : i32) -> i32 {
+fn a(pre : i32, i_indices : array<u32, 4u>, post : i32) -> i32 {
   return S[i_indices[0u]][i_indices[1u]][i_indices[2u]][i_indices[3u]];
 }
 
-fn b_S_X(p_indices : array<u32, 1u>) -> i32 {
+fn b(p_indices : array<u32, 1u>) -> i32 {
   let v = p_indices[0u];
-  let v_1 = a_S_X_X_X_X(20i, array<u32, 4u>(v, u32(0i), u32(1i), u32(2i)), 30i);
-  let v_2 = a_S_X_X_X_X(40i, array<u32, 4u>(v, u32(3i), u32(4i), u32(5i)), 50i);
-  return a_S_X_X_X_X(10i, array<u32, 4u>(v, u32(v_1), u32(v_2), u32(a_S_X_X_X_X(60i, array<u32, 4u>(v, u32(6i), u32(7i), u32(8i)), 70i))), 80i);
+  let v_1 = a(20i, array<u32, 4u>(v, u32(0i), u32(1i), u32(2i)), 30i);
+  let v_2 = a(40i, array<u32, 4u>(v, u32(3i), u32(4i), u32(5i)), 50i);
+  return a(10i, array<u32, 4u>(v, u32(v_1), u32(v_2), u32(a(60i, array<u32, 4u>(v, u32(6i), u32(7i), u32(8i)), 70i))), 80i);
 }
 
 fn c() {
-  let v = b_S_X(array<u32, 1u>(u32(42i)));
+  let v = b(array<u32, 1u>(u32(42i)));
 }
 )";
 
@@ -2504,8 +2439,6 @@
 
 TEST_F(IR_DirectVariableAccessWgslTest_Complex, IndexingDualPointers) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage> S : array<array<array<i32, 9>, 9>, 50>;
 @group(0) @binding(0) var<uniform> U : array<array<array<vec4<i32>, 9>, 9>, 50>;
 
@@ -2531,13 +2464,13 @@
   return i;
 }
 
-fn b_S_X_U_X(s_indices : array<u32, 1u>, u_indices : array<u32, 1u>) -> i32 {
+fn b(s_indices : array<u32, 1u>, u_indices : array<u32, 1u>) -> i32 {
   let v = &(U[u_indices[0u]]);
   return S[s_indices[0u]][a((*(v))[0i][1i].x)][a((*(v))[a(3i)][4i].y)];
 }
 
 fn c() {
-  let v = b_S_X_U_X(array<u32, 1u>(u32(42i)), array<u32, 1u>(u32(24i)));
+  let v = b(array<u32, 1u>(u32(42i)), array<u32, 1u>(u32(24i)));
 }
 )";
 
diff --git a/src/tint/lang/core/ir/validator.cc b/src/tint/lang/core/ir/validator.cc
index 2aa16f8..65c952a 100644
--- a/src/tint/lang/core/ir/validator.cc
+++ b/src/tint/lang/core/ir/validator.cc
@@ -90,7 +90,8 @@
   public:
     /// Create a core validator
     /// @param mod the module to be validated
-    explicit Validator(const Module& mod);
+    /// @param capabilities the optional capabilities that are allowed
+    explicit Validator(const Module& mod, EnumSet<Capability> capabilities);
 
     /// Destructor
     ~Validator();
@@ -286,6 +287,7 @@
 
   private:
     const Module& mod_;
+    EnumSet<Capability> capabilities_;
     std::shared_ptr<Source::File> disassembly_file;
     diag::List diagnostics_;
     Disassembler dis_{mod_};
@@ -297,7 +299,8 @@
     void DisassembleIfNeeded();
 };
 
-Validator::Validator(const Module& mod) : mod_(mod) {}
+Validator::Validator(const Module& mod, EnumSet<Capability> capabilities)
+    : mod_(mod), capabilities_(capabilities) {}
 
 Validator::~Validator() = default;
 
@@ -651,9 +654,11 @@
             return;
         }
 
-        if (obj_ptr && el_ty->Is<core::type::Vector>()) {
-            err("cannot obtain address of vector element");
-            return;
+        if (!capabilities_.Contains(Capability::kAllowVectorElementPointer)) {
+            if (obj_ptr && el_ty->Is<core::type::Vector>()) {
+                err("cannot obtain address of vector element");
+                return;
+            }
         }
 
         if (auto* const_index = index->As<ir::Constant>()) {
@@ -1024,13 +1029,14 @@
 
 }  // namespace
 
-Result<SuccessType> Validate(const Module& mod) {
-    Validator v(mod);
+Result<SuccessType> Validate(const Module& mod, EnumSet<Capability> capabilities) {
+    Validator v(mod, capabilities);
     return v.Run();
 }
 
 Result<SuccessType> ValidateAndDumpIfNeeded([[maybe_unused]] const Module& ir,
-                                            [[maybe_unused]] const char* msg) {
+                                            [[maybe_unused]] const char* msg,
+                                            [[maybe_unused]] EnumSet<Capability> capabilities) {
 #if TINT_DUMP_IR_WHEN_VALIDATING
     std::cout << "=========================================================" << std::endl;
     std::cout << "== IR dump before " << msg << ":" << std::endl;
@@ -1039,7 +1045,7 @@
 #endif
 
 #ifndef NDEBUG
-    auto result = Validate(ir);
+    auto result = Validate(ir, capabilities);
     if (result != Success) {
         return result.Failure();
     }
diff --git a/src/tint/lang/core/ir/validator.h b/src/tint/lang/core/ir/validator.h
index d6b0843..ba854d8 100644
--- a/src/tint/lang/core/ir/validator.h
+++ b/src/tint/lang/core/ir/validator.h
@@ -30,6 +30,7 @@
 
 #include <string>
 
+#include "src/tint/utils/containers/enum_set.h"
 #include "src/tint/utils/result/result.h"
 
 // Forward declarations
@@ -39,16 +40,26 @@
 
 namespace tint::core::ir {
 
+/// Enumerator of optional IR capabilities.
+enum class Capability {
+    /// Allows access instructions to create pointers to vector elements.
+    kAllowVectorElementPointer,
+};
+
 /// Validates that a given IR module is correctly formed
 /// @param mod the module to validate
+/// @param capabilities the optional capabilities that are allowed
 /// @returns success or failure
-Result<SuccessType> Validate(const Module& mod);
+Result<SuccessType> Validate(const Module& mod, EnumSet<Capability> capabilities = {});
 
 /// Validates the module @p ir and dumps its contents if required by the build configuration.
 /// @param ir the module to transform
 /// @param msg the msg to accompany the output
+/// @param capabilities the optional capabilities that are allowed
 /// @returns success or failure
-Result<SuccessType> ValidateAndDumpIfNeeded(const Module& ir, const char* msg);
+Result<SuccessType> ValidateAndDumpIfNeeded(const Module& ir,
+                                            const char* msg,
+                                            EnumSet<Capability> capabilities = {});
 
 }  // namespace tint::core::ir
 
diff --git a/src/tint/lang/core/ir/validator_test.cc b/src/tint/lang/core/ir/validator_test.cc
index 3c2733a..0c2bef9 100644
--- a/src/tint/lang/core/ir/validator_test.cc
+++ b/src/tint/lang/core/ir/validator_test.cc
@@ -760,6 +760,20 @@
 )");
 }
 
+TEST_F(IR_ValidatorTest, Access_IndexVectorPtr_WithCapability) {
+    auto* f = b.Function("my_func", ty.void_());
+    auto* obj = b.FunctionParam(ty.ptr<private_, vec3<f32>>());
+    f->SetParams({obj});
+
+    b.Append(f->Block(), [&] {
+        b.Access(ty.ptr<private_, f32>(), obj, 1_u);
+        b.Return(f);
+    });
+
+    auto res = ir::Validate(mod, EnumSet<Capability>{Capability::kAllowVectorElementPointer});
+    ASSERT_EQ(res, Success);
+}
+
 TEST_F(IR_ValidatorTest, Access_IndexVectorPtr_ViaMatrixPtr) {
     auto* f = b.Function("my_func", ty.void_());
     auto* obj = b.FunctionParam(ty.ptr<private_, mat3x2<f32>>());
@@ -791,6 +805,20 @@
 )");
 }
 
+TEST_F(IR_ValidatorTest, Access_IndexVectorPtr_ViaMatrixPtr_WithCapability) {
+    auto* f = b.Function("my_func", ty.void_());
+    auto* obj = b.FunctionParam(ty.ptr<private_, mat3x2<f32>>());
+    f->SetParams({obj});
+
+    b.Append(f->Block(), [&] {
+        b.Access(ty.ptr<private_, f32>(), obj, 1_u, 1_u);
+        b.Return(f);
+    });
+
+    auto res = ir::Validate(mod, EnumSet<Capability>{Capability::kAllowVectorElementPointer});
+    ASSERT_EQ(res, Success);
+}
+
 TEST_F(IR_ValidatorTest, Access_Incorrect_Ptr_AddressSpace) {
     auto* f = b.Function("my_func", ty.void_());
     auto* obj = b.FunctionParam(ty.ptr<storage, array<f32, 2>, read>());
diff --git a/src/tint/lang/glsl/writer/ast_printer/ast_printer.cc b/src/tint/lang/glsl/writer/ast_printer/ast_printer.cc
index 9a7c6ea..2026bd2 100644
--- a/src/tint/lang/glsl/writer/ast_printer/ast_printer.cc
+++ b/src/tint/lang/glsl/writer/ast_printer/ast_printer.cc
@@ -266,7 +266,6 @@
             "GLSL", builder_.AST(), diagnostics_,
             Vector{
                 wgsl::Extension::kChromiumDisableUniformityAnalysis,
-                wgsl::Extension::kChromiumExperimentalFullPtrParameters,
                 wgsl::Extension::kChromiumInternalDualSourceBlending,
                 wgsl::Extension::kChromiumExperimentalPushConstant,
                 wgsl::Extension::kF16,
diff --git a/src/tint/lang/hlsl/writer/ast_printer/ast_printer.cc b/src/tint/lang/hlsl/writer/ast_printer/ast_printer.cc
index 3d25696..d6eec1a 100644
--- a/src/tint/lang/hlsl/writer/ast_printer/ast_printer.cc
+++ b/src/tint/lang/hlsl/writer/ast_printer/ast_printer.cc
@@ -385,7 +385,6 @@
             "HLSL", builder_.AST(), diagnostics_,
             Vector{
                 wgsl::Extension::kChromiumDisableUniformityAnalysis,
-                wgsl::Extension::kChromiumExperimentalFullPtrParameters,
                 wgsl::Extension::kChromiumExperimentalPushConstant,
                 wgsl::Extension::kChromiumExperimentalSubgroups,
                 wgsl::Extension::kF16,
@@ -416,6 +415,8 @@
         }
         last_kind = kind;
 
+        global_insertion_point_ = current_buffer_->lines.size();
+
         bool ok = Switch(
             decl,
             [&](const ast::Variable* global) {  //
@@ -3847,14 +3848,20 @@
                 }
             } else {
                 // HLSL requires structure initializers to be assigned directly to a variable.
+                // For these constants use 'static const' at global-scope. 'const' at global scope
+                // creates a variable who's initializer is ignored, and the value is expected to be
+                // provided in a cbuffer. 'static const' is a true value-embedded-in-the-shader-code
+                // constant. We also emit these for function-local constant expressions for
+                // consistency and to ensure that these are not computed at execution time.
                 auto name = UniqueIdentifier("c");
                 {
-                    auto decl = Line();
-                    decl << "const " << StructName(s) << " " << name << " = ";
+                    StringStream decl;
+                    decl << "static const " << StructName(s) << " " << name << " = ";
                     if (!emit_member_values(decl)) {
                         return false;
                     }
                     decl << ";";
+                    current_buffer_->Insert(decl.str(), global_insertion_point_++, 0);
                 }
                 out << name;
             }
diff --git a/src/tint/lang/hlsl/writer/ast_printer/ast_printer.h b/src/tint/lang/hlsl/writer/ast_printer/ast_printer.h
index 633180c..3d14eac 100644
--- a/src/tint/lang/hlsl/writer/ast_printer/ast_printer.h
+++ b/src/tint/lang/hlsl/writer/ast_printer/ast_printer.h
@@ -619,6 +619,9 @@
     std::unordered_map<const core::type::Type*, std::string> value_or_one_if_zero_;
     std::unordered_set<const core::type::Struct*> emitted_structs_;
     std::unordered_map<const core::type::Type*, bool> is_struct_or_array_of_matrix_;
+
+    // The line index in current_buffer_ of the current global declaration / function.
+    size_t global_insertion_point_ = 0;
 };
 
 }  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_printer/builtin_test.cc b/src/tint/lang/hlsl/writer/ast_printer/builtin_test.cc
index d9d6d89..803be81 100644
--- a/src/tint/lang/hlsl/writer/ast_printer/builtin_test.cc
+++ b/src/tint/lang/hlsl/writer/ast_printer/builtin_test.cc
@@ -588,10 +588,10 @@
   float3 fract;
   float3 whole;
 };
+static const modf_result_vec3_f32 c = {(0.5f).xxx, float3(4.0f, 5.0f, 6.0f)};
 [numthreads(1, 1, 1)]
 void test_function() {
   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;
 }
@@ -802,10 +802,10 @@
   float3 fract;
   int3 exp;
 };
+static const frexp_result_vec3_f32 c = {float3(0.5625f, 0.6875f, 0.8125f), (3).xxx};
 [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;
 }
diff --git a/src/tint/lang/msl/writer/ast_printer/ast_printer.cc b/src/tint/lang/msl/writer/ast_printer/ast_printer.cc
index 3fb75a8..63dcf81 100644
--- a/src/tint/lang/msl/writer/ast_printer/ast_printer.cc
+++ b/src/tint/lang/msl/writer/ast_printer/ast_printer.cc
@@ -277,7 +277,6 @@
             "MSL", builder_.AST(), diagnostics_,
             Vector{
                 wgsl::Extension::kChromiumDisableUniformityAnalysis,
-                wgsl::Extension::kChromiumExperimentalFullPtrParameters,
                 wgsl::Extension::kChromiumExperimentalPixelLocal,
                 wgsl::Extension::kChromiumExperimentalSubgroups,
                 wgsl::Extension::kChromiumExperimentalFramebufferFetch,
diff --git a/src/tint/lang/msl/writer/ast_raise/module_scope_var_to_entry_point_param.cc b/src/tint/lang/msl/writer/ast_raise/module_scope_var_to_entry_point_param.cc
index 38cad89..8335eac 100644
--- a/src/tint/lang/msl/writer/ast_raise/module_scope_var_to_entry_point_param.cc
+++ b/src/tint/lang/msl/writer/ast_raise/module_scope_var_to_entry_point_param.cc
@@ -375,9 +375,6 @@
         if (!private_struct_members.IsEmpty()) {
             // Create the private variable struct.
             ctx.dst->Structure(PrivateStructName(), std::move(private_struct_members));
-            // Passing a pointer to a private variable will now involve passing a pointer to the
-            // member of a structure, so enable the extension that allows this.
-            ctx.dst->Enable(wgsl::Extension::kChromiumExperimentalFullPtrParameters);
         }
 
         // Build a list of `&ident` expressions. We'll use this later to avoid generating
diff --git a/src/tint/lang/msl/writer/ast_raise/module_scope_var_to_entry_point_param_test.cc b/src/tint/lang/msl/writer/ast_raise/module_scope_var_to_entry_point_param_test.cc
index f7630f5..3bbb4fe 100644
--- a/src/tint/lang/msl/writer/ast_raise/module_scope_var_to_entry_point_param_test.cc
+++ b/src/tint/lang/msl/writer/ast_raise/module_scope_var_to_entry_point_param_test.cc
@@ -62,8 +62,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct tint_private_vars_struct {
   p : f32,
 }
@@ -93,8 +91,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct tint_private_vars_struct {
   p : f32,
 }
@@ -143,8 +139,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct tint_private_vars_struct {
   p : f32,
 }
@@ -214,8 +208,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct tint_private_vars_struct {
   p : f32,
 }
@@ -266,8 +258,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct tint_private_vars_struct {
   a : f32,
   b : f32,
@@ -299,8 +289,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct tint_private_vars_struct {
   a : f32,
   b : f32,
@@ -335,8 +323,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct tint_private_vars_struct {
   p : f32,
 }
@@ -372,8 +358,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct tint_private_vars_struct {
   p : f32,
 }
@@ -1232,8 +1216,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct tint_private_vars_struct {
   p : f32,
   p_with_init : f32,
@@ -1278,8 +1260,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct S {
   a : f32,
   b : f32,
@@ -1339,8 +1319,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct S {
   a : f32,
   b : f32,
diff --git a/src/tint/lang/msl/writer/ast_raise/packed_vec3_test.cc b/src/tint/lang/msl/writer/ast_raise/packed_vec3_test.cc
index 325f528..220dabb 100644
--- a/src/tint/lang/msl/writer/ast_raise/packed_vec3_test.cc
+++ b/src/tint/lang/msl/writer/ast_raise/packed_vec3_test.cc
@@ -4652,8 +4652,6 @@
     // Test that we can pass a pointer to the vec3 member of the modf return struct to a function
     // parameter to which we also pass a pointer to a vec3 member on a host-shareable struct.
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct S {
   v : vec3<f32>
 }
@@ -4676,7 +4674,6 @@
 
     auto* expect = R"(
 enable chromium_internal_relaxed_uniform_layout;
-enable chromium_experimental_full_ptr_parameters;
 
 struct S_tint_packed_vec3 {
   @align(16)
@@ -5305,8 +5302,6 @@
 
 TEST_F(PackedVec3Test, VectorPointerParameters) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct S {
   v : vec3<f32>,
   m : mat3x3<f32>,
@@ -5350,7 +5345,6 @@
 
     auto* expect = R"(
 enable chromium_internal_relaxed_uniform_layout;
-enable chromium_experimental_full_ptr_parameters;
 
 struct tint_packed_vec3_f32_array_element {
   @align(16)
@@ -5421,8 +5415,6 @@
 
 TEST_F(PackedVec3Test, MatrixPointerParameters) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct S {
   m : mat3x3<f32>,
   arr_m : array<mat3x3<f32>, 4>,
@@ -5454,7 +5446,6 @@
 
     auto* expect = R"(
 enable chromium_internal_relaxed_uniform_layout;
-enable chromium_experimental_full_ptr_parameters;
 
 struct tint_packed_vec3_f32_array_element {
   @align(16)
@@ -5523,8 +5514,6 @@
 
 TEST_F(PackedVec3Test, ArrayOfVectorPointerParameters) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct S {
   arr_v : array<vec3<f32>, 4>,
 }
@@ -5550,7 +5539,6 @@
 
     auto* expect = R"(
 enable chromium_internal_relaxed_uniform_layout;
-enable chromium_experimental_full_ptr_parameters;
 
 struct tint_packed_vec3_f32_array_element {
   @align(16)
@@ -5610,8 +5598,6 @@
 
 TEST_F(PackedVec3Test, ArrayOfMatrixPointerParameters) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct S {
   arr_m : array<mat3x3<f32>, 4>,
 }
@@ -5637,7 +5623,6 @@
 
     auto* expect = R"(
 enable chromium_internal_relaxed_uniform_layout;
-enable chromium_experimental_full_ptr_parameters;
 
 struct tint_packed_vec3_f32_array_element {
   @align(16)
@@ -5713,8 +5698,6 @@
 
 TEST_F(PackedVec3Test, StructPointerParameters) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct S {
   v : vec3<f32>,
   m : mat3x3<f32>,
@@ -5740,7 +5723,6 @@
 
     auto* expect = R"(
 enable chromium_internal_relaxed_uniform_layout;
-enable chromium_experimental_full_ptr_parameters;
 
 struct tint_packed_vec3_f32_array_element {
   @align(16)
@@ -6016,8 +5998,6 @@
     // Test that we can pass a pointers to a members of both shared and non-shared structs to the
     // same function.
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct S {
   v : vec3<f32>,
   arr : array<vec3<f32>, 4>,
@@ -6047,7 +6027,6 @@
 
     auto* expect = R"(
 enable chromium_internal_relaxed_uniform_layout;
-enable chromium_experimental_full_ptr_parameters;
 
 struct tint_packed_vec3_f32_array_element {
   @align(16)
@@ -6683,8 +6662,6 @@
 
 TEST_F(PackedVec3Test, MixedAddressSpace_PointerParameters) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct S {
   v : vec3<f32>,
   m : mat3x3<f32>,
@@ -6715,7 +6692,6 @@
 
     auto* expect = R"(
 enable chromium_internal_relaxed_uniform_layout;
-enable chromium_experimental_full_ptr_parameters;
 
 struct tint_packed_vec3_f32_array_element {
   @align(16)
@@ -7649,8 +7625,6 @@
     // matrix into an array of vec3s in uniform storage.
     auto* src = R"(
 enable f16;
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<uniform> m : mat3x3<f16>;
 
 fn g(p : ptr<uniform, mat3x3<f16>>) -> vec3<f16> {
@@ -7666,7 +7640,6 @@
         R"(
 enable chromium_internal_relaxed_uniform_layout;
 enable f16;
-enable chromium_experimental_full_ptr_parameters;
 
 struct tint_packed_vec3_f16_array_element {
   @align(8)
diff --git a/src/tint/lang/spirv/reader/BUILD.bazel b/src/tint/lang/spirv/reader/BUILD.bazel
index f7662cf..3f50893 100644
--- a/src/tint/lang/spirv/reader/BUILD.bazel
+++ b/src/tint/lang/spirv/reader/BUILD.bazel
@@ -50,7 +50,7 @@
     "//src/tint/lang/core/constant",
     "//src/tint/lang/core/ir",
     "//src/tint/lang/core/type",
-    "//src/tint/lang/spirv/reader/common",
+    "//src/tint/lang/spirv/reader/lower",
     "//src/tint/lang/wgsl",
     "//src/tint/lang/wgsl/ast",
     "//src/tint/lang/wgsl/common",
@@ -73,6 +73,7 @@
   ] + select({
     ":tint_build_spv_reader": [
       "//src/tint/lang/spirv/reader/ast_parser",
+      "//src/tint/lang/spirv/reader/common",
       "//src/tint/lang/spirv/reader/parser",
     ],
     "//conditions:default": [],
@@ -80,9 +81,70 @@
   copts = COPTS,
   visibility = ["//visibility:public"],
 )
+cc_library(
+  name = "test",
+  alwayslink = True,
+  srcs = [
+    "reader_test.cc",
+  ],
+  deps = [
+    "//src/tint/api/common",
+    "//src/tint/lang/core",
+    "//src/tint/lang/core/constant",
+    "//src/tint/lang/core/ir",
+    "//src/tint/lang/core/type",
+    "//src/tint/lang/wgsl",
+    "//src/tint/lang/wgsl/ast",
+    "//src/tint/lang/wgsl/common",
+    "//src/tint/lang/wgsl/features",
+    "//src/tint/lang/wgsl/program",
+    "//src/tint/lang/wgsl/sem",
+    "//src/tint/utils/containers",
+    "//src/tint/utils/diagnostic",
+    "//src/tint/utils/ice",
+    "//src/tint/utils/id",
+    "//src/tint/utils/macros",
+    "//src/tint/utils/math",
+    "//src/tint/utils/memory",
+    "//src/tint/utils/reflection",
+    "//src/tint/utils/result",
+    "//src/tint/utils/rtti",
+    "//src/tint/utils/symbol",
+    "//src/tint/utils/text",
+    "//src/tint/utils/traits",
+    "@gtest",
+  ] + select({
+    ":tint_build_spv_reader": [
+      "//src/tint/lang/spirv/reader",
+      "//src/tint/lang/spirv/reader/common",
+      "//src/tint/lang/spirv/reader/common:test",
+    ],
+    "//conditions:default": [],
+  }) + select({
+    ":tint_build_spv_reader_or_tint_build_spv_writer": [
+      "@spirv_tools",
+    ],
+    "//conditions:default": [],
+  }),
+  copts = COPTS,
+  visibility = ["//visibility:public"],
+)
 
 alias(
   name = "tint_build_spv_reader",
   actual = "//src/tint:tint_build_spv_reader_true",
 )
 
+alias(
+  name = "tint_build_spv_writer",
+  actual = "//src/tint:tint_build_spv_writer_true",
+)
+
+selects.config_setting_group(
+    name = "tint_build_spv_reader_or_tint_build_spv_writer",
+    match_any = [
+        "tint_build_spv_reader",
+        "tint_build_spv_writer",
+    ],
+)
+
diff --git a/src/tint/lang/spirv/reader/BUILD.cmake b/src/tint/lang/spirv/reader/BUILD.cmake
index 6e0d5ac..6b1a73c 100644
--- a/src/tint/lang/spirv/reader/BUILD.cmake
+++ b/src/tint/lang/spirv/reader/BUILD.cmake
@@ -37,6 +37,7 @@
 include(lang/spirv/reader/ast_lower/BUILD.cmake)
 include(lang/spirv/reader/ast_parser/BUILD.cmake)
 include(lang/spirv/reader/common/BUILD.cmake)
+include(lang/spirv/reader/lower/BUILD.cmake)
 include(lang/spirv/reader/parser/BUILD.cmake)
 
 if(TINT_BUILD_SPV_READER)
@@ -56,7 +57,7 @@
   tint_lang_core_constant
   tint_lang_core_ir
   tint_lang_core_type
-  tint_lang_spirv_reader_common
+  tint_lang_spirv_reader_lower
   tint_lang_wgsl
   tint_lang_wgsl_ast
   tint_lang_wgsl_common
@@ -81,8 +82,65 @@
 if(TINT_BUILD_SPV_READER)
   tint_target_add_dependencies(tint_lang_spirv_reader lib
     tint_lang_spirv_reader_ast_parser
+    tint_lang_spirv_reader_common
     tint_lang_spirv_reader_parser
   )
 endif(TINT_BUILD_SPV_READER)
 
+endif(TINT_BUILD_SPV_READER)
+if(TINT_BUILD_SPV_READER)
+################################################################################
+# Target:    tint_lang_spirv_reader_test
+# Kind:      test
+# Condition: TINT_BUILD_SPV_READER
+################################################################################
+tint_add_target(tint_lang_spirv_reader_test test
+  lang/spirv/reader/reader_test.cc
+)
+
+tint_target_add_dependencies(tint_lang_spirv_reader_test test
+  tint_api_common
+  tint_lang_core
+  tint_lang_core_constant
+  tint_lang_core_ir
+  tint_lang_core_type
+  tint_lang_wgsl
+  tint_lang_wgsl_ast
+  tint_lang_wgsl_common
+  tint_lang_wgsl_features
+  tint_lang_wgsl_program
+  tint_lang_wgsl_sem
+  tint_utils_containers
+  tint_utils_diagnostic
+  tint_utils_ice
+  tint_utils_id
+  tint_utils_macros
+  tint_utils_math
+  tint_utils_memory
+  tint_utils_reflection
+  tint_utils_result
+  tint_utils_rtti
+  tint_utils_symbol
+  tint_utils_text
+  tint_utils_traits
+)
+
+tint_target_add_external_dependencies(tint_lang_spirv_reader_test test
+  "gtest"
+)
+
+if(TINT_BUILD_SPV_READER)
+  tint_target_add_dependencies(tint_lang_spirv_reader_test test
+    tint_lang_spirv_reader
+    tint_lang_spirv_reader_common
+    tint_lang_spirv_reader_common_test
+  )
+endif(TINT_BUILD_SPV_READER)
+
+if(TINT_BUILD_SPV_READER OR TINT_BUILD_SPV_WRITER)
+  tint_target_add_external_dependencies(tint_lang_spirv_reader_test test
+    "spirv-tools"
+  )
+endif(TINT_BUILD_SPV_READER OR TINT_BUILD_SPV_WRITER)
+
 endif(TINT_BUILD_SPV_READER)
\ No newline at end of file
diff --git a/src/tint/lang/spirv/reader/BUILD.gn b/src/tint/lang/spirv/reader/BUILD.gn
index 82b4314..09788fc 100644
--- a/src/tint/lang/spirv/reader/BUILD.gn
+++ b/src/tint/lang/spirv/reader/BUILD.gn
@@ -37,6 +37,10 @@
 import("../../../../../scripts/tint_overrides_with_defaults.gni")
 
 import("${tint_src_dir}/tint.gni")
+
+if (tint_build_unittests || tint_build_benchmarks) {
+  import("//testing/test.gni")
+}
 if (tint_build_spv_reader) {
   libtint_source_set("reader") {
     sources = [
@@ -49,7 +53,7 @@
       "${tint_src_dir}/lang/core/constant",
       "${tint_src_dir}/lang/core/ir",
       "${tint_src_dir}/lang/core/type",
-      "${tint_src_dir}/lang/spirv/reader/common",
+      "${tint_src_dir}/lang/spirv/reader/lower",
       "${tint_src_dir}/lang/wgsl",
       "${tint_src_dir}/lang/wgsl/ast",
       "${tint_src_dir}/lang/wgsl/common",
@@ -74,8 +78,58 @@
     if (tint_build_spv_reader) {
       deps += [
         "${tint_src_dir}/lang/spirv/reader/ast_parser",
+        "${tint_src_dir}/lang/spirv/reader/common",
         "${tint_src_dir}/lang/spirv/reader/parser",
       ]
     }
   }
 }
+if (tint_build_unittests) {
+  if (tint_build_spv_reader) {
+    tint_unittests_source_set("unittests") {
+      sources = [ "reader_test.cc" ]
+      deps = [
+        "${tint_src_dir}:gmock_and_gtest",
+        "${tint_src_dir}/api/common",
+        "${tint_src_dir}/lang/core",
+        "${tint_src_dir}/lang/core/constant",
+        "${tint_src_dir}/lang/core/ir",
+        "${tint_src_dir}/lang/core/type",
+        "${tint_src_dir}/lang/wgsl",
+        "${tint_src_dir}/lang/wgsl/ast",
+        "${tint_src_dir}/lang/wgsl/common",
+        "${tint_src_dir}/lang/wgsl/features",
+        "${tint_src_dir}/lang/wgsl/program",
+        "${tint_src_dir}/lang/wgsl/sem",
+        "${tint_src_dir}/utils/containers",
+        "${tint_src_dir}/utils/diagnostic",
+        "${tint_src_dir}/utils/ice",
+        "${tint_src_dir}/utils/id",
+        "${tint_src_dir}/utils/macros",
+        "${tint_src_dir}/utils/math",
+        "${tint_src_dir}/utils/memory",
+        "${tint_src_dir}/utils/reflection",
+        "${tint_src_dir}/utils/result",
+        "${tint_src_dir}/utils/rtti",
+        "${tint_src_dir}/utils/symbol",
+        "${tint_src_dir}/utils/text",
+        "${tint_src_dir}/utils/traits",
+      ]
+
+      if (tint_build_spv_reader) {
+        deps += [
+          "${tint_src_dir}/lang/spirv/reader",
+          "${tint_src_dir}/lang/spirv/reader/common",
+          "${tint_src_dir}/lang/spirv/reader/common:unittests",
+        ]
+      }
+
+      if (tint_build_spv_reader || tint_build_spv_writer) {
+        deps += [
+          "${tint_spirv_tools_dir}:spvtools_headers",
+          "${tint_spirv_tools_dir}:spvtools_val",
+        ]
+      }
+    }
+  }
+}
diff --git a/src/tint/lang/spirv/reader/ast_parser/BUILD.bazel b/src/tint/lang/spirv/reader/ast_parser/BUILD.bazel
index 421ec97..c656285 100644
--- a/src/tint/lang/spirv/reader/ast_parser/BUILD.bazel
+++ b/src/tint/lang/spirv/reader/ast_parser/BUILD.bazel
@@ -67,7 +67,6 @@
     "//src/tint/lang/core",
     "//src/tint/lang/core/constant",
     "//src/tint/lang/core/type",
-    "//src/tint/lang/spirv/reader/common",
     "//src/tint/lang/wgsl",
     "//src/tint/lang/wgsl/ast",
     "//src/tint/lang/wgsl/ast/transform",
@@ -92,6 +91,7 @@
   ] + select({
     ":tint_build_spv_reader": [
       "//src/tint/lang/spirv/reader/ast_lower",
+      "//src/tint/lang/spirv/reader/common",
     ],
     "//conditions:default": [],
   }) + select({
@@ -148,7 +148,6 @@
     "//src/tint/lang/core",
     "//src/tint/lang/core/constant",
     "//src/tint/lang/core/type",
-    "//src/tint/lang/spirv/reader/common",
     "//src/tint/lang/wgsl",
     "//src/tint/lang/wgsl/ast",
     "//src/tint/lang/wgsl/common",
@@ -173,6 +172,7 @@
   ] + select({
     ":tint_build_spv_reader": [
       "//src/tint/lang/spirv/reader/ast_parser",
+      "//src/tint/lang/spirv/reader/common",
     ],
     "//conditions:default": [],
   }) + select({
diff --git a/src/tint/lang/spirv/reader/ast_parser/BUILD.cmake b/src/tint/lang/spirv/reader/ast_parser/BUILD.cmake
index cf9191f..e4ab32d 100644
--- a/src/tint/lang/spirv/reader/ast_parser/BUILD.cmake
+++ b/src/tint/lang/spirv/reader/ast_parser/BUILD.cmake
@@ -68,7 +68,6 @@
   tint_lang_core
   tint_lang_core_constant
   tint_lang_core_type
-  tint_lang_spirv_reader_common
   tint_lang_wgsl
   tint_lang_wgsl_ast
   tint_lang_wgsl_ast_transform
@@ -95,6 +94,7 @@
 if(TINT_BUILD_SPV_READER)
   tint_target_add_dependencies(tint_lang_spirv_reader_ast_parser lib
     tint_lang_spirv_reader_ast_lower
+    tint_lang_spirv_reader_common
   )
 endif(TINT_BUILD_SPV_READER)
 
@@ -154,7 +154,6 @@
   tint_lang_core
   tint_lang_core_constant
   tint_lang_core_type
-  tint_lang_spirv_reader_common
   tint_lang_wgsl
   tint_lang_wgsl_ast
   tint_lang_wgsl_common
@@ -184,6 +183,7 @@
 if(TINT_BUILD_SPV_READER)
   tint_target_add_dependencies(tint_lang_spirv_reader_ast_parser_test test
     tint_lang_spirv_reader_ast_parser
+    tint_lang_spirv_reader_common
   )
 endif(TINT_BUILD_SPV_READER)
 
diff --git a/src/tint/lang/spirv/reader/ast_parser/BUILD.gn b/src/tint/lang/spirv/reader/ast_parser/BUILD.gn
index f82d2fb..5c8e0c1 100644
--- a/src/tint/lang/spirv/reader/ast_parser/BUILD.gn
+++ b/src/tint/lang/spirv/reader/ast_parser/BUILD.gn
@@ -70,7 +70,6 @@
       "${tint_src_dir}/lang/core",
       "${tint_src_dir}/lang/core/constant",
       "${tint_src_dir}/lang/core/type",
-      "${tint_src_dir}/lang/spirv/reader/common",
       "${tint_src_dir}/lang/wgsl",
       "${tint_src_dir}/lang/wgsl/ast",
       "${tint_src_dir}/lang/wgsl/ast/transform",
@@ -95,7 +94,10 @@
     ]
 
     if (tint_build_spv_reader) {
-      deps += [ "${tint_src_dir}/lang/spirv/reader/ast_lower" ]
+      deps += [
+        "${tint_src_dir}/lang/spirv/reader/ast_lower",
+        "${tint_src_dir}/lang/spirv/reader/common",
+      ]
     }
 
     if (tint_build_spv_reader || tint_build_spv_writer) {
@@ -154,7 +156,6 @@
         "${tint_src_dir}/lang/core",
         "${tint_src_dir}/lang/core/constant",
         "${tint_src_dir}/lang/core/type",
-        "${tint_src_dir}/lang/spirv/reader/common",
         "${tint_src_dir}/lang/wgsl",
         "${tint_src_dir}/lang/wgsl/ast",
         "${tint_src_dir}/lang/wgsl/common",
@@ -178,7 +179,10 @@
       ]
 
       if (tint_build_spv_reader) {
-        deps += [ "${tint_src_dir}/lang/spirv/reader/ast_parser" ]
+        deps += [
+          "${tint_src_dir}/lang/spirv/reader/ast_parser",
+          "${tint_src_dir}/lang/spirv/reader/common",
+        ]
       }
 
       if (tint_build_spv_reader || tint_build_spv_writer) {
diff --git a/src/tint/lang/spirv/reader/common/BUILD.bazel b/src/tint/lang/spirv/reader/common/BUILD.bazel
index b74c742..438bddd 100644
--- a/src/tint/lang/spirv/reader/common/BUILD.bazel
+++ b/src/tint/lang/spirv/reader/common/BUILD.bazel
@@ -60,4 +60,48 @@
   copts = COPTS,
   visibility = ["//visibility:public"],
 )
+cc_library(
+  name = "test",
+  alwayslink = True,
+  srcs = [
+    "helper_test.h",
+  ],
+  deps = [
+    "//src/tint/utils/containers",
+    "//src/tint/utils/diagnostic",
+    "//src/tint/utils/ice",
+    "//src/tint/utils/macros",
+    "//src/tint/utils/math",
+    "//src/tint/utils/memory",
+    "//src/tint/utils/result",
+    "//src/tint/utils/rtti",
+    "//src/tint/utils/text",
+    "//src/tint/utils/traits",
+  ] + select({
+    ":tint_build_spv_reader_or_tint_build_spv_writer": [
+      "@spirv_tools",
+    ],
+    "//conditions:default": [],
+  }),
+  copts = COPTS,
+  visibility = ["//visibility:public"],
+)
+
+alias(
+  name = "tint_build_spv_reader",
+  actual = "//src/tint:tint_build_spv_reader_true",
+)
+
+alias(
+  name = "tint_build_spv_writer",
+  actual = "//src/tint:tint_build_spv_writer_true",
+)
+
+selects.config_setting_group(
+    name = "tint_build_spv_reader_or_tint_build_spv_writer",
+    match_any = [
+        "tint_build_spv_reader",
+        "tint_build_spv_writer",
+    ],
+)
 
diff --git a/src/tint/lang/spirv/reader/common/BUILD.cfg b/src/tint/lang/spirv/reader/common/BUILD.cfg
new file mode 100644
index 0000000..a460fd5
--- /dev/null
+++ b/src/tint/lang/spirv/reader/common/BUILD.cfg
@@ -0,0 +1,3 @@
+{
+    "condition": "tint_build_spv_reader"
+}
diff --git a/src/tint/lang/spirv/reader/common/BUILD.cmake b/src/tint/lang/spirv/reader/common/BUILD.cmake
index 23c0ff1..f0206ba 100644
--- a/src/tint/lang/spirv/reader/common/BUILD.cmake
+++ b/src/tint/lang/spirv/reader/common/BUILD.cmake
@@ -34,9 +34,11 @@
 #                       Do not modify this file directly
 ################################################################################
 
+if(TINT_BUILD_SPV_READER)
 ################################################################################
 # Target:    tint_lang_spirv_reader_common
 # Kind:      lib
+# Condition: TINT_BUILD_SPV_READER
 ################################################################################
 tint_add_target(tint_lang_spirv_reader_common lib
   lang/spirv/reader/common/common.cc
@@ -56,3 +58,35 @@
   tint_utils_rtti
   tint_utils_traits
 )
+
+endif(TINT_BUILD_SPV_READER)
+if(TINT_BUILD_SPV_READER)
+################################################################################
+# Target:    tint_lang_spirv_reader_common_test
+# Kind:      test
+# Condition: TINT_BUILD_SPV_READER
+################################################################################
+tint_add_target(tint_lang_spirv_reader_common_test test
+  lang/spirv/reader/common/helper_test.h
+)
+
+tint_target_add_dependencies(tint_lang_spirv_reader_common_test test
+  tint_utils_containers
+  tint_utils_diagnostic
+  tint_utils_ice
+  tint_utils_macros
+  tint_utils_math
+  tint_utils_memory
+  tint_utils_result
+  tint_utils_rtti
+  tint_utils_text
+  tint_utils_traits
+)
+
+if(TINT_BUILD_SPV_READER OR TINT_BUILD_SPV_WRITER)
+  tint_target_add_external_dependencies(tint_lang_spirv_reader_common_test test
+    "spirv-tools"
+  )
+endif(TINT_BUILD_SPV_READER OR TINT_BUILD_SPV_WRITER)
+
+endif(TINT_BUILD_SPV_READER)
\ No newline at end of file
diff --git a/src/tint/lang/spirv/reader/common/BUILD.gn b/src/tint/lang/spirv/reader/common/BUILD.gn
index 9e75c83..8383ffe 100644
--- a/src/tint/lang/spirv/reader/common/BUILD.gn
+++ b/src/tint/lang/spirv/reader/common/BUILD.gn
@@ -38,22 +38,53 @@
 
 import("${tint_src_dir}/tint.gni")
 
-libtint_source_set("common") {
-  sources = [
-    "common.cc",
-    "options.h",
-  ]
-  deps = [
-    "${tint_src_dir}/lang/wgsl",
-    "${tint_src_dir}/lang/wgsl/common",
-    "${tint_src_dir}/lang/wgsl/features",
-    "${tint_src_dir}/utils/containers",
-    "${tint_src_dir}/utils/ice",
-    "${tint_src_dir}/utils/macros",
-    "${tint_src_dir}/utils/math",
-    "${tint_src_dir}/utils/memory",
-    "${tint_src_dir}/utils/reflection",
-    "${tint_src_dir}/utils/rtti",
-    "${tint_src_dir}/utils/traits",
-  ]
+if (tint_build_unittests || tint_build_benchmarks) {
+  import("//testing/test.gni")
+}
+if (tint_build_spv_reader) {
+  libtint_source_set("common") {
+    sources = [
+      "common.cc",
+      "options.h",
+    ]
+    deps = [
+      "${tint_src_dir}/lang/wgsl",
+      "${tint_src_dir}/lang/wgsl/common",
+      "${tint_src_dir}/lang/wgsl/features",
+      "${tint_src_dir}/utils/containers",
+      "${tint_src_dir}/utils/ice",
+      "${tint_src_dir}/utils/macros",
+      "${tint_src_dir}/utils/math",
+      "${tint_src_dir}/utils/memory",
+      "${tint_src_dir}/utils/reflection",
+      "${tint_src_dir}/utils/rtti",
+      "${tint_src_dir}/utils/traits",
+    ]
+  }
+}
+if (tint_build_unittests) {
+  if (tint_build_spv_reader) {
+    tint_unittests_source_set("unittests") {
+      sources = [ "helper_test.h" ]
+      deps = [
+        "${tint_src_dir}/utils/containers",
+        "${tint_src_dir}/utils/diagnostic",
+        "${tint_src_dir}/utils/ice",
+        "${tint_src_dir}/utils/macros",
+        "${tint_src_dir}/utils/math",
+        "${tint_src_dir}/utils/memory",
+        "${tint_src_dir}/utils/result",
+        "${tint_src_dir}/utils/rtti",
+        "${tint_src_dir}/utils/text",
+        "${tint_src_dir}/utils/traits",
+      ]
+
+      if (tint_build_spv_reader || tint_build_spv_writer) {
+        deps += [
+          "${tint_spirv_tools_dir}:spvtools_headers",
+          "${tint_spirv_tools_dir}:spvtools_val",
+        ]
+      }
+    }
+  }
 }
diff --git a/src/tint/lang/spirv/reader/common/helper_test.h b/src/tint/lang/spirv/reader/common/helper_test.h
new file mode 100644
index 0000000..6a0ae0e
--- /dev/null
+++ b/src/tint/lang/spirv/reader/common/helper_test.h
@@ -0,0 +1,58 @@
+// Copyright 2024 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef SRC_TINT_LANG_SPIRV_READER_COMMON_HELPER_TEST_H_
+#define SRC_TINT_LANG_SPIRV_READER_COMMON_HELPER_TEST_H_
+
+#include <string>
+#include <vector>
+
+#include "spirv-tools/libspirv.hpp"
+#include "src/tint/utils/result/result.h"
+
+namespace tint::spirv::reader {
+
+/// Assemble a textual SPIR-V module into a SPIR-V binary.
+/// @param spirv_asm the textual SPIR-V assembly
+/// @returns the SPIR-V binary data, or an error string
+inline Result<std::vector<uint32_t>, std::string> Assemble(std::string spirv_asm) {
+    StringStream err;
+    std::vector<uint32_t> binary;
+    spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_0);
+    tools.SetMessageConsumer(
+        [&err](spv_message_level_t, const char*, const spv_position_t& pos, const char* msg) {
+            err << "SPIR-V assembly failed:" << pos.line << ":" << pos.column << ": " << msg;
+        });
+    if (!tools.Assemble(spirv_asm, &binary, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS)) {
+        return err.str();
+    }
+    return binary;
+}
+
+}  // namespace tint::spirv::reader
+
+#endif  // SRC_TINT_LANG_SPIRV_READER_COMMON_HELPER_TEST_H_
diff --git a/src/tint/lang/spirv/reader/lower/BUILD.bazel b/src/tint/lang/spirv/reader/lower/BUILD.bazel
new file mode 100644
index 0000000..01fd572
--- /dev/null
+++ b/src/tint/lang/spirv/reader/lower/BUILD.bazel
@@ -0,0 +1,106 @@
+# Copyright 2023 The Dawn & Tint Authors
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this
+#    list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+#    this list of conditions and the following disclaimer in the documentation
+#    and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its
+#    contributors may be used to endorse or promote products derived from
+#    this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+################################################################################
+# File generated by 'tools/src/cmd/gen' using the template:
+#   tools/src/cmd/gen/build/BUILD.bazel.tmpl
+#
+# To regenerate run: './tools/run gen'
+#
+#                       Do not modify this file directly
+################################################################################
+
+load("//src/tint:flags.bzl", "COPTS")
+load("@bazel_skylib//lib:selects.bzl", "selects")
+cc_library(
+  name = "lower",
+  srcs = [
+    "lower.cc",
+    "vector_element_pointer.cc",
+  ],
+  hdrs = [
+    "lower.h",
+    "vector_element_pointer.h",
+  ],
+  deps = [
+    "//src/tint/api/common",
+    "//src/tint/lang/core",
+    "//src/tint/lang/core/constant",
+    "//src/tint/lang/core/intrinsic",
+    "//src/tint/lang/core/ir",
+    "//src/tint/lang/core/type",
+    "//src/tint/utils/containers",
+    "//src/tint/utils/diagnostic",
+    "//src/tint/utils/ice",
+    "//src/tint/utils/id",
+    "//src/tint/utils/macros",
+    "//src/tint/utils/math",
+    "//src/tint/utils/memory",
+    "//src/tint/utils/reflection",
+    "//src/tint/utils/result",
+    "//src/tint/utils/rtti",
+    "//src/tint/utils/symbol",
+    "//src/tint/utils/text",
+    "//src/tint/utils/traits",
+  ],
+  copts = COPTS,
+  visibility = ["//visibility:public"],
+)
+cc_library(
+  name = "test",
+  alwayslink = True,
+  srcs = [
+    "vector_element_pointer_test.cc",
+  ],
+  deps = [
+    "//src/tint/api/common",
+    "//src/tint/lang/core",
+    "//src/tint/lang/core/constant",
+    "//src/tint/lang/core/intrinsic",
+    "//src/tint/lang/core/ir",
+    "//src/tint/lang/core/ir/transform:test",
+    "//src/tint/lang/core/type",
+    "//src/tint/lang/spirv/reader/lower",
+    "//src/tint/utils/containers",
+    "//src/tint/utils/diagnostic",
+    "//src/tint/utils/ice",
+    "//src/tint/utils/id",
+    "//src/tint/utils/macros",
+    "//src/tint/utils/math",
+    "//src/tint/utils/memory",
+    "//src/tint/utils/reflection",
+    "//src/tint/utils/result",
+    "//src/tint/utils/rtti",
+    "//src/tint/utils/symbol",
+    "//src/tint/utils/text",
+    "//src/tint/utils/traits",
+    "@gtest",
+  ],
+  copts = COPTS,
+  visibility = ["//visibility:public"],
+)
+
diff --git a/src/tint/lang/spirv/reader/lower/BUILD.cmake b/src/tint/lang/spirv/reader/lower/BUILD.cmake
new file mode 100644
index 0000000..5f0e866
--- /dev/null
+++ b/src/tint/lang/spirv/reader/lower/BUILD.cmake
@@ -0,0 +1,104 @@
+# Copyright 2023 The Dawn & Tint Authors
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this
+#    list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+#    this list of conditions and the following disclaimer in the documentation
+#    and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its
+#    contributors may be used to endorse or promote products derived from
+#    this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+################################################################################
+# File generated by 'tools/src/cmd/gen' using the template:
+#   tools/src/cmd/gen/build/BUILD.cmake.tmpl
+#
+# To regenerate run: './tools/run gen'
+#
+#                       Do not modify this file directly
+################################################################################
+
+################################################################################
+# Target:    tint_lang_spirv_reader_lower
+# Kind:      lib
+################################################################################
+tint_add_target(tint_lang_spirv_reader_lower lib
+  lang/spirv/reader/lower/lower.cc
+  lang/spirv/reader/lower/lower.h
+  lang/spirv/reader/lower/vector_element_pointer.cc
+  lang/spirv/reader/lower/vector_element_pointer.h
+)
+
+tint_target_add_dependencies(tint_lang_spirv_reader_lower lib
+  tint_api_common
+  tint_lang_core
+  tint_lang_core_constant
+  tint_lang_core_intrinsic
+  tint_lang_core_ir
+  tint_lang_core_type
+  tint_utils_containers
+  tint_utils_diagnostic
+  tint_utils_ice
+  tint_utils_id
+  tint_utils_macros
+  tint_utils_math
+  tint_utils_memory
+  tint_utils_reflection
+  tint_utils_result
+  tint_utils_rtti
+  tint_utils_symbol
+  tint_utils_text
+  tint_utils_traits
+)
+
+################################################################################
+# Target:    tint_lang_spirv_reader_lower_test
+# Kind:      test
+################################################################################
+tint_add_target(tint_lang_spirv_reader_lower_test test
+  lang/spirv/reader/lower/vector_element_pointer_test.cc
+)
+
+tint_target_add_dependencies(tint_lang_spirv_reader_lower_test test
+  tint_api_common
+  tint_lang_core
+  tint_lang_core_constant
+  tint_lang_core_intrinsic
+  tint_lang_core_ir
+  tint_lang_core_ir_transform_test
+  tint_lang_core_type
+  tint_lang_spirv_reader_lower
+  tint_utils_containers
+  tint_utils_diagnostic
+  tint_utils_ice
+  tint_utils_id
+  tint_utils_macros
+  tint_utils_math
+  tint_utils_memory
+  tint_utils_reflection
+  tint_utils_result
+  tint_utils_rtti
+  tint_utils_symbol
+  tint_utils_text
+  tint_utils_traits
+)
+
+tint_target_add_external_dependencies(tint_lang_spirv_reader_lower_test test
+  "gtest"
+)
diff --git a/src/tint/lang/spirv/reader/lower/BUILD.gn b/src/tint/lang/spirv/reader/lower/BUILD.gn
new file mode 100644
index 0000000..b5f0342
--- /dev/null
+++ b/src/tint/lang/spirv/reader/lower/BUILD.gn
@@ -0,0 +1,102 @@
+# Copyright 2023 The Dawn & Tint Authors
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this
+#    list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+#    this list of conditions and the following disclaimer in the documentation
+#    and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its
+#    contributors may be used to endorse or promote products derived from
+#    this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+################################################################################
+# File generated by 'tools/src/cmd/gen' using the template:
+#   tools/src/cmd/gen/build/BUILD.gn.tmpl
+#
+# To regenerate run: './tools/run gen'
+#
+#                       Do not modify this file directly
+################################################################################
+
+import("../../../../../../scripts/tint_overrides_with_defaults.gni")
+
+import("${tint_src_dir}/tint.gni")
+
+if (tint_build_unittests || tint_build_benchmarks) {
+  import("//testing/test.gni")
+}
+
+libtint_source_set("lower") {
+  sources = [
+    "lower.cc",
+    "lower.h",
+    "vector_element_pointer.cc",
+    "vector_element_pointer.h",
+  ]
+  deps = [
+    "${tint_src_dir}/api/common",
+    "${tint_src_dir}/lang/core",
+    "${tint_src_dir}/lang/core/constant",
+    "${tint_src_dir}/lang/core/intrinsic",
+    "${tint_src_dir}/lang/core/ir",
+    "${tint_src_dir}/lang/core/type",
+    "${tint_src_dir}/utils/containers",
+    "${tint_src_dir}/utils/diagnostic",
+    "${tint_src_dir}/utils/ice",
+    "${tint_src_dir}/utils/id",
+    "${tint_src_dir}/utils/macros",
+    "${tint_src_dir}/utils/math",
+    "${tint_src_dir}/utils/memory",
+    "${tint_src_dir}/utils/reflection",
+    "${tint_src_dir}/utils/result",
+    "${tint_src_dir}/utils/rtti",
+    "${tint_src_dir}/utils/symbol",
+    "${tint_src_dir}/utils/text",
+    "${tint_src_dir}/utils/traits",
+  ]
+}
+if (tint_build_unittests) {
+  tint_unittests_source_set("unittests") {
+    sources = [ "vector_element_pointer_test.cc" ]
+    deps = [
+      "${tint_src_dir}:gmock_and_gtest",
+      "${tint_src_dir}/api/common",
+      "${tint_src_dir}/lang/core",
+      "${tint_src_dir}/lang/core/constant",
+      "${tint_src_dir}/lang/core/intrinsic",
+      "${tint_src_dir}/lang/core/ir",
+      "${tint_src_dir}/lang/core/ir/transform:unittests",
+      "${tint_src_dir}/lang/core/type",
+      "${tint_src_dir}/lang/spirv/reader/lower",
+      "${tint_src_dir}/utils/containers",
+      "${tint_src_dir}/utils/diagnostic",
+      "${tint_src_dir}/utils/ice",
+      "${tint_src_dir}/utils/id",
+      "${tint_src_dir}/utils/macros",
+      "${tint_src_dir}/utils/math",
+      "${tint_src_dir}/utils/memory",
+      "${tint_src_dir}/utils/reflection",
+      "${tint_src_dir}/utils/result",
+      "${tint_src_dir}/utils/rtti",
+      "${tint_src_dir}/utils/symbol",
+      "${tint_src_dir}/utils/text",
+      "${tint_src_dir}/utils/traits",
+    ]
+  }
+}
diff --git a/src/tint/lang/spirv/reader/lower/lower.cc b/src/tint/lang/spirv/reader/lower/lower.cc
new file mode 100644
index 0000000..25f546c
--- /dev/null
+++ b/src/tint/lang/spirv/reader/lower/lower.cc
@@ -0,0 +1,54 @@
+// Copyright 2024 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "src/tint/lang/spirv/reader/lower/lower.h"
+
+#include "src/tint/lang/core/ir/validator.h"
+#include "src/tint/lang/spirv/reader/lower/vector_element_pointer.h"
+
+namespace tint::spirv::reader {
+
+Result<SuccessType> Lower(core::ir::Module& mod) {
+#define RUN_TRANSFORM(name, ...)         \
+    do {                                 \
+        auto result = name(__VA_ARGS__); \
+        if (result != Success) {         \
+            return result;               \
+        }                                \
+    } while (false)
+
+    RUN_TRANSFORM(lower::VectorElementPointer, mod);
+
+    if (auto res = core::ir::ValidateAndDumpIfNeeded(mod, "end of lowering from SPIR-V");
+        res != Success) {
+        return res.Failure();
+    }
+
+    return Success;
+}
+
+}  // namespace tint::spirv::reader
diff --git a/src/tint/lang/spirv/reader/lower/lower.h b/src/tint/lang/spirv/reader/lower/lower.h
new file mode 100644
index 0000000..0d8e3b2
--- /dev/null
+++ b/src/tint/lang/spirv/reader/lower/lower.h
@@ -0,0 +1,43 @@
+// Copyright 2024 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef SRC_TINT_LANG_SPIRV_READER_LOWER_LOWER_H_
+#define SRC_TINT_LANG_SPIRV_READER_LOWER_LOWER_H_
+
+#include "src/tint/lang/core/ir/module.h"
+#include "src/tint/utils/result/result.h"
+
+namespace tint::spirv::reader {
+
+/// Lower converts a SPIR-V-dialect IR module to a core-dialect IR module
+/// @param  mod the IR module
+/// @return the result of the operation
+Result<SuccessType> Lower(core::ir::Module& mod);
+
+}  // namespace tint::spirv::reader
+
+#endif  // SRC_TINT_LANG_SPIRV_READER_LOWER_LOWER_H_
diff --git a/src/tint/lang/spirv/reader/lower/vector_element_pointer.cc b/src/tint/lang/spirv/reader/lower/vector_element_pointer.cc
new file mode 100644
index 0000000..ad0d3f3
--- /dev/null
+++ b/src/tint/lang/spirv/reader/lower/vector_element_pointer.cc
@@ -0,0 +1,178 @@
+// Copyright 2024 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "src/tint/lang/spirv/reader/lower/vector_element_pointer.h"
+
+#include <utility>
+
+#include "src/tint/lang/core/ir/builder.h"
+#include "src/tint/lang/core/ir/module.h"
+#include "src/tint/lang/core/ir/validator.h"
+
+namespace tint::spirv::reader::lower {
+
+namespace {
+
+using namespace tint::core::fluent_types;  // NOLINT
+
+/// PIMPL state for the transform.
+struct State {
+    /// The IR module.
+    core::ir::Module& ir;
+
+    /// The IR builder.
+    core::ir::Builder b{ir};
+
+    /// The type manager.
+    core::type::Manager& ty{ir.Types()};
+
+    /// Access is an access instruction and the type of the vector that it produces a pointer to.
+    struct Access {
+        /// The access instruction.
+        core::ir::Access* inst;
+        /// The vector type being accessed.
+        const core::type::Type* type;
+    };
+
+    /// Process the module.
+    void Process() {
+        // Find the access instructions that need to be replaced.
+        Vector<Access, 8> worklist;
+        for (auto* inst : ir.instructions.Objects()) {
+            if (!inst->Alive()) {
+                continue;
+            }
+            if (auto* access = inst->As<core::ir::Access>()) {
+                auto* source_ty = access->Object()->Type();
+                if (!source_ty->Is<core::type::Pointer>()) {
+                    continue;
+                }
+                source_ty = source_ty->UnwrapPtr();
+
+                // Step through the indices of the access instruction to check for vector types.
+                for (auto* idx : access->Indices()) {
+                    if (source_ty->Is<core::type::Vector>()) {
+                        // Found an access that is indexing into a vector pointer.
+                        worklist.Push(Access{access, source_ty});
+                        break;
+                    }
+
+                    // Update the current source type based on the next index.
+                    if (auto* constant = idx->As<core::ir::Constant>()) {
+                        auto i = constant->Value()->ValueAs<u32>();
+                        source_ty = source_ty->Element(i);
+                    } else {
+                        source_ty = source_ty->Elements().type;
+                    }
+                }
+            }
+        }
+
+        // Replace the access instructions that we found.
+        for (const auto& access : worklist) {
+            ReplaceAccess(access);
+        }
+    }
+
+    /// Replace an access instruction with {load,store}_vector_element instructions.
+    /// @param access the access instruction to replace
+    void ReplaceAccess(const Access& access) {
+        auto* object = access.inst->Object();
+
+        if (access.inst->Indices().Length() > 1) {
+            // Create a new access instruction that stops at the vector pointer.
+            Vector<core::ir::Value*, 8> partial_indices{access.inst->Indices()};
+            partial_indices.Pop();
+            auto addrspace = object->Type()->As<core::type::Pointer>()->AddressSpace();
+            auto* access_to_vec = b.Access(ty.ptr(addrspace, access.type), object, partial_indices);
+            access_to_vec->InsertBefore(access.inst);
+
+            object = access_to_vec->Result(0);
+        }
+
+        // Replace all uses of the original access instruction.
+        auto* index = access.inst->Indices().Back();
+        ReplaceAccessUses(access.inst, object, index);
+
+        // Destroy the original access instruction.
+        access.inst->Destroy();
+    }
+
+    /// Replace all uses of an access instruction with {load,store}_vector_element instructions.
+    /// @param access the access instruction to replace
+    /// @param object the pointer-to-vector source object
+    /// @param index the index of the vector element
+    void ReplaceAccessUses(core::ir::Access* access,
+                           core::ir::Value* object,
+                           core::ir::Value* index) {
+        Vector<core::ir::Instruction*, 4> to_destroy;
+        access->Result(0)->ForEachUse([&](core::ir::Usage use) {
+            Switch(
+                use.instruction,
+                [&](core::ir::Load* load) {
+                    auto* lve = b.LoadVectorElement(object, index);
+                    lve->InsertBefore(load);
+                    load->Result(0)->ReplaceAllUsesWith(lve->Result(0));
+                    to_destroy.Push(load);
+                },
+                [&](core::ir::Store* store) {
+                    auto* sve = b.StoreVectorElement(object, index, store->From());
+                    sve->InsertBefore(store);
+                    to_destroy.Push(store);
+                },
+                [&](core::ir::Access* noop_access) {
+                    TINT_ASSERT(noop_access->Indices().IsEmpty());
+                    ReplaceAccessUses(noop_access, object, index);
+                    to_destroy.Push(noop_access);
+                },
+                TINT_ICE_ON_NO_MATCH);
+        });
+
+        // Clean up old instructions.
+        for (auto* inst : to_destroy) {
+            inst->Destroy();
+        }
+    }
+};
+
+}  // namespace
+
+Result<SuccessType> VectorElementPointer(core::ir::Module& ir) {
+    auto result = ValidateAndDumpIfNeeded(ir, "VectorElementPointer transform",
+                                          EnumSet<core::ir::Capability>{
+                                              core::ir::Capability::kAllowVectorElementPointer,
+                                          });
+    if (result != Success) {
+        return result.Failure();
+    }
+
+    State{ir}.Process();
+
+    return Success;
+}
+
+}  // namespace tint::spirv::reader::lower
diff --git a/src/tint/lang/spirv/reader/lower/vector_element_pointer.h b/src/tint/lang/spirv/reader/lower/vector_element_pointer.h
new file mode 100644
index 0000000..e07a30ca
--- /dev/null
+++ b/src/tint/lang/spirv/reader/lower/vector_element_pointer.h
@@ -0,0 +1,48 @@
+// Copyright 2024 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef SRC_TINT_LANG_SPIRV_READER_LOWER_VECTOR_ELEMENT_POINTER_H_
+#define SRC_TINT_LANG_SPIRV_READER_LOWER_VECTOR_ELEMENT_POINTER_H_
+
+#include "src/tint/utils/result/result.h"
+
+// Forward declarations.
+namespace tint::core::ir {
+class Module;
+}
+
+namespace tint::spirv::reader::lower {
+
+/// VectorElementPointer is a transform that removes pointers to vector elements by replacing access
+/// instructions and their uses.
+/// @param module the module to transform
+/// @returns success or failure
+Result<SuccessType> VectorElementPointer(core::ir::Module& module);
+
+}  // namespace tint::spirv::reader::lower
+
+#endif  // SRC_TINT_LANG_SPIRV_READER_LOWER_VECTOR_ELEMENT_POINTER_H_
diff --git a/src/tint/lang/spirv/reader/lower/vector_element_pointer_test.cc b/src/tint/lang/spirv/reader/lower/vector_element_pointer_test.cc
new file mode 100644
index 0000000..e7160a5
--- /dev/null
+++ b/src/tint/lang/spirv/reader/lower/vector_element_pointer_test.cc
@@ -0,0 +1,487 @@
+// Copyright 2024 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "src/tint/lang/spirv/reader/lower/vector_element_pointer.h"
+
+#include <utility>
+
+#include "src/tint/lang/core/ir/transform/helper_test.h"
+
+namespace tint::spirv::reader::lower {
+namespace {
+
+using namespace tint::core::fluent_types;     // NOLINT
+using namespace tint::core::number_suffixes;  // NOLINT
+
+using SpirvReader_VectorElementPointerTest = core::ir::transform::TransformTest;
+
+TEST_F(SpirvReader_VectorElementPointerTest, NonPointerAccess) {
+    auto* vec = b.FunctionParam("vec", ty.vec4<u32>());
+    auto* foo = b.Function("foo", ty.u32());
+    b.Append(foo->Block(), [&] {
+        auto* access = b.Access<u32>(vec, 2_u);
+        b.Return(foo, access);
+    });
+
+    auto* src = R"(
+%foo = func():u32 -> %b1 {
+  %b1 = block {
+    %2:u32 = access %vec, 2u
+    ret %2
+  }
+}
+)";
+    EXPECT_EQ(src, str());
+
+    auto* expect = src;
+
+    Run(VectorElementPointer);
+
+    EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvReader_VectorElementPointerTest, Access_NoIndices) {
+    auto* foo = b.Function("foo", ty.vec4<u32>());
+    b.Append(foo->Block(), [&] {
+        auto* vec = b.Var<function, vec4<u32>>("vec");
+        auto* access = b.Access<ptr<function, vec4<u32>>>(vec);
+        b.Return(foo, b.Load(access));
+    });
+
+    auto* src = R"(
+%foo = func():vec4<u32> -> %b1 {
+  %b1 = block {
+    %vec:ptr<function, vec4<u32>, read_write> = var
+    %3:ptr<function, vec4<u32>, read_write> = access %vec
+    %4:vec4<u32> = load %3
+    ret %4
+  }
+}
+)";
+    EXPECT_EQ(src, str());
+
+    auto* expect = src;
+
+    Run(VectorElementPointer);
+
+    EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvReader_VectorElementPointerTest, Access_NoIndices_Chain) {
+    auto* foo = b.Function("foo", ty.vec4<u32>());
+    b.Append(foo->Block(), [&] {
+        auto* vec = b.Var<function, vec4<u32>>("vec");
+        auto* access_1 = b.Access<ptr<function, vec4<u32>>>(vec);
+        auto* access_2 = b.Access<ptr<function, vec4<u32>>>(access_1);
+        auto* access_3 = b.Access<ptr<function, vec4<u32>>>(access_2);
+        b.Return(foo, b.Load(access_3));
+    });
+
+    auto* src = R"(
+%foo = func():vec4<u32> -> %b1 {
+  %b1 = block {
+    %vec:ptr<function, vec4<u32>, read_write> = var
+    %3:ptr<function, vec4<u32>, read_write> = access %vec
+    %4:ptr<function, vec4<u32>, read_write> = access %3
+    %5:ptr<function, vec4<u32>, read_write> = access %4
+    %6:vec4<u32> = load %5
+    ret %6
+  }
+}
+)";
+    EXPECT_EQ(src, str());
+
+    auto* expect = src;
+
+    Run(VectorElementPointer);
+
+    EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvReader_VectorElementPointerTest, Access_Component_NoUse) {
+    auto* foo = b.Function("foo", ty.void_());
+    b.Append(foo->Block(), [&] {
+        auto* vec = b.Var<function, vec4<u32>>("vec");
+        b.Access<ptr<function, u32>>(vec, 2_u);
+        b.Return(foo);
+    });
+
+    auto* src = R"(
+%foo = func():void -> %b1 {
+  %b1 = block {
+    %vec:ptr<function, vec4<u32>, read_write> = var
+    %3:ptr<function, u32, read_write> = access %vec, 2u
+    ret
+  }
+}
+)";
+    EXPECT_EQ(src, str());
+
+    auto* expect = R"(
+%foo = func():void -> %b1 {
+  %b1 = block {
+    %vec:ptr<function, vec4<u32>, read_write> = var
+    ret
+  }
+}
+)";
+
+    Run(VectorElementPointer);
+
+    EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvReader_VectorElementPointerTest, Load) {
+    auto* foo = b.Function("foo", ty.u32());
+    b.Append(foo->Block(), [&] {
+        auto* vec = b.Var<function, vec4<u32>>("vec");
+        auto* access = b.Access<ptr<function, u32>>(vec, 2_u);
+        auto* load = b.Load(access);
+        b.Return(foo, load);
+    });
+
+    auto* src = R"(
+%foo = func():u32 -> %b1 {
+  %b1 = block {
+    %vec:ptr<function, vec4<u32>, read_write> = var
+    %3:ptr<function, u32, read_write> = access %vec, 2u
+    %4:u32 = load %3
+    ret %4
+  }
+}
+)";
+    EXPECT_EQ(src, str());
+
+    auto* expect = R"(
+%foo = func():u32 -> %b1 {
+  %b1 = block {
+    %vec:ptr<function, vec4<u32>, read_write> = var
+    %3:u32 = load_vector_element %vec, 2u
+    ret %3
+  }
+}
+)";
+
+    Run(VectorElementPointer);
+
+    EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvReader_VectorElementPointerTest, Store) {
+    auto* foo = b.Function("foo", ty.void_());
+    b.Append(foo->Block(), [&] {
+        auto* vec = b.Var<function, vec4<u32>>("vec");
+        auto* access = b.Access<ptr<function, u32>>(vec, 2_u);
+        b.Store(access, 42_u);
+        b.Return(foo);
+    });
+
+    auto* src = R"(
+%foo = func():void -> %b1 {
+  %b1 = block {
+    %vec:ptr<function, vec4<u32>, read_write> = var
+    %3:ptr<function, u32, read_write> = access %vec, 2u
+    store %3, 42u
+    ret
+  }
+}
+)";
+    EXPECT_EQ(src, str());
+
+    auto* expect = R"(
+%foo = func():void -> %b1 {
+  %b1 = block {
+    %vec:ptr<function, vec4<u32>, read_write> = var
+    store_vector_element %vec, 2u, 42u
+    ret
+  }
+}
+)";
+
+    Run(VectorElementPointer);
+
+    EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvReader_VectorElementPointerTest, AccessBeforeUse) {
+    auto* foo = b.Function("foo", ty.void_());
+    b.Append(foo->Block(), [&] {
+        auto* vec = b.Var<function, vec4<u32>>("vec");
+        auto* access_1 = b.Access<ptr<function, u32>>(vec, 2_u);
+        auto* access_2 = b.Access<ptr<function, u32>>(access_1);
+        b.Store(access_2, 42_u);
+        b.Return(foo);
+    });
+
+    auto* src = R"(
+%foo = func():void -> %b1 {
+  %b1 = block {
+    %vec:ptr<function, vec4<u32>, read_write> = var
+    %3:ptr<function, u32, read_write> = access %vec, 2u
+    %4:ptr<function, u32, read_write> = access %3
+    store %4, 42u
+    ret
+  }
+}
+)";
+    EXPECT_EQ(src, str());
+
+    auto* expect = R"(
+%foo = func():void -> %b1 {
+  %b1 = block {
+    %vec:ptr<function, vec4<u32>, read_write> = var
+    store_vector_element %vec, 2u, 42u
+    ret
+  }
+}
+)";
+
+    Run(VectorElementPointer);
+
+    EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvReader_VectorElementPointerTest, MultipleUses) {
+    auto* foo = b.Function("foo", ty.void_());
+    b.Append(foo->Block(), [&] {
+        auto* vec = b.Var<function, vec4<u32>>("vec");
+        auto* access = b.Access<ptr<function, u32>>(vec, 2_u);
+        auto* load = b.Load(access);
+        auto* add = b.Add<u32>(load, 1_u);
+        b.Store(access, add);
+        b.Return(foo);
+    });
+
+    auto* src = R"(
+%foo = func():void -> %b1 {
+  %b1 = block {
+    %vec:ptr<function, vec4<u32>, read_write> = var
+    %3:ptr<function, u32, read_write> = access %vec, 2u
+    %4:u32 = load %3
+    %5:u32 = add %4, 1u
+    store %3, %5
+    ret
+  }
+}
+)";
+    EXPECT_EQ(src, str());
+
+    auto* expect = R"(
+%foo = func():void -> %b1 {
+  %b1 = block {
+    %vec:ptr<function, vec4<u32>, read_write> = var
+    %3:u32 = load_vector_element %vec, 2u
+    %4:u32 = add %3, 1u
+    store_vector_element %vec, 2u, %4
+    ret
+  }
+}
+)";
+
+    Run(VectorElementPointer);
+
+    EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvReader_VectorElementPointerTest, ViaMatrix) {
+    auto* foo = b.Function("foo", ty.void_());
+    b.Append(foo->Block(), [&] {
+        auto* mat = b.Var<function, mat4x4<f32>>("mat");
+        auto* access = b.Access<ptr<function, f32>>(mat, 1_u, 2_u);
+        b.Store(access, 42_f);
+        b.Return(foo);
+    });
+
+    auto* src = R"(
+%foo = func():void -> %b1 {
+  %b1 = block {
+    %mat:ptr<function, mat4x4<f32>, read_write> = var
+    %3:ptr<function, f32, read_write> = access %mat, 1u, 2u
+    store %3, 42.0f
+    ret
+  }
+}
+)";
+    EXPECT_EQ(src, str());
+
+    auto* expect = R"(
+%foo = func():void -> %b1 {
+  %b1 = block {
+    %mat:ptr<function, mat4x4<f32>, read_write> = var
+    %3:ptr<function, vec4<f32>, read_write> = access %mat, 1u
+    store_vector_element %3, 2u, 42.0f
+    ret
+  }
+}
+)";
+
+    Run(VectorElementPointer);
+
+    EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvReader_VectorElementPointerTest, ViaArray) {
+    auto* foo = b.Function("foo", ty.void_());
+    b.Append(foo->Block(), [&] {
+        auto* arr = b.Var<function, array<vec4<f32>, 4>>("arr");
+        auto* access = b.Access<ptr<function, f32>>(arr, 1_u, 2_u);
+        b.Store(access, 42_f);
+        b.Return(foo);
+    });
+
+    auto* src = R"(
+%foo = func():void -> %b1 {
+  %b1 = block {
+    %arr:ptr<function, array<vec4<f32>, 4>, read_write> = var
+    %3:ptr<function, f32, read_write> = access %arr, 1u, 2u
+    store %3, 42.0f
+    ret
+  }
+}
+)";
+    EXPECT_EQ(src, str());
+
+    auto* expect = R"(
+%foo = func():void -> %b1 {
+  %b1 = block {
+    %arr:ptr<function, array<vec4<f32>, 4>, read_write> = var
+    %3:ptr<function, vec4<f32>, read_write> = access %arr, 1u
+    store_vector_element %3, 2u, 42.0f
+    ret
+  }
+}
+)";
+
+    Run(VectorElementPointer);
+
+    EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvReader_VectorElementPointerTest, ViaStruct) {
+    auto* str_ty = ty.Struct(mod.symbols.New("str"), {{
+                                                         mod.symbols.New("vec"),
+                                                         ty.vec4<f32>(),
+                                                     }});
+
+    auto* foo = b.Function("foo", ty.void_());
+    b.Append(foo->Block(), [&] {
+        auto* str = b.Var("str", ty.ptr<function>(str_ty));
+        auto* access = b.Access<ptr<function, f32>>(str, 0_u, 2_u);
+        b.Store(access, 42_f);
+        b.Return(foo);
+    });
+
+    auto* src = R"(
+str = struct @align(16) {
+  vec:vec4<f32> @offset(0)
+}
+
+%foo = func():void -> %b1 {
+  %b1 = block {
+    %str:ptr<function, str, read_write> = var
+    %3:ptr<function, f32, read_write> = access %str, 0u, 2u
+    store %3, 42.0f
+    ret
+  }
+}
+)";
+    EXPECT_EQ(src, str());
+
+    auto* expect = R"(
+str = struct @align(16) {
+  vec:vec4<f32> @offset(0)
+}
+
+%foo = func():void -> %b1 {
+  %b1 = block {
+    %str:ptr<function, str, read_write> = var
+    %3:ptr<function, vec4<f32>, read_write> = access %str, 0u
+    store_vector_element %3, 2u, 42.0f
+    ret
+  }
+}
+)";
+
+    Run(VectorElementPointer);
+
+    EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvReader_VectorElementPointerTest, DeeplyNested) {
+    auto* inner_arr = ty.array(ty.mat4x4<f32>(), 4);
+    auto* str_ty = ty.Struct(mod.symbols.New("str"), {{
+                                                         mod.symbols.New("inner"),
+                                                         inner_arr,
+                                                     }});
+    auto* outer_arr = ty.array(str_ty, 4);
+
+    auto* foo = b.Function("foo", ty.void_());
+    b.Append(foo->Block(), [&] {
+        auto* arr = b.Var("arr", ty.ptr<function>(outer_arr));
+        auto* access = b.Access<ptr<function, f32>>(arr, 1_u, 0_u, 3_u, 2_u, 1_u);
+        b.Store(access, 42_f);
+        b.Return(foo);
+    });
+
+    auto* src = R"(
+str = struct @align(16) {
+  inner:array<mat4x4<f32>, 4> @offset(0)
+}
+
+%foo = func():void -> %b1 {
+  %b1 = block {
+    %arr:ptr<function, array<str, 4>, read_write> = var
+    %3:ptr<function, f32, read_write> = access %arr, 1u, 0u, 3u, 2u, 1u
+    store %3, 42.0f
+    ret
+  }
+}
+)";
+    EXPECT_EQ(src, str());
+
+    auto* expect = R"(
+str = struct @align(16) {
+  inner:array<mat4x4<f32>, 4> @offset(0)
+}
+
+%foo = func():void -> %b1 {
+  %b1 = block {
+    %arr:ptr<function, array<str, 4>, read_write> = var
+    %3:ptr<function, vec4<f32>, read_write> = access %arr, 1u, 0u, 3u, 2u
+    store_vector_element %3, 1u, 42.0f
+    ret
+  }
+}
+)";
+
+    Run(VectorElementPointer);
+
+    EXPECT_EQ(expect, str());
+}
+
+}  // namespace
+}  // namespace tint::spirv::reader::lower
diff --git a/src/tint/lang/spirv/reader/parser/BUILD.bazel b/src/tint/lang/spirv/reader/parser/BUILD.bazel
index d41f46f..2bfaad6 100644
--- a/src/tint/lang/spirv/reader/parser/BUILD.bazel
+++ b/src/tint/lang/spirv/reader/parser/BUILD.bazel
@@ -108,6 +108,7 @@
     "@gtest",
   ] + select({
     ":tint_build_spv_reader": [
+      "//src/tint/lang/spirv/reader/common:test",
       "//src/tint/lang/spirv/reader/parser",
     ],
     "//conditions:default": [],
diff --git a/src/tint/lang/spirv/reader/parser/BUILD.cmake b/src/tint/lang/spirv/reader/parser/BUILD.cmake
index e82f651..c8abe87 100644
--- a/src/tint/lang/spirv/reader/parser/BUILD.cmake
+++ b/src/tint/lang/spirv/reader/parser/BUILD.cmake
@@ -120,6 +120,7 @@
 
 if(TINT_BUILD_SPV_READER)
   tint_target_add_dependencies(tint_lang_spirv_reader_parser_test test
+    tint_lang_spirv_reader_common_test
     tint_lang_spirv_reader_parser
   )
 endif(TINT_BUILD_SPV_READER)
diff --git a/src/tint/lang/spirv/reader/parser/BUILD.gn b/src/tint/lang/spirv/reader/parser/BUILD.gn
index 430c576..26c2954 100644
--- a/src/tint/lang/spirv/reader/parser/BUILD.gn
+++ b/src/tint/lang/spirv/reader/parser/BUILD.gn
@@ -116,7 +116,10 @@
       ]
 
       if (tint_build_spv_reader) {
-        deps += [ "${tint_src_dir}/lang/spirv/reader/parser" ]
+        deps += [
+          "${tint_src_dir}/lang/spirv/reader/common:unittests",
+          "${tint_src_dir}/lang/spirv/reader/parser",
+        ]
       }
 
       if (tint_build_spv_reader || tint_build_spv_writer) {
diff --git a/src/tint/lang/spirv/reader/parser/helper_test.h b/src/tint/lang/spirv/reader/parser/helper_test.h
index 489484e..c7fc02a 100644
--- a/src/tint/lang/spirv/reader/parser/helper_test.h
+++ b/src/tint/lang/spirv/reader/parser/helper_test.h
@@ -34,47 +34,32 @@
 
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
-#include "spirv-tools/libspirv.hpp"
 #include "src/tint/lang/core/ir/disassembler.h"
 #include "src/tint/lang/core/ir/module.h"
 #include "src/tint/lang/core/ir/validator.h"
+#include "src/tint/lang/spirv/reader/common/helper_test.h"
 #include "src/tint/lang/spirv/reader/parser/parser.h"
 
 namespace tint::spirv::reader {
 
 // Helper macro to run the parser and compare the disassembled IR to a string.
 // Automatically prefixes the IR disassembly with a newline to improve formatting of tests.
-#define EXPECT_IR(asm, ir)                               \
-    do {                                                 \
-        auto got = "\n" + Run(asm);                      \
-        ASSERT_THAT(got, testing::HasSubstr(ir)) << got; \
+#define EXPECT_IR(asm, ir)                                           \
+    do {                                                             \
+        auto result = Run(asm);                                      \
+        ASSERT_EQ(result, Success) << result.Failure().reason.str(); \
+        auto got = "\n" + result.Get();                              \
+        ASSERT_THAT(got, testing::HasSubstr(ir)) << got;             \
     } while (false)
 
 /// Base helper class for testing the SPIR-V parser implementation.
 template <typename BASE>
 class SpirvParserTestHelperBase : public BASE {
   protected:
-    /// Assemble a textual SPIR-V module into a SPIR-V binary.
-    /// @param spirv_asm the textual SPIR-V assembly
-    /// @returns the SPIR-V binary data, or an error string
-    static Result<std::vector<uint32_t>, std::string> Assemble(std::string spirv_asm) {
-        StringStream err;
-        std::vector<uint32_t> binary;
-        spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_0);
-        tools.SetMessageConsumer(
-            [&err](spv_message_level_t, const char*, const spv_position_t& pos, const char* msg) {
-                err << "SPIR-V assembly failed:" << pos.line << ":" << pos.column << ": " << msg;
-            });
-        if (!tools.Assemble(spirv_asm, &binary, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS)) {
-            return err.str();
-        }
-        return binary;
-    }
-
     /// Run the parser on a SPIR-V module and return the Tint IR or an error string.
     /// @param spirv_asm the SPIR-V assembly to parse
-    /// @returns the disassembled Tint IR
-    std::string Run(std::string spirv_asm) {
+    /// @returns the disassembled Tint IR or an error
+    Result<std::string> Run(std::string spirv_asm) {
         // Assemble the SPIR-V input.
         auto binary = Assemble(spirv_asm);
         if (binary != Success) {
@@ -84,13 +69,16 @@
         // Parse the SPIR-V to produce an IR module.
         auto parsed = Parse(Slice(binary.Get().data(), binary.Get().size()));
         if (parsed != Success) {
-            return parsed.Failure().reason.str();
+            return parsed.Failure();
         }
 
-        // Validate the IR module.
-        auto validated = core::ir::Validate(parsed.Get());
+        // Validate the IR module against the capabilities supported by the SPIR-V dialect.
+        auto validated =
+            core::ir::Validate(parsed.Get(), EnumSet<core::ir::Capability>{
+                                                 core::ir::Capability::kAllowVectorElementPointer,
+                                             });
         if (validated != Success) {
-            return validated.Failure().reason.str();
+            return validated.Failure();
         }
 
         // Return the disassembled IR module.
diff --git a/src/tint/lang/spirv/reader/parser/memory_test.cc b/src/tint/lang/spirv/reader/parser/memory_test.cc
index 2031696..9f8c75f 100644
--- a/src/tint/lang/spirv/reader/parser/memory_test.cc
+++ b/src/tint/lang/spirv/reader/parser/memory_test.cc
@@ -83,8 +83,7 @@
 )");
 }
 
-// TODO(jrprice): We need to handle pointer-to-vector component somewhere.
-TEST_F(SpirvParserTest, DISABLED_Load_VectorComponent) {
+TEST_F(SpirvParserTest, Load_VectorComponent) {
     EXPECT_IR(R"(
                OpCapability Shader
                OpMemoryModel Logical GLSL450
@@ -350,8 +349,7 @@
 )");
 }
 
-// TODO(jrprice): We need to handle pointer-to-vector component somewhere.
-TEST_F(SpirvParserTest, DISABLED_Store_VectorComponent) {
+TEST_F(SpirvParserTest, Store_VectorComponent) {
     EXPECT_IR(R"(
                OpCapability Shader
                OpMemoryModel Logical GLSL450
@@ -359,7 +357,7 @@
                OpExecutionMode %main LocalSize 1 1 1
        %void = OpTypeVoid
         %u32 = OpTypeInt 32 0
-     %u32_42 = OpConstantInt %u32 42
+     %u32_42 = OpConstant %u32 42
       %vec4u = OpTypeVector %u32 4
     %u32_ptr = OpTypePointer Function %u32
   %vec4u_ptr = OpTypePointer Function %vec4u
diff --git a/src/tint/lang/spirv/reader/reader.cc b/src/tint/lang/spirv/reader/reader.cc
index 11199ea..d70c16c 100644
--- a/src/tint/lang/spirv/reader/reader.cc
+++ b/src/tint/lang/spirv/reader/reader.cc
@@ -31,17 +31,22 @@
 
 #include "src/tint/lang/core/ir/module.h"
 #include "src/tint/lang/spirv/reader/ast_parser/parse.h"
+#include "src/tint/lang/spirv/reader/lower/lower.h"
 #include "src/tint/lang/spirv/reader/parser/parser.h"
 
 namespace tint::spirv::reader {
 
 Result<core::ir::Module> ReadIR(const std::vector<uint32_t>& input) {
+    // Parse the input SPIR-V to the SPIR-V dialect of the IR.
     auto mod = Parse(Slice(input.data(), input.size()));
     if (mod != Success) {
         return mod.Failure();
     }
 
-    // TODO(crbug.com/tint/1907): Lower the module to core dialect.
+    // Lower the module to the core dialect of the IR.
+    if (auto res = Lower(mod.Get()); res != Success) {
+        return std::move(res.Failure());
+    }
 
     return mod;
 }
diff --git a/src/tint/lang/spirv/reader/reader_test.cc b/src/tint/lang/spirv/reader/reader_test.cc
new file mode 100644
index 0000000..1a086a8
--- /dev/null
+++ b/src/tint/lang/spirv/reader/reader_test.cc
@@ -0,0 +1,139 @@
+// Copyright 2024 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "src/tint/lang/spirv/reader/reader.h"
+
+#include <string>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "src/tint/lang/core/ir/disassembler.h"
+#include "src/tint/lang/core/ir/module.h"
+#include "src/tint/lang/core/ir/validator.h"
+#include "src/tint/lang/spirv/reader/common/helper_test.h"
+
+namespace tint::spirv::reader {
+namespace {
+
+class SpirvReaderTest : public testing::Test {
+  protected:
+    /// Run the reader on a SPIR-V module and return the Tint IR or an error string.
+    /// @param spirv_asm the SPIR-V assembly to read
+    /// @returns the disassembled Tint IR or an error
+    Result<std::string> Run(std::string spirv_asm) {
+        // Assemble the SPIR-V input.
+        auto binary = Assemble(spirv_asm);
+        if (binary != Success) {
+            return binary.Failure();
+        }
+
+        // Read the SPIR-V to produce a core IR module.
+        auto ir = ReadIR(binary.Get());
+        if (ir != Success) {
+            return ir.Failure();
+        }
+
+        // Validate the IR module against the core dialect.
+        auto validated = core::ir::Validate(ir.Get());
+        if (validated != Success) {
+            return validated.Failure();
+        }
+
+        // Return the disassembled IR module.
+        return "\n" + core::ir::Disassemble(ir.Get());
+    }
+};
+
+TEST_F(SpirvReaderTest, Load_VectorComponent) {
+    auto got = Run(R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+       %void = OpTypeVoid
+        %u32 = OpTypeInt 32 0
+      %vec4u = OpTypeVector %u32 4
+    %u32_ptr = OpTypePointer Function %u32
+  %vec4u_ptr = OpTypePointer Function %vec4u
+      %u32_2 = OpConstant %u32 2
+    %ep_type = OpTypeFunction %void
+       %main = OpFunction %void None %ep_type
+ %main_start = OpLabel
+        %var = OpVariable %vec4u_ptr Function
+     %access = OpAccessChain %u32_ptr %var %u32_2
+       %load = OpLoad %u32 %access
+               OpReturn
+               OpFunctionEnd
+)");
+    ASSERT_EQ(got, Success);
+    EXPECT_EQ(got, R"(
+%main = @compute @workgroup_size(1, 1, 1) func():void -> %b1 {
+  %b1 = block {
+    %2:ptr<function, vec4<u32>, read_write> = var
+    %3:u32 = load_vector_element %2, 2u
+    ret
+  }
+}
+)");
+}
+
+TEST_F(SpirvReaderTest, Store_VectorComponent) {
+    auto got = Run(R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+       %void = OpTypeVoid
+        %u32 = OpTypeInt 32 0
+     %u32_42 = OpConstant %u32 42
+      %vec4u = OpTypeVector %u32 4
+    %u32_ptr = OpTypePointer Function %u32
+  %vec4u_ptr = OpTypePointer Function %vec4u
+      %u32_2 = OpConstant %u32 2
+    %ep_type = OpTypeFunction %void
+       %main = OpFunction %void None %ep_type
+ %main_start = OpLabel
+        %var = OpVariable %vec4u_ptr Function
+     %access = OpAccessChain %u32_ptr %var %u32_2
+               OpStore %access %u32_42
+               OpReturn
+               OpFunctionEnd
+)");
+    ASSERT_EQ(got, Success);
+    EXPECT_EQ(got, R"(
+%main = @compute @workgroup_size(1, 1, 1) func():void -> %b1 {
+  %b1 = block {
+    %2:ptr<function, vec4<u32>, read_write> = var
+    store_vector_element %2, 2u, 42u
+    ret
+  }
+}
+)");
+}
+
+}  // namespace
+}  // namespace tint::spirv::reader
diff --git a/src/tint/lang/spirv/writer/ast_printer/builder.cc b/src/tint/lang/spirv/writer/ast_printer/builder.cc
index b736b28..1895d11 100644
--- a/src/tint/lang/spirv/writer/ast_printer/builder.cc
+++ b/src/tint/lang/spirv/writer/ast_printer/builder.cc
@@ -277,7 +277,6 @@
             "SPIR-V", builder_.AST(), builder_.Diagnostics(),
             Vector{
                 wgsl::Extension::kChromiumDisableUniformityAnalysis,
-                wgsl::Extension::kChromiumExperimentalFullPtrParameters,
                 wgsl::Extension::kChromiumExperimentalPushConstant,
                 wgsl::Extension::kChromiumExperimentalSubgroups,
                 wgsl::Extension::kF16,
diff --git a/src/tint/lang/wgsl/ast/module_test.cc b/src/tint/lang/wgsl/ast/module_test.cc
index 842a357..8e5f629 100644
--- a/src/tint/lang/wgsl/ast/module_test.cc
+++ b/src/tint/lang/wgsl/ast/module_test.cc
@@ -147,7 +147,7 @@
 TEST_F(ModuleTest, Directives) {
     auto* enable_1 = Enable(wgsl::Extension::kF16);
     auto* diagnostic_1 = DiagnosticDirective(wgsl::DiagnosticSeverity::kWarning, "foo");
-    auto* enable_2 = Enable(wgsl::Extension::kChromiumExperimentalFullPtrParameters);
+    auto* enable_2 = Enable(wgsl::Extension::kChromiumExperimentalPixelLocal);
     auto* diagnostic_2 = DiagnosticDirective(wgsl::DiagnosticSeverity::kOff, "bar");
 
     Program program(std::move(*this));
diff --git a/src/tint/lang/wgsl/ast/transform/builtin_polyfill.cc b/src/tint/lang/wgsl/ast/transform/builtin_polyfill.cc
index adc8df2..b3a8ece 100644
--- a/src/tint/lang/wgsl/ast/transform/builtin_polyfill.cc
+++ b/src/tint/lang/wgsl/ast/transform/builtin_polyfill.cc
@@ -61,15 +61,7 @@
     /// Constructor
     /// @param program the source program
     /// @param config the transform config
-    State(const Program& program, const Config& config) : src(program), cfg(config) {
-        has_full_ptr_params = false;
-        for (auto* enable : src.AST().Enables()) {
-            if (enable->HasExtension(wgsl::Extension::kChromiumExperimentalFullPtrParameters)) {
-                has_full_ptr_params = true;
-                break;
-            }
-        }
-    }
+    State(const Program& program, const Config& config) : src(program), cfg(config) {}
 
     /// Runs the transform
     /// @returns the new program or SkipTransform if the transform is not required
@@ -171,8 +163,6 @@
     Hashmap<const sem::BuiltinFn*, Symbol, 8> builtin_polyfills;
     /// Polyfill f32 conversion to i32 or u32 (or vectors of)
     Hashmap<const core::type::Type*, Symbol, 2> f32_conv_polyfills;
-    // Tracks whether the chromium_experimental_full_ptr_parameters extension has been enabled.
-    bool has_full_ptr_params = false;
     /// True if the transform has made changes (i.e. the program needs cloning)
     bool made_changes = false;
 
@@ -823,10 +813,6 @@
     /// @param type the type being loaded
     /// @return the polyfill function name
     Symbol workgroupUniformLoad(const core::type::Type* type) {
-        if (!has_full_ptr_params) {
-            b.Enable(wgsl::Extension::kChromiumExperimentalFullPtrParameters);
-            has_full_ptr_params = true;
-        }
         auto name = b.Symbols().New("tint_workgroupUniformLoad");
         b.Func(name,
                tint::Vector{
diff --git a/src/tint/lang/wgsl/ast/transform/builtin_polyfill_test.cc b/src/tint/lang/wgsl/ast/transform/builtin_polyfill_test.cc
index 4366226..0c64163 100644
--- a/src/tint/lang/wgsl/ast/transform/builtin_polyfill_test.cc
+++ b/src/tint/lang/wgsl/ast/transform/builtin_polyfill_test.cc
@@ -3774,8 +3774,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 fn tint_workgroupUniformLoad(p : ptr<workgroup, i32>) -> i32 {
   workgroupBarrier();
   let result = *(p);
@@ -3815,8 +3813,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 fn tint_workgroupUniformLoad(p : ptr<workgroup, Outer>) -> Outer {
   workgroupBarrier();
   let result = *(p);
@@ -3848,8 +3844,6 @@
 
 TEST_F(BuiltinPolyfillTest, WorkgroupUniformLoad_AvoidDuplicateEnables) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<workgroup> a : i32;
 var<workgroup> b : u32;
 var<workgroup> c : f32;
@@ -3862,8 +3856,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 fn tint_workgroupUniformLoad(p : ptr<workgroup, i32>) -> i32 {
   workgroupBarrier();
   let result = *(p);
@@ -3915,8 +3907,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 fn tint_workgroupUniformLoad_v() -> i32 {
   workgroupBarrier();
   let result = v;
diff --git a/src/tint/lang/wgsl/ast/transform/direct_variable_access.cc b/src/tint/lang/wgsl/ast/transform/direct_variable_access.cc
index c73dead..4ecea23 100644
--- a/src/tint/lang/wgsl/ast/transform/direct_variable_access.cc
+++ b/src/tint/lang/wgsl/ast/transform/direct_variable_access.cc
@@ -195,10 +195,8 @@
     /// The main function for the transform.
     /// @returns the ApplyResult
     ApplyResult Run() {
-        if (!ctx.src->Sem().Module()->Extensions().Contains(
-                wgsl::Extension::kChromiumExperimentalFullPtrParameters)) {
-            // If the 'chromium_experimental_full_ptr_parameters' extension is not enabled, then
-            // there's nothing for this transform to do.
+        // If there are no functions with pointer parameters, then this transform can be skipped.
+        if (!AnyPointerParameters()) {
             return SkipTransform;
         }
 
@@ -409,6 +407,18 @@
     /// Only valid during the lifetime of the program::CloneContext::Clone().
     CloneState* clone_state = nullptr;
 
+    /// @returns true if any user functions have parameters of a pointer type.
+    bool AnyPointerParameters() const {
+        for (auto* fn : ctx.src->AST().Functions()) {
+            for (auto* param : fn->params) {
+                if (sem.Get(param)->Type()->Is<core::type::Pointer>()) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     /// AppendAccessChain creates or extends an existing AccessChain for the given expression,
     /// modifying the #access_chains map.
     void AppendAccessChain(const sem::ValueExpression* expr) {
diff --git a/src/tint/lang/wgsl/ast/transform/direct_variable_access.h b/src/tint/lang/wgsl/ast/transform/direct_variable_access.h
index b47f7e3..d1bb9fe 100644
--- a/src/tint/lang/wgsl/ast/transform/direct_variable_access.h
+++ b/src/tint/lang/wgsl/ast/transform/direct_variable_access.h
@@ -34,8 +34,7 @@
 
 /// DirectVariableAccess is a transform that allows usage of pointer parameters in the 'storage',
 /// 'uniform' and 'workgroup' address space, and passing of pointers to sub-objects. These pointers
-/// are only allowed by the resolver when the `chromium_experimental_full_ptr_parameters` extension
-/// is enabled.
+/// are allowed with the `unrestricted_pointer_parameters` WGSL feature.
 ///
 /// DirectVariableAccess works by creating specializations of functions that have pointer
 /// parameters, one specialization for each pointer argument's unique access chain 'shape' from a
diff --git a/src/tint/lang/wgsl/ast/transform/direct_variable_access_test.cc b/src/tint/lang/wgsl/ast/transform/direct_variable_access_test.cc
index 10f6421..04666b2 100644
--- a/src/tint/lang/wgsl/ast/transform/direct_variable_access_test.cc
+++ b/src/tint/lang/wgsl/ast/transform/direct_variable_access_test.cc
@@ -80,8 +80,6 @@
 
 TEST_F(DirectVariableAccessRemoveUncalledTest, PtrUniform) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<private> keep_me = 42;
 
 fn u(pre : i32, p : ptr<uniform, i32>, post : i32) -> i32 {
@@ -91,8 +89,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<private> keep_me = 42;
 )";
 
@@ -103,8 +99,6 @@
 
 TEST_F(DirectVariableAccessRemoveUncalledTest, PtrStorage) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<private> keep_me = 42;
 
 fn s(pre : i32, p : ptr<storage, i32>, post : i32) -> i32 {
@@ -113,8 +107,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<private> keep_me = 42;
 )";
 
@@ -125,8 +117,6 @@
 
 TEST_F(DirectVariableAccessRemoveUncalledTest, PtrWorkgroup) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<private> keep_me = 42;
 
 fn w(pre : i32, p : ptr<workgroup, i32>, post : i32) -> i32 {
@@ -136,8 +126,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<private> keep_me = 42;
 )";
 
@@ -148,8 +136,6 @@
 
 TEST_F(DirectVariableAccessRemoveUncalledTest, PtrPrivate_Disabled) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<private> keep_me = 42;
 
 fn f(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
@@ -166,8 +152,6 @@
 
 TEST_F(DirectVariableAccessRemoveUncalledTest, PtrPrivate_Enabled) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<private> keep_me = 42;
 )";
 
@@ -180,8 +164,6 @@
 
 TEST_F(DirectVariableAccessRemoveUncalledTest, PtrFunction_Disabled) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<private> keep_me = 42;
 
 fn f(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
@@ -198,8 +180,6 @@
 
 TEST_F(DirectVariableAccessRemoveUncalledTest, PtrFunction_Enabled) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<private> keep_me = 42;
 )";
 
@@ -221,8 +201,6 @@
 
 TEST_F(DirectVariableAccessPtrChainsTest, ConstantIndices) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<uniform> U : array<array<array<vec4<i32>, 8>, 8>, 8>;
 
 fn a(pre : i32, p : ptr<uniform, vec4<i32>>, post : i32) -> vec4<i32> {
@@ -251,8 +229,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<uniform> U : array<array<array<vec4<i32>, 8>, 8>, 8>;
 
 alias U_X_X_X = array<u32, 3u>;
@@ -289,8 +265,6 @@
 
 TEST_F(DirectVariableAccessPtrChainsTest, ConstantIndices_ViaPointerIndex) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<uniform> U : array<array<array<vec4<i32>, 8>, 8>, 8>;
 
 fn a(pre : i32, p : ptr<uniform, vec4<i32>>, post : i32) -> vec4<i32> {
@@ -319,8 +293,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<uniform> U : array<array<array<vec4<i32>, 8>, 8>, 8>;
 
 alias U_X_X_X = array<u32, 3u>;
@@ -357,8 +329,6 @@
 
 TEST_F(DirectVariableAccessPtrChainsTest, HoistIndices) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<uniform> U : array<array<array<vec4<i32>, 8>, 8>, 8>;
 
 var<private> i : i32;
@@ -399,8 +369,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<uniform> U : array<array<array<vec4<i32>, 8>, 8>, 8>;
 
 var<private> i : i32;
@@ -458,8 +426,6 @@
 
 TEST_F(DirectVariableAccessPtrChainsTest, HoistInForLoopInit) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<uniform> U : array<array<vec4<i32>, 8>, 8>;
 
 var<private> i : i32;
@@ -494,8 +460,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<uniform> U : array<array<vec4<i32>, 8>, 8>;
 
 var<private> i : i32;
@@ -558,8 +522,6 @@
 
 TEST_F(DirectVariableAccessPtrChainsTest, HoistInForLoopCond) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<uniform> U : array<array<vec4<i32>, 8>, 8>;
 
 var<private> i : i32;
@@ -596,8 +558,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<uniform> U : array<array<vec4<i32>, 8>, 8>;
 
 var<private> i : i32;
@@ -648,8 +608,6 @@
 
 TEST_F(DirectVariableAccessPtrChainsTest, HoistInForLoopCont) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<uniform> U : array<array<vec4<i32>, 8>, 8>;
 
 var<private> i : i32;
@@ -686,8 +644,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<uniform> U : array<array<vec4<i32>, 8>, 8>;
 
 var<private> i : i32;
@@ -738,8 +694,6 @@
 
 TEST_F(DirectVariableAccessPtrChainsTest, HoistInWhileCond) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<uniform> U : array<array<vec4<i32>, 8>, 8>;
 
 var<private> i : i32;
@@ -776,8 +730,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<uniform> U : array<array<vec4<i32>, 8>, 8>;
 
 var<private> i : i32;
@@ -837,8 +789,6 @@
 
 TEST_F(DirectVariableAccessUniformASTest, Param_ptr_i32_read) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<uniform> U : i32;
 
 fn a(pre : i32, p : ptr<uniform, i32>, post : i32) -> i32 {
@@ -851,8 +801,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<uniform> U : i32;
 
 fn a_U(pre : i32, post : i32) -> i32 {
@@ -871,8 +819,6 @@
 
 TEST_F(DirectVariableAccessUniformASTest, Param_ptr_vec4i32_Via_array_DynamicRead) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<uniform> U : array<vec4<i32>, 8>;
 
 fn a(pre : i32, p : ptr<uniform, vec4<i32>>, post : i32) -> vec4<i32> {
@@ -886,8 +832,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<uniform> U : array<vec4<i32>, 8>;
 
 alias U_X = array<u32, 1u>;
@@ -909,8 +853,6 @@
 
 TEST_F(DirectVariableAccessUniformASTest, Param_ptr_vec4i32_Via_array_DynamicRead_ViaPointerDot) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<uniform> U : array<vec4<i32>, 8>;
 
 fn a(pre : i32, p : ptr<uniform, vec4<i32>>, post : i32) -> vec4<i32> {
@@ -925,8 +867,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<uniform> U : array<vec4<i32>, 8>;
 
 alias U_X = array<u32, 1u>;
@@ -949,8 +889,6 @@
 
 TEST_F(DirectVariableAccessUniformASTest, CallChaining) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct Inner {
   mat : mat3x4<f32>,
 };
@@ -1011,8 +949,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct Inner {
   mat : mat3x4<f32>,
 }
@@ -1106,8 +1042,6 @@
 
 TEST_F(DirectVariableAccessUniformASTest, CallChaining_ViaPointerDotOrIndex) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct Inner {
   mat : mat3x4<f32>,
 };
@@ -1168,8 +1102,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct Inner {
   mat : mat3x4<f32>,
 }
@@ -1263,8 +1195,6 @@
 
 TEST_F(DirectVariableAccessUniformASTest, CallChaining2) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 alias T3 = vec4i;
 alias T2 = array<T3, 5>;
 alias T1 = array<T2, 5>;
@@ -1292,8 +1222,6 @@
 
     auto* expect =
         R"(
-enable chromium_experimental_full_ptr_parameters;
-
 alias T3 = vec4i;
 
 alias T2 = array<T3, 5>;
@@ -1342,8 +1270,6 @@
 
 TEST_F(DirectVariableAccessStorageASTest, Param_ptr_i32_Via_struct_read) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct str {
   i : i32,
 };
@@ -1360,8 +1286,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct str {
   i : i32,
 }
@@ -1384,8 +1308,6 @@
 
 TEST_F(DirectVariableAccessStorageASTest, Param_ptr_arr_i32_Via_struct_write) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct str {
   arr : array<i32, 4>,
 };
@@ -1402,8 +1324,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct str {
   arr : array<i32, 4>,
 }
@@ -1426,8 +1346,6 @@
 
 TEST_F(DirectVariableAccessStorageASTest, Param_ptr_vec4i32_Via_array_DynamicWrite) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage, read_write> S : array<vec4<i32>, 8>;
 
 fn a(pre : i32, p : ptr<storage, vec4<i32>, read_write>, post : i32) {
@@ -1441,8 +1359,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage, read_write> S : array<vec4<i32>, 8>;
 
 alias S_X = array<u32, 1u>;
@@ -1464,8 +1380,6 @@
 
 TEST_F(DirectVariableAccessStorageASTest, CallChaining) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct Inner {
   mat : mat3x4<f32>,
 };
@@ -1526,8 +1440,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct Inner {
   mat : mat3x4<f32>,
 }
@@ -1621,8 +1533,6 @@
 
 TEST_F(DirectVariableAccessStorageASTest, CallChaining_ViaPointerDotOrIndex) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct Inner {
   mat : mat3x4<f32>,
 };
@@ -1683,8 +1593,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct Inner {
   mat : mat3x4<f32>,
 }
@@ -1778,8 +1686,6 @@
 
 TEST_F(DirectVariableAccessStorageASTest, CallChaining2) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 alias T3 = vec4i;
 alias T2 = array<T3, 5>;
 alias T1 = array<T2, 5>;
@@ -1807,8 +1713,6 @@
 
     auto* expect =
         R"(
-enable chromium_experimental_full_ptr_parameters;
-
 alias T3 = vec4i;
 
 alias T2 = array<T3, 5>;
@@ -1857,8 +1761,6 @@
 
 TEST_F(DirectVariableAccessWorkgroupASTest, Param_ptr_vec4i32_Via_array_StaticRead) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<workgroup> W : array<vec4<i32>, 8>;
 
 fn a(pre : i32, p : ptr<workgroup, vec4<i32>>, post : i32) -> vec4<i32> {
@@ -1871,8 +1773,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<workgroup> W : array<vec4<i32>, 8>;
 
 alias W_X = array<u32, 1u>;
@@ -1893,8 +1793,6 @@
 
 TEST_F(DirectVariableAccessWorkgroupASTest, Param_ptr_vec4i32_Via_array_StaticWrite) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<workgroup> W : array<vec4<i32>, 8>;
 
 fn a(pre : i32, p : ptr<workgroup, vec4<i32>>, post : i32) {
@@ -1907,8 +1805,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<workgroup> W : array<vec4<i32>, 8>;
 
 alias W_X = array<u32, 1u>;
@@ -1929,8 +1825,6 @@
 
 TEST_F(DirectVariableAccessWorkgroupASTest, CallChaining) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct Inner {
   mat : mat3x4<f32>,
 };
@@ -1991,8 +1885,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct Inner {
   mat : mat3x4<f32>,
 }
@@ -2086,8 +1978,6 @@
 
 TEST_F(DirectVariableAccessWorkgroupASTest, CallChaining_ViaPointerDotOrIndex) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct Inner {
   mat : mat3x4<f32>,
 };
@@ -2148,8 +2038,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct Inner {
   mat : mat3x4<f32>,
 }
@@ -2243,8 +2131,6 @@
 
 TEST_F(DirectVariableAccessWorkgroupASTest, CallChaining2) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 alias T3 = vec4i;
 alias T2 = array<T3, 5>;
 alias T1 = array<T2, 5>;
@@ -2274,8 +2160,6 @@
 
     auto* expect =
         R"(
-enable chromium_experimental_full_ptr_parameters;
-
 alias T3 = vec4i;
 
 alias T2 = array<T3, 5>;
@@ -2327,8 +2211,6 @@
 
 TEST_F(DirectVariableAccessPrivateASTest, Enabled_Param_ptr_i32_read) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 fn a(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
   return *(p);
 }
@@ -2341,8 +2223,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 fn a_F(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
   return *(p);
 }
@@ -2361,8 +2241,6 @@
 
 TEST_F(DirectVariableAccessPrivateASTest, Enabled_Param_ptr_i32_write) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 fn a(pre : i32, p : ptr<private, i32>, post : i32) {
   *(p) = 42;
 }
@@ -2375,8 +2253,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 fn a_F(pre : i32, p : ptr<private, i32>, post : i32) {
   *(p) = 42;
 }
@@ -2395,8 +2271,6 @@
 
 TEST_F(DirectVariableAccessPrivateASTest, Enabled_Param_ptr_i32_Via_struct_read) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct str {
   i : i32,
 };
@@ -2413,8 +2287,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct str {
   i : i32,
 }
@@ -2437,8 +2309,6 @@
 
 TEST_F(DirectVariableAccessPrivateASTest, Disabled_Param_ptr_i32_Via_struct_read) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct str {
   i : i32,
 }
@@ -2463,8 +2333,6 @@
 
 TEST_F(DirectVariableAccessPrivateASTest, Enabled_Param_ptr_arr_i32_Via_struct_write) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct str {
   arr : array<i32, 4>,
 };
@@ -2481,8 +2349,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct str {
   arr : array<i32, 4>,
 }
@@ -2505,8 +2371,6 @@
 
 TEST_F(DirectVariableAccessPrivateASTest, Disabled_Param_ptr_arr_i32_Via_struct_write) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct str {
   arr : array<i32, 4>,
 }
@@ -2531,8 +2395,6 @@
 
 TEST_F(DirectVariableAccessPrivateASTest, Enabled_Param_ptr_i32_mixed) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct str {
   i : i32,
 };
@@ -2553,8 +2415,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct str {
   i : i32,
 }
@@ -2595,8 +2455,6 @@
 
 TEST_F(DirectVariableAccessPrivateASTest, Disabled_Param_ptr_i32_mixed) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct str {
   i : i32,
 }
@@ -2627,8 +2485,6 @@
 
 TEST_F(DirectVariableAccessPrivateASTest, Enabled_CallChaining) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct Inner {
   mat : mat3x4<f32>,
 };
@@ -2689,8 +2545,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct Inner {
   mat : mat3x4<f32>,
 }
@@ -2794,8 +2648,6 @@
 
 TEST_F(DirectVariableAccessPrivateASTest, Enabled_CallChaining2) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 alias T3 = vec4i;
 alias T2 = array<T3, 5>;
 alias T1 = array<T2, 5>;
@@ -2823,8 +2675,6 @@
 
     auto* expect =
         R"(
-enable chromium_experimental_full_ptr_parameters;
-
 alias T3 = vec4i;
 
 alias T2 = array<T3, 5>;
@@ -2868,8 +2718,6 @@
 
 TEST_F(DirectVariableAccessPrivateASTest, Disabled_CallChaining) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct Inner {
   mat : mat3x4<f32>,
 }
@@ -2943,8 +2791,6 @@
 
 TEST_F(DirectVariableAccessFunctionASTest, Enabled_LocalPtr) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 fn f() {
   var v : i32;
   let p : ptr<function, i32> = &(v);
@@ -2961,8 +2807,6 @@
 
 TEST_F(DirectVariableAccessFunctionASTest, Enabled_Param_ptr_i32_read) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 fn a(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
   return *(p);
 }
@@ -2974,8 +2818,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 fn a_F(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
   return *(p);
 }
@@ -2993,8 +2835,6 @@
 
 TEST_F(DirectVariableAccessFunctionASTest, Enabled_Param_ptr_i32_write) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 fn a(pre : i32, p : ptr<function, i32>, post : i32) {
   *(p) = 42;
 }
@@ -3006,8 +2846,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 fn a_F(pre : i32, p : ptr<function, i32>, post : i32) {
   *(p) = 42;
 }
@@ -3025,8 +2863,6 @@
 
 TEST_F(DirectVariableAccessFunctionASTest, Enabled_Param_ptr_i32_Via_struct_read) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct str {
   i : i32,
 };
@@ -3042,8 +2878,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct str {
   i : i32,
 }
@@ -3065,8 +2899,6 @@
 
 TEST_F(DirectVariableAccessFunctionASTest, Enabled_Param_ptr_arr_i32_Via_struct_write) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct str {
   arr : array<i32, 4>,
 };
@@ -3082,8 +2914,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct str {
   arr : array<i32, 4>,
 }
@@ -3105,8 +2935,6 @@
 
 TEST_F(DirectVariableAccessFunctionASTest, Enabled_Param_ptr_i32_mixed) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct str {
   i : i32,
 };
@@ -3127,8 +2955,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct str {
   i : i32,
 }
@@ -3166,8 +2992,6 @@
 
 TEST_F(DirectVariableAccessFunctionASTest, Enabled_CallChaining) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct Inner {
   mat : mat3x4<f32>,
 };
@@ -3219,8 +3043,6 @@
 
     auto* expect =
         R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct Inner {
   mat : mat3x4<f32>,
 }
@@ -3275,8 +3097,6 @@
 
 TEST_F(DirectVariableAccessFunctionASTest, Enabled_CallChaining2) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 alias T3 = vec4i;
 alias T2 = array<T3, 5>;
 alias T1 = array<T2, 5>;
@@ -3303,8 +3123,6 @@
 
     auto* expect =
         R"(
-enable chromium_experimental_full_ptr_parameters;
-
 alias T3 = vec4i;
 
 alias T2 = array<T3, 5>;
@@ -3347,8 +3165,6 @@
 
 TEST_F(DirectVariableAccessFunctionASTest, Disabled_Param_ptr_i32_Via_struct_read) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct str {
   i : i32,
 }
@@ -3372,8 +3188,6 @@
 
 TEST_F(DirectVariableAccessFunctionASTest, Disabled_Param_ptr_arr_i32_Via_struct_write) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct str {
   arr : array<i32, 4>,
 }
@@ -3406,8 +3220,6 @@
 
 TEST_F(DirectVariableAccessComplexTest, Param_ptr_mixed_vec4i32_ViaMultiple) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct str {
   i : vec4<i32>,
 };
@@ -3476,8 +3288,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct str {
   i : vec4<i32>,
 }
@@ -3606,8 +3416,6 @@
 
 TEST_F(DirectVariableAccessComplexTest, Indexing) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage> S : array<array<array<array<i32, 9>, 9>, 9>, 50>;
 
 fn a(i : i32) -> i32 { return i; }
@@ -3624,8 +3432,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage> S : array<array<array<array<i32, 9>, 9>, 9>, 50>;
 
 fn a(i : i32) -> i32 {
@@ -3650,8 +3456,6 @@
 
 TEST_F(DirectVariableAccessComplexTest, IndexingInPtrCall) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage> S : array<array<array<array<i32, 9>, 9>, 9>, 50>;
 
 fn a(pre : i32, i : ptr<storage, i32>, post : i32) -> i32 {
@@ -3670,8 +3474,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage> S : array<array<array<array<i32, 9>, 9>, 9>, 50>;
 
 alias S_X_X_X_X = array<u32, 4u>;
@@ -3698,8 +3500,6 @@
 
 TEST_F(DirectVariableAccessComplexTest, IndexingDualPointers) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage> S : array<array<array<i32, 9>, 9>, 50>;
 @group(0) @binding(0) var<uniform> U : array<array<array<vec4<i32>, 9>, 9>, 50>;
 
@@ -3717,8 +3517,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage> S : array<array<array<i32, 9>, 9>, 50>;
 
 @group(0) @binding(0) var<uniform> U : array<array<array<vec4<i32>, 9>, 9>, 50>;
diff --git a/src/tint/lang/wgsl/ast/transform/preserve_padding.cc b/src/tint/lang/wgsl/ast/transform/preserve_padding.cc
index 39ebb2d..d6935a9 100644
--- a/src/tint/lang/wgsl/ast/transform/preserve_padding.cc
+++ b/src/tint/lang/wgsl/ast/transform/preserve_padding.cc
@@ -62,32 +62,25 @@
         // Gather a list of assignments that need to be transformed.
         std::unordered_set<const AssignmentStatement*> assignments_to_transform;
         for (auto* node : ctx.src->ASTNodes().Objects()) {
-            Switch(
-                node,  //
-                [&](const AssignmentStatement* assign) {
-                    auto* ty = sem.GetVal(assign->lhs)->Type();
-                    if (assign->lhs->Is<PhonyExpression>()) {
-                        // Ignore phony assignment.
-                        return;
-                    }
-                    if (ty->As<core::type::Reference>()->AddressSpace() !=
-                        core::AddressSpace::kStorage) {
-                        // We only care about assignments that write to variables in the storage
-                        // address space, as nothing else is host-visible.
-                        return;
-                    }
-                    if (HasPadding(ty->UnwrapRef())) {
-                        // The assigned type has padding bytes, so we need to decompose the writes.
-                        assignments_to_transform.insert(assign);
-                    }
-                },
-                [&](const Enable* enable) {
-                    // Check if the full pointer parameters extension is already enabled.
-                    if (enable->HasExtension(
-                            wgsl::Extension::kChromiumExperimentalFullPtrParameters)) {
-                        ext_enabled = true;
-                    }
-                });
+            Switch(node,  //
+                   [&](const AssignmentStatement* assign) {
+                       auto* ty = sem.GetVal(assign->lhs)->Type();
+                       if (assign->lhs->Is<PhonyExpression>()) {
+                           // Ignore phony assignment.
+                           return;
+                       }
+                       if (ty->As<core::type::Reference>()->AddressSpace() !=
+                           core::AddressSpace::kStorage) {
+                           // We only care about assignments that write to variables in the storage
+                           // address space, as nothing else is host-visible.
+                           return;
+                       }
+                       if (HasPadding(ty->UnwrapRef())) {
+                           // The assigned type has padding bytes, so we need to decompose the
+                           // writes.
+                           assignments_to_transform.insert(assign);
+                       }
+                   });
         }
         if (assignments_to_transform.empty()) {
             return SkipTransform;
@@ -127,13 +120,9 @@
         //   }
         // It will be called by passing a pointer to the original LHS:
         //   assign_helper_T(&lhs, rhs);
-        //
-        // Since this requires passing pointers to the storage address space, this will also enable
-        // the chromium_experimental_full_ptr_parameters extension.
         const char* kDestParamName = "dest";
         const char* kValueParamName = "value";
         auto call_helper = [&](auto&& body) {
-            EnableExtension();
             auto helper = helpers.GetOrCreate(ty, [&] {
                 auto helper_name = b.Symbols().New("assign_and_preserve_padding");
                 tint::Vector<const Parameter*, 2> params = {
@@ -227,14 +216,6 @@
             [&](Default) { return false; });
     }
 
-    /// Enable the full pointer parameters extension, if we have not already done so.
-    void EnableExtension() {
-        if (!ext_enabled) {
-            b.Enable(wgsl::Extension::kChromiumExperimentalFullPtrParameters);
-            ext_enabled = true;
-        }
-    }
-
   private:
     /// The program builder
     ProgramBuilder b;
@@ -244,8 +225,6 @@
     const sem::Info& sem = ctx.src->Sem();
     /// Alias to the symbols in ctx.src
     const SymbolTable& sym = ctx.src->Symbols();
-    /// Flag to track whether we have already enabled the full pointer parameters extension.
-    bool ext_enabled = false;
     /// Map of semantic types to their assignment helper functions.
     Hashmap<const core::type::Type*, Symbol, 8> helpers;
 };
diff --git a/src/tint/lang/wgsl/ast/transform/preserve_padding_test.cc b/src/tint/lang/wgsl/ast/transform/preserve_padding_test.cc
index 469448b..be548fc 100644
--- a/src/tint/lang/wgsl/ast/transform/preserve_padding_test.cc
+++ b/src/tint/lang/wgsl/ast/transform/preserve_padding_test.cc
@@ -119,8 +119,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct S {
   a : u32,
   b : u32,
@@ -175,8 +173,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct S {
   a : u32,
   b : u32,
@@ -227,8 +223,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct S {
   a : u32,
   b : vec4<u32>,
@@ -267,8 +261,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct S {
   @size(16)
   a : u32,
@@ -307,8 +299,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct S {
   @size(16)
   a : u32,
@@ -362,8 +352,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct S1 {
   a1 : u32,
   b1 : vec3<u32>,
@@ -424,8 +412,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage, read_write> v : array<vec3<u32>, 4>;
 
 fn assign_and_preserve_padding(dest : ptr<storage, array<vec3<u32>, 4u>, read_write>, value : array<vec3<u32>, 4u>) {
@@ -458,8 +444,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 alias Array = array<array<vec3<u32>, 4>, 3>;
 
 @group(0) @binding(0) var<storage, read_write> v : Array;
@@ -503,8 +487,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct S {
   a : u32,
   b : array<vec3<u32>, 4>,
@@ -551,8 +533,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage, read_write> m : mat3x3<f32>;
 
 fn assign_and_preserve_padding(dest : ptr<storage, mat3x3<f32>, read_write>, value : mat3x3<f32>) {
@@ -588,8 +568,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 struct S {
   a : u32,
   m : mat3x3<f32>,
@@ -631,8 +609,6 @@
 )";
 
     auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage, read_write> arr_m : array<mat3x3<f32>, 4>;
 
 fn assign_and_preserve_padding_1(dest : ptr<storage, mat3x3<f32>, read_write>, value : mat3x3<f32>) {
@@ -676,47 +652,6 @@
     EXPECT_EQ(expect, str(got));
 }
 
-TEST_F(PreservePaddingTest, AvoidDuplicateEnables) {
-    auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
-struct S {
-  @size(16) a : u32,
-}
-
-@group(0) @binding(0) var<storage, read_write> v : S;
-
-@compute @workgroup_size(1)
-fn foo() {
-  v = S();
-}
-)";
-
-    auto* expect = R"(
-enable chromium_experimental_full_ptr_parameters;
-
-struct S {
-  @size(16)
-  a : u32,
-}
-
-@group(0) @binding(0) var<storage, read_write> v : S;
-
-fn assign_and_preserve_padding(dest : ptr<storage, S, read_write>, value : S) {
-  (*(dest)).a = value.a;
-}
-
-@compute @workgroup_size(1)
-fn foo() {
-  assign_and_preserve_padding(&(v), S());
-}
-)";
-
-    auto got = Run<PreservePadding>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
 TEST_F(PreservePaddingTest, NoModify_StructNoPadding) {
     auto* src = R"(
 struct S {
diff --git a/src/tint/lang/wgsl/ast/transform/robustness_test.cc b/src/tint/lang/wgsl/ast/transform/robustness_test.cc
index 647e669..1cd715f 100644
--- a/src/tint/lang/wgsl/ast/transform/robustness_test.cc
+++ b/src/tint/lang/wgsl/ast/transform/robustness_test.cc
@@ -4614,8 +4614,6 @@
 
 TEST_P(RobustnessTest, Read_PrivatePointerParameter_IndexWithConstant) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<private> a : array<i32, 4>;
 
 fn x(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
@@ -4635,8 +4633,6 @@
                           /* ignore */ src,
                           /* clamp */ src,
                           /* predicate */ R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<private> a : array<i32, 4>;
 
 fn x(pre : i32, p : ptr<private, i32>, p_predicate : bool, post : i32) -> i32 {
@@ -4663,8 +4659,6 @@
 
 TEST_P(RobustnessTest, Read_WorkgroupPointerParameter_IndexWithConstant) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<workgroup> a : array<i32, 4>;
 
 fn x(pre : i32, p : ptr<workgroup, i32>, post : i32) -> i32 {
@@ -4684,8 +4678,6 @@
                           /* ignore */ src,
                           /* clamp */ src,
                           /* predicate */ R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<workgroup> a : array<i32, 4>;
 
 fn x(pre : i32, p : ptr<workgroup, i32>, p_predicate : bool, post : i32) -> i32 {
@@ -4712,8 +4704,6 @@
 
 TEST_P(RobustnessTest, Read_UniformPointerParameter_IndexWithConstant) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<uniform> a : array<vec4i, 4>;
 
 fn x(pre : vec4i, p : ptr<uniform, vec4i>, post : vec4i) -> vec4i {
@@ -4733,8 +4723,6 @@
                           /* ignore */ src,
                           /* clamp */ src,
                           /* predicate */ R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<uniform> a : array<vec4i, 4>;
 
 fn x(pre : vec4i, p : ptr<uniform, vec4i>, p_predicate : bool, post : vec4i) -> vec4i {
@@ -4761,8 +4749,6 @@
 
 TEST_P(RobustnessTest, Read_StoragePointerParameter_IndexWithConstant) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage> a : array<vec4i, 4>;
 
 fn x(pre : vec4i, p : ptr<storage, vec4i>, post : vec4i) -> vec4i {
@@ -4782,8 +4768,6 @@
                           /* ignore */ src,
                           /* clamp */ src,
                           /* predicate */ R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage> a : array<vec4i, 4>;
 
 fn x(pre : vec4i, p : ptr<storage, vec4i>, p_predicate : bool, post : vec4i) -> vec4i {
@@ -4810,8 +4794,6 @@
 
 TEST_P(RobustnessTest, Read_FunctionPointerParameter_IndexWithConstant) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 fn x(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
   return ((pre + *(p)) + post);
 }
@@ -4830,8 +4812,6 @@
                           /* ignore */ src,
                           /* clamp */ src,
                           /* predicate */ R"(
-enable chromium_experimental_full_ptr_parameters;
-
 fn x(pre : i32, p : ptr<function, i32>, p_predicate : bool, post : i32) -> i32 {
   var predicated_expr : i32;
   if (p_predicate) {
@@ -4857,8 +4837,6 @@
 
 TEST_P(RobustnessTest, Read_PrivatePointerParameter_IndexWithLet) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<private> a : array<i32, 4>;
 
 fn x(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
@@ -4878,8 +4856,6 @@
     auto* expect = Expect(GetParam(),
                           /* ignore */ src,
                           /* clamp */ R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<private> a : array<i32, 4>;
 
 fn x(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
@@ -4896,8 +4872,6 @@
 }
 )",
                           /* predicate */ R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<private> a : array<i32, 4>;
 
 fn x(pre : i32, p : ptr<private, i32>, p_predicate : bool, post : i32) -> i32 {
@@ -4927,8 +4901,6 @@
 
 TEST_P(RobustnessTest, Read_WorkgroupPointerParameter_IndexWithLet) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<workgroup> a : array<i32, 4>;
 
 fn x(pre : i32, p : ptr<workgroup, i32>, post : i32) -> i32 {
@@ -4948,8 +4920,6 @@
     auto* expect = Expect(GetParam(),
                           /* ignore */ src,
                           /* clamp */ R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<workgroup> a : array<i32, 4>;
 
 fn x(pre : i32, p : ptr<workgroup, i32>, post : i32) -> i32 {
@@ -4966,8 +4936,6 @@
 }
 )",
                           /* predicate */ R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<workgroup> a : array<i32, 4>;
 
 fn x(pre : i32, p : ptr<workgroup, i32>, p_predicate : bool, post : i32) -> i32 {
@@ -4997,8 +4965,6 @@
 
 TEST_P(RobustnessTest, Read_UniformPointerParameter_IndexWithLet) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<uniform> a : array<vec4i, 4>;
 
 fn x(pre : vec4i, p : ptr<uniform, vec4i>, post : vec4i) -> vec4i {
@@ -5018,8 +4984,6 @@
     auto* expect = Expect(GetParam(),
                           /* ignore */ src,
                           /* clamp */ R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<uniform> a : array<vec4i, 4>;
 
 fn x(pre : vec4i, p : ptr<uniform, vec4i>, post : vec4i) -> vec4i {
@@ -5036,8 +5000,6 @@
 }
 )",
                           /* predicate */ R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<uniform> a : array<vec4i, 4>;
 
 fn x(pre : vec4i, p : ptr<uniform, vec4i>, p_predicate : bool, post : vec4i) -> vec4i {
@@ -5067,8 +5029,6 @@
 
 TEST_P(RobustnessTest, Read_StoragePointerParameter_IndexWithLet) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage> a : array<vec4i, 4>;
 
 fn x(pre : vec4i, p : ptr<storage, vec4i>, post : vec4i) -> vec4i {
@@ -5088,8 +5048,6 @@
     auto* expect = Expect(GetParam(),
                           /* ignore */ src,
                           /* clamp */ R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage> a : array<vec4i, 4>;
 
 fn x(pre : vec4i, p : ptr<storage, vec4i>, post : vec4i) -> vec4i {
@@ -5106,8 +5064,6 @@
 }
 )",
                           /* predicate */ R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage> a : array<vec4i, 4>;
 
 fn x(pre : vec4i, p : ptr<storage, vec4i>, p_predicate : bool, post : vec4i) -> vec4i {
@@ -5137,8 +5093,6 @@
 
 TEST_P(RobustnessTest, Read_FunctionPointerParameter_IndexWithLet) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 fn x(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
   return ((pre + *(p)) + post);
 }
@@ -5157,8 +5111,6 @@
     auto* expect = Expect(GetParam(),
                           /* ignore */ src,
                           /* clamp */ R"(
-enable chromium_experimental_full_ptr_parameters;
-
 fn x(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
   return ((pre + *(p)) + post);
 }
@@ -5174,8 +5126,6 @@
 }
 )",
                           /* predicate */ R"(
-enable chromium_experimental_full_ptr_parameters;
-
 fn x(pre : i32, p : ptr<function, i32>, p_predicate : bool, post : i32) -> i32 {
   var predicated_expr : i32;
   if (p_predicate) {
@@ -5204,8 +5154,6 @@
 
 TEST_P(RobustnessTest, Write_PrivatePointerParameter_IndexWithConstant) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<private> a : array<i32, 4>;
 
 fn x(pre : i32, p : ptr<private, i32>, post : i32) {
@@ -5225,8 +5173,6 @@
                           /* ignore */ src,
                           /* clamp */ src,
                           /* predicate */ R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<private> a : array<i32, 4>;
 
 fn x(pre : i32, p : ptr<private, i32>, p_predicate : bool, post : i32) {
@@ -5251,8 +5197,6 @@
 
 TEST_P(RobustnessTest, Write_WorkgroupPointerParameter_IndexWithConstant) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<workgroup> a : array<i32, 4>;
 
 fn x(pre : i32, p : ptr<workgroup, i32>, post : i32) {
@@ -5272,8 +5216,6 @@
                           /* ignore */ src,
                           /* clamp */ src,
                           /* predicate */ R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<workgroup> a : array<i32, 4>;
 
 fn x(pre : i32, p : ptr<workgroup, i32>, p_predicate : bool, post : i32) {
@@ -5298,8 +5240,6 @@
 
 TEST_P(RobustnessTest, Write_StoragePointerParameter_IndexWithConstant) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage, read_write> a : array<i32, 4>;
 
 fn x(pre : i32, p : ptr<storage, i32, read_write>, post : i32) {
@@ -5319,8 +5259,6 @@
                           /* ignore */ src,
                           /* clamp */ src,
                           /* predicate */ R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage, read_write> a : array<i32, 4>;
 
 fn x(pre : i32, p : ptr<storage, i32, read_write>, p_predicate : bool, post : i32) {
@@ -5345,8 +5283,6 @@
 
 TEST_P(RobustnessTest, Write_FunctionPointerParameter_IndexWithConstant) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 fn x(pre : i32, p : ptr<function, i32>, post : i32) {
   *(p) = (pre + post);
 }
@@ -5365,8 +5301,6 @@
                           /* ignore */ src,
                           /* clamp */ src,
                           /* predicate */ R"(
-enable chromium_experimental_full_ptr_parameters;
-
 fn x(pre : i32, p : ptr<function, i32>, p_predicate : bool, post : i32) {
   if (p_predicate) {
     *(p) = (pre + post);
@@ -5390,8 +5324,6 @@
 
 TEST_P(RobustnessTest, Write_PrivatePointerParameter_IndexWithLet) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<private> a : array<i32, 4>;
 
 fn x(pre : i32, p : ptr<private, i32>, post : i32) {
@@ -5411,8 +5343,6 @@
     auto* expect = Expect(GetParam(),
                           /* ignore */ src,
                           /* clamp */ R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<private> a : array<i32, 4>;
 
 fn x(pre : i32, p : ptr<private, i32>, post : i32) {
@@ -5429,8 +5359,6 @@
 }
 )",
                           /* predicate */ R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<private> a : array<i32, 4>;
 
 fn x(pre : i32, p : ptr<private, i32>, p_predicate : bool, post : i32) {
@@ -5458,8 +5386,6 @@
 
 TEST_P(RobustnessTest, Write_WorkgroupPointerParameter_IndexWithLet) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<workgroup> a : array<i32, 4>;
 
 fn x(pre : i32, p : ptr<workgroup, i32>, post : i32) {
@@ -5479,8 +5405,6 @@
     auto* expect = Expect(GetParam(),
                           /* ignore */ src,
                           /* clamp */ R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<workgroup> a : array<i32, 4>;
 
 fn x(pre : i32, p : ptr<workgroup, i32>, post : i32) {
@@ -5497,8 +5421,6 @@
 }
 )",
                           /* predicate */ R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<workgroup> a : array<i32, 4>;
 
 fn x(pre : i32, p : ptr<workgroup, i32>, p_predicate : bool, post : i32) {
@@ -5526,8 +5448,6 @@
 
 TEST_P(RobustnessTest, Write_StoragePointerParameter_IndexWithLet) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage, read_write> a : array<i32, 4>;
 
 fn x(pre : i32, p : ptr<storage, i32, read_write>, post : i32) {
@@ -5547,8 +5467,6 @@
     auto* expect = Expect(GetParam(),
                           /* ignore */ src,
                           /* clamp */ R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage, read_write> a : array<i32, 4>;
 
 fn x(pre : i32, p : ptr<storage, i32, read_write>, post : i32) {
@@ -5565,8 +5483,6 @@
 }
 )",
                           /* predicate */ R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage, read_write> a : array<i32, 4>;
 
 fn x(pre : i32, p : ptr<storage, i32, read_write>, p_predicate : bool, post : i32) {
@@ -5594,8 +5510,6 @@
 
 TEST_P(RobustnessTest, Write_FunctionPointerParameter_IndexWithLet) {
     auto* src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 fn x(pre : i32, p : ptr<function, i32>, post : i32) {
   *(p) = (pre + post);
 }
@@ -5614,8 +5528,6 @@
     auto* expect = Expect(GetParam(),
                           /* ignore */ src,
                           /* clamp */ R"(
-enable chromium_experimental_full_ptr_parameters;
-
 fn x(pre : i32, p : ptr<function, i32>, post : i32) {
   *(p) = (pre + post);
 }
@@ -5631,8 +5543,6 @@
 }
 )",
                           /* predicate */ R"(
-enable chromium_experimental_full_ptr_parameters;
-
 fn x(pre : i32, p : ptr<function, i32>, p_predicate : bool, post : i32) {
   if (p_predicate) {
     *(p) = (pre + post);
diff --git a/src/tint/lang/wgsl/extension.cc b/src/tint/lang/wgsl/extension.cc
index c2f1762..3069ccf 100644
--- a/src/tint/lang/wgsl/extension.cc
+++ b/src/tint/lang/wgsl/extension.cc
@@ -48,9 +48,6 @@
     if (str == "chromium_experimental_framebuffer_fetch") {
         return Extension::kChromiumExperimentalFramebufferFetch;
     }
-    if (str == "chromium_experimental_full_ptr_parameters") {
-        return Extension::kChromiumExperimentalFullPtrParameters;
-    }
     if (str == "chromium_experimental_pixel_local") {
         return Extension::kChromiumExperimentalPixelLocal;
     }
@@ -80,8 +77,6 @@
             return "chromium_disable_uniformity_analysis";
         case Extension::kChromiumExperimentalFramebufferFetch:
             return "chromium_experimental_framebuffer_fetch";
-        case Extension::kChromiumExperimentalFullPtrParameters:
-            return "chromium_experimental_full_ptr_parameters";
         case Extension::kChromiumExperimentalPixelLocal:
             return "chromium_experimental_pixel_local";
         case Extension::kChromiumExperimentalPushConstant:
diff --git a/src/tint/lang/wgsl/extension.h b/src/tint/lang/wgsl/extension.h
index 41edcba..7de7e82 100644
--- a/src/tint/lang/wgsl/extension.h
+++ b/src/tint/lang/wgsl/extension.h
@@ -48,7 +48,6 @@
     kUndefined,
     kChromiumDisableUniformityAnalysis,
     kChromiumExperimentalFramebufferFetch,
-    kChromiumExperimentalFullPtrParameters,
     kChromiumExperimentalPixelLocal,
     kChromiumExperimentalPushConstant,
     kChromiumExperimentalSubgroups,
@@ -75,22 +74,16 @@
 Extension ParseExtension(std::string_view str);
 
 constexpr std::string_view kExtensionStrings[] = {
-    "chromium_disable_uniformity_analysis",
-    "chromium_experimental_framebuffer_fetch",
-    "chromium_experimental_full_ptr_parameters",
-    "chromium_experimental_pixel_local",
-    "chromium_experimental_push_constant",
-    "chromium_experimental_subgroups",
-    "chromium_internal_dual_source_blending",
-    "chromium_internal_relaxed_uniform_layout",
-    "f16",
+    "chromium_disable_uniformity_analysis",     "chromium_experimental_framebuffer_fetch",
+    "chromium_experimental_pixel_local",        "chromium_experimental_push_constant",
+    "chromium_experimental_subgroups",          "chromium_internal_dual_source_blending",
+    "chromium_internal_relaxed_uniform_layout", "f16",
 };
 
 /// All extensions
 static constexpr Extension kAllExtensions[] = {
     Extension::kChromiumDisableUniformityAnalysis,
     Extension::kChromiumExperimentalFramebufferFetch,
-    Extension::kChromiumExperimentalFullPtrParameters,
     Extension::kChromiumExperimentalPixelLocal,
     Extension::kChromiumExperimentalPushConstant,
     Extension::kChromiumExperimentalSubgroups,
diff --git a/src/tint/lang/wgsl/extension_bench.cc b/src/tint/lang/wgsl/extension_bench.cc
index c2e6bd2..70c0d73 100644
--- a/src/tint/lang/wgsl/extension_bench.cc
+++ b/src/tint/lang/wgsl/extension_bench.cc
@@ -59,55 +59,48 @@
         "chromium_experimental_vramebuffeii_fetch",
         "chro8WWum_experimental_framebuffer_fetch",
         "chromium_eperimenxxMl_framebuffer_fetch",
-        "chromium_expeggimeXtal_full_ptr_paraeters",
-        "chromium_expVrimental_full_ptr_puraXeer",
-        "chromium_experimental_full_ptr3parameters",
-        "chromium_experimental_full_ptr_parameters",
-        "chromium_experimentalEfull_ptr_parameters",
-        "chromium_experimentalfull_ptr_PPaTTameters",
-        "chromium_ddxperimental_fullptrxxparameters",
-        "chromium_experi44ental_pixel_local",
-        "chromium_experimental_VVSixel_local",
-        "chroRium_experimental_pix22Rlocal",
+        "chromum_experimental_pixeX_loggal",
+        "chromium_expVrXmntal_ixel_local",
+        "3hromium_experimental_pixel_local",
         "chromium_experimental_pixel_local",
-        "chromiuF_experiment9lpixel_local",
-        "chromium_experimental_pixel_loca",
-        "Vhromium_expeOOimentalHpixRRl_lcal",
-        "chromiym_experimental_push_contant",
-        "nnhro77ium_experimenGal_push_conrrllant",
-        "chromium_experimental_push_c4nstan00",
+        "chromium_eEperimental_pixel_local",
+        "chTTomiu_experimentaPP_pixel_local",
+        "cxxromium_expddrimenal_pixel_local",
+        "c44romium_experimental_push_constant",
+        "chromium_experimental_pSSsVV_constant",
+        "chrom22Rm_experimental_pushRonstant",
         "chromium_experimental_push_constant",
-        "chooomum_experimental_ush_constat",
-        "chromium_xperimntal_zzush_constant",
-        "chromi11m_experimepptal_psh_ciistant",
-        "chromium_experimental_subgroXXps",
-        "chromium55eIIperimental_subgnno99ps",
-        "chraamiuSS_experimentaHHr_subgrouYs",
+        "chromium_exp9rimFntal_ush_constant",
+        "chrmium_experimental_push_constant",
+        "cOOromium_experiVeHtal_puh_conRRtant",
+        "chromium_eperimental_sybgroups",
+        "chrorri77mGexperimllntal_subgrnnups",
+        "chromium_exp4rimen00al_subgroups",
         "chromium_experimental_subgroups",
-        "chkkomium_eperimntal_subgroup",
-        "jhromium_experRmental_subgogps",
-        "chromiubexperiental_subgroups",
-        "chromium_internal_dujl_source_blending",
-        "chromium_intenal_dual_source_blending",
-        "chqomium_internal_dual_source_beding",
+        "chromium_exprimenal_ubgrouoos",
+        "chrmiumexperimenzzal_subgroups",
+        "chrmi11m_experppmeiita_subgroups",
+        "chromium_internal_dual_sourcXX_blending",
+        "chromium_internal_dual_99ou5IInce_blending",
+        "crrroSSium_internYlaadual_source_bHHending",
         "chromium_internal_dual_source_blending",
-        "chroium_NNnternal_dual_source_blending",
-        "chovvium_internal_dual_source_lending",
-        "chromium_internQQl_dual_sorce_blending",
-        "chromirm_intenal_rfflaxed_unifrm_layout",
-        "chromium_internal_jelaxed_uniform_layout",
-        "chromium_interna_relNNxed_uwwiform_lay82t",
+        "chromium_internakk_ualsourc_blendHng",
+        "chromium_inRRrnal_dujl_sourceblgnding",
+        "chromiuminternal_duab_source_blendin",
+        "chromium_internal_relaxed_uniform_lajout",
+        "chromium_internal_relxed_uniform_layout",
+        "chroium_inqernal_rlaxed_uniform_layout",
         "chromium_internal_relaxed_uniform_layout",
-        "chromium_internal_relaxed_uniform_layut",
-        "chromium_internal_relaxed_rrniform_layout",
-        "chromium_internal_relaxedGuniform_layout",
-        "FF16",
-        "",
-        "rr1",
+        "chromium_internNNl_relaxed_uniform_layou",
+        "chromium_internal_relaxvvd_unifom_laout",
+        "chromium_internalrelaxed_uniQQorm_layout",
+        "ff",
+        "fj6",
+        "wNN2",
         "f16",
-        "1",
-        "DJ1",
-        "",
+        "f6",
+        "rr16",
+        "fG6",
     };
     for (auto _ : state) {
         for (auto* str : kStrings) {
diff --git a/src/tint/lang/wgsl/extension_test.cc b/src/tint/lang/wgsl/extension_test.cc
index 08cda9b..8bcbd0c 100644
--- a/src/tint/lang/wgsl/extension_test.cc
+++ b/src/tint/lang/wgsl/extension_test.cc
@@ -59,8 +59,6 @@
 static constexpr Case kValidCases[] = {
     {"chromium_disable_uniformity_analysis", Extension::kChromiumDisableUniformityAnalysis},
     {"chromium_experimental_framebuffer_fetch", Extension::kChromiumExperimentalFramebufferFetch},
-    {"chromium_experimental_full_ptr_parameters",
-     Extension::kChromiumExperimentalFullPtrParameters},
     {"chromium_experimental_pixel_local", Extension::kChromiumExperimentalPixelLocal},
     {"chromium_experimental_push_constant", Extension::kChromiumExperimentalPushConstant},
     {"chromium_experimental_subgroups", Extension::kChromiumExperimentalSubgroups},
@@ -76,27 +74,24 @@
     {"chromium_experimental_framebuf1er_fetch", Extension::kUndefined},
     {"chromium_experiqqntal_framebuffer_fetch", Extension::kUndefined},
     {"chromium_experimental_framebuffll77_fetch", Extension::kUndefined},
-    {"chroium_experimental_full_ptr_paqqppmetHHrs", Extension::kUndefined},
-    {"chrium_evperiental_full_ptr_paraceters", Extension::kUndefined},
-    {"chromium_expGimental_fullbptr_parameters", Extension::kUndefined},
-    {"vhromium_experimental_pixel_liical", Extension::kUndefined},
-    {"chromium_experiment8l_pixel_lWWcal", Extension::kUndefined},
-    {"chromium_expeimentMl_xxixel_local", Extension::kUndefined},
-    {"chrXmium_experimeggtal_ush_constant", Extension::kUndefined},
-    {"chromiu_experVmentalpusX_constant", Extension::kUndefined},
-    {"chro3ium_experimental_push_constant", Extension::kUndefined},
-    {"cEromium_experimental_subgroups", Extension::kUndefined},
-    {"TThromium_experiPPental_sugroups", Extension::kUndefined},
-    {"chddomium_experimental_subgroxxs", Extension::kUndefined},
-    {"chromium_internal_44ual_source_blending", Extension::kUndefined},
-    {"chromium_inteSSnal_dual_source_blendinVV", Extension::kUndefined},
-    {"chromiuR_interna22dual_source_blenRing", Extension::kUndefined},
-    {"chromium_int9rnal_relaxed_Fnifor_layout", Extension::kUndefined},
-    {"chrmium_internal_relaxed_uniform_layout", Extension::kUndefined},
-    {"VRhHomium_internal_relaxd_uniform_OOayout", Extension::kUndefined},
-    {"y1", Extension::kUndefined},
-    {"l77rrn6", Extension::kUndefined},
-    {"4016", Extension::kUndefined},
+    {"chroqqppum_expermental_pixel_HHocal", Extension::kUndefined},
+    {"chromium_experimenta_piel_occl", Extension::kUndefined},
+    {"chromium_exeGimental_pixel_local", Extension::kUndefined},
+    {"chvomium_experimental_push_constiint", Extension::kUndefined},
+    {"chromiu8WWexperimental_push_constant", Extension::kUndefined},
+    {"chromium_experiMental_push_costanxx", Extension::kUndefined},
+    {"chromiuX_experimentl_sugggroups", Extension::kUndefined},
+    {"chromiu_exuerimntal_XVbgroups", Extension::kUndefined},
+    {"chromium_experimen3al_subgroups", Extension::kUndefined},
+    {"chromium_internal_dual_soErce_blending", Extension::kUndefined},
+    {"chromiuPP_internal_dual_sourceblenTTing", Extension::kUndefined},
+    {"chromim_internadd_dual_sxxurce_blending", Extension::kUndefined},
+    {"chromium_interna44_relaxed_uniform_layout", Extension::kUndefined},
+    {"chromium_internal_relaxed_uniformSSlayouVV", Extension::kUndefined},
+    {"chromiumRnteRnal_re22axed_uniform_layout", Extension::kUndefined},
+    {"96", Extension::kUndefined},
+    {"f1", Extension::kUndefined},
+    {"VOR6", Extension::kUndefined},
 };
 
 using ExtensionParseTest = testing::TestWithParam<Case>;
diff --git a/src/tint/lang/wgsl/features/language_feature.cc b/src/tint/lang/wgsl/features/language_feature.cc
index 4561eb0..f0391dc 100644
--- a/src/tint/lang/wgsl/features/language_feature.cc
+++ b/src/tint/lang/wgsl/features/language_feature.cc
@@ -66,6 +66,9 @@
     if (str == "readonly_and_readwrite_storage_textures") {
         return LanguageFeature::kReadonlyAndReadwriteStorageTextures;
     }
+    if (str == "unrestricted_pointer_parameters") {
+        return LanguageFeature::kUnrestrictedPointerParameters;
+    }
     return LanguageFeature::kUndefined;
 }
 
@@ -89,6 +92,8 @@
             return "pointer_composite_access";
         case LanguageFeature::kReadonlyAndReadwriteStorageTextures:
             return "readonly_and_readwrite_storage_textures";
+        case LanguageFeature::kUnrestrictedPointerParameters:
+            return "unrestricted_pointer_parameters";
     }
     return "<unknown>";
 }
diff --git a/src/tint/lang/wgsl/features/language_feature.h b/src/tint/lang/wgsl/features/language_feature.h
index aa7315b..b7aedbb 100644
--- a/src/tint/lang/wgsl/features/language_feature.h
+++ b/src/tint/lang/wgsl/features/language_feature.h
@@ -54,6 +54,7 @@
     kPacked4X8IntegerDotProduct,
     kPointerCompositeAccess,
     kReadonlyAndReadwriteStorageTextures,
+    kUnrestrictedPointerParameters,
 };
 
 /// @param value the enum value
@@ -74,6 +75,7 @@
     "packed_4x8_integer_dot_product",
     "pointer_composite_access",
     "readonly_and_readwrite_storage_textures",
+    "unrestricted_pointer_parameters",
 };
 
 /// All features
@@ -86,6 +88,7 @@
     LanguageFeature::kPacked4X8IntegerDotProduct,
     LanguageFeature::kPointerCompositeAccess,
     LanguageFeature::kReadonlyAndReadwriteStorageTextures,
+    LanguageFeature::kUnrestrictedPointerParameters,
 };
 
 }  // namespace tint::wgsl
diff --git a/src/tint/lang/wgsl/features/status.cc b/src/tint/lang/wgsl/features/status.cc
index f8297ea..2fe2f02 100644
--- a/src/tint/lang/wgsl/features/status.cc
+++ b/src/tint/lang/wgsl/features/status.cc
@@ -33,9 +33,10 @@
 
 FeatureStatus GetLanguageFeatureStatus(LanguageFeature f) {
     switch (f) {
-        case LanguageFeature::kReadonlyAndReadwriteStorageTextures:
         case LanguageFeature::kPacked4X8IntegerDotProduct:
         case LanguageFeature::kPointerCompositeAccess:
+        case LanguageFeature::kReadonlyAndReadwriteStorageTextures:
+        case LanguageFeature::kUnrestrictedPointerParameters:
             return FeatureStatus::kExperimental;
         case LanguageFeature::kUndefined:
             return FeatureStatus::kUnknown;
diff --git a/src/tint/lang/wgsl/helpers/append_vector_test.cc b/src/tint/lang/wgsl/helpers/append_vector_test.cc
index f1a95d9..26ebba4 100644
--- a/src/tint/lang/wgsl/helpers/append_vector_test.cc
+++ b/src/tint/lang/wgsl/helpers/append_vector_test.cc
@@ -49,7 +49,7 @@
     auto* vec_12 = Call<vec2<i32>>(scalar_1, scalar_2);
     WrapInFunction(vec_12, scalar_3);
 
-    resolver::Resolver resolver(this, {});
+    resolver::Resolver resolver{this, wgsl::AllowedFeatures{}};
     EXPECT_TRUE(resolver.Resolve());
     ASSERT_THAT(resolver.Diagnostics(), testing::IsEmpty());
 
@@ -90,7 +90,7 @@
     auto* vec_12 = Call<vec2<i32>>(scalar_1, scalar_2);
     WrapInFunction(vec_12, scalar_3);
 
-    resolver::Resolver resolver(this, {});
+    resolver::Resolver resolver{this, wgsl::AllowedFeatures{}};
     EXPECT_TRUE(resolver.Resolve());
     ASSERT_THAT(resolver.Diagnostics(), testing::IsEmpty());
 
@@ -138,7 +138,7 @@
     auto* vec_12 = Call<vec2<i32>>(uvec_12);
     WrapInFunction(vec_12, scalar_3);
 
-    resolver::Resolver resolver(this, {});
+    resolver::Resolver resolver{this, wgsl::AllowedFeatures{}};
     EXPECT_TRUE(resolver.Resolve());
     ASSERT_THAT(resolver.Diagnostics(), testing::IsEmpty());
 
@@ -187,7 +187,7 @@
     auto* vec_12 = Call<vec2<i32>>(scalar_1, scalar_2);
     WrapInFunction(vec_12, scalar_3);
 
-    resolver::Resolver resolver(this, {});
+    resolver::Resolver resolver{this, wgsl::AllowedFeatures{}};
     EXPECT_TRUE(resolver.Resolve());
     ASSERT_THAT(resolver.Diagnostics(), testing::IsEmpty());
 
@@ -233,7 +233,7 @@
     auto* vec_123 = Call<vec3<i32>>(scalar_1, scalar_2, scalar_3);
     WrapInFunction(vec_123, scalar_4);
 
-    resolver::Resolver resolver(this, {});
+    resolver::Resolver resolver{this, wgsl::AllowedFeatures{}};
     EXPECT_TRUE(resolver.Resolve());
     ASSERT_THAT(resolver.Diagnostics(), testing::IsEmpty());
 
@@ -276,7 +276,7 @@
     auto* scalar_3 = Expr(3_i);
     WrapInFunction(vec_12, scalar_3);
 
-    resolver::Resolver resolver(this, {});
+    resolver::Resolver resolver{this, wgsl::AllowedFeatures{}};
     EXPECT_TRUE(resolver.Resolve());
     ASSERT_THAT(resolver.Diagnostics(), testing::IsEmpty());
 
@@ -315,7 +315,7 @@
     auto* vec_12 = Call<vec2<i32>>(scalar_1, scalar_2);
     WrapInFunction(vec_12, scalar_3);
 
-    resolver::Resolver resolver(this, {});
+    resolver::Resolver resolver{this, wgsl::AllowedFeatures{}};
     EXPECT_TRUE(resolver.Resolve());
     ASSERT_THAT(resolver.Diagnostics(), testing::IsEmpty());
 
@@ -356,7 +356,7 @@
     auto* scalar_3 = Expr("scalar_3");
     WrapInFunction(vec_12, scalar_3);
 
-    resolver::Resolver resolver(this, {});
+    resolver::Resolver resolver{this, wgsl::AllowedFeatures{}};
     EXPECT_TRUE(resolver.Resolve());
     ASSERT_THAT(resolver.Diagnostics(), testing::IsEmpty());
 
@@ -394,7 +394,7 @@
     auto* scalar_3 = Expr("scalar_3");
     WrapInFunction(vec_12, scalar_3);
 
-    resolver::Resolver resolver(this, {});
+    resolver::Resolver resolver{this, wgsl::AllowedFeatures{}};
     EXPECT_TRUE(resolver.Resolve());
     ASSERT_THAT(resolver.Diagnostics(), testing::IsEmpty());
 
@@ -436,7 +436,7 @@
     auto* scalar_3 = Expr("scalar_3");
     WrapInFunction(vec_12, scalar_3);
 
-    resolver::Resolver resolver(this, {});
+    resolver::Resolver resolver{this, wgsl::AllowedFeatures{}};
     EXPECT_TRUE(resolver.Resolve());
     ASSERT_THAT(resolver.Diagnostics(), testing::IsEmpty());
 
@@ -472,7 +472,7 @@
     auto* vec000 = Call<vec3<i32>>();
     WrapInFunction(vec000, scalar);
 
-    resolver::Resolver resolver(this, {});
+    resolver::Resolver resolver{this, wgsl::AllowedFeatures{}};
     EXPECT_TRUE(resolver.Resolve());
     ASSERT_THAT(resolver.Diagnostics(), testing::IsEmpty());
 
diff --git a/src/tint/lang/wgsl/language_feature_test.cc b/src/tint/lang/wgsl/language_feature_test.cc
index 991f5db..dc3b564 100644
--- a/src/tint/lang/wgsl/language_feature_test.cc
+++ b/src/tint/lang/wgsl/language_feature_test.cc
@@ -67,6 +67,7 @@
     {"pointer_composite_access", LanguageFeature::kPointerCompositeAccess},
     {"readonly_and_readwrite_storage_textures",
      LanguageFeature::kReadonlyAndReadwriteStorageTextures},
+    {"unrestricted_pointer_parameters", LanguageFeature::kUnrestrictedPointerParameters},
 };
 
 static constexpr Case kInvalidCases[] = {
@@ -94,6 +95,9 @@
     {"readonlF_and_readwrite_st9rage_textues", LanguageFeature::kUndefined},
     {"readonly_and_radwrite_storage_textures", LanguageFeature::kUndefined},
     {"readonly_and_readwrite_sOOrage_tVxRRures", LanguageFeature::kUndefined},
+    {"unrestrictd_pointer_payameters", LanguageFeature::kUndefined},
+    {"unrerrt77iGted_poillter_paramenners", LanguageFeature::kUndefined},
+    {"unrestricted4point00r_parameters", LanguageFeature::kUndefined},
 };
 
 using LanguageFeatureParseTest = testing::TestWithParam<Case>;
diff --git a/src/tint/lang/wgsl/reader/parser/enable_directive_test.cc b/src/tint/lang/wgsl/reader/parser/enable_directive_test.cc
index 0af6015..6f689b3 100644
--- a/src/tint/lang/wgsl/reader/parser/enable_directive_test.cc
+++ b/src/tint/lang/wgsl/reader/parser/enable_directive_test.cc
@@ -205,7 +205,7 @@
     // Error when unknown extension found
     EXPECT_TRUE(p->has_error());
     EXPECT_EQ(p->error(), R"(1:8: expected extension
-Possible values: 'chromium_disable_uniformity_analysis', 'chromium_experimental_framebuffer_fetch', 'chromium_experimental_full_ptr_parameters', 'chromium_experimental_pixel_local', 'chromium_experimental_push_constant', 'chromium_experimental_subgroups', 'chromium_internal_dual_source_blending', 'chromium_internal_relaxed_uniform_layout', 'f16')");
+Possible values: 'chromium_disable_uniformity_analysis', 'chromium_experimental_framebuffer_fetch', 'chromium_experimental_pixel_local', 'chromium_experimental_push_constant', 'chromium_experimental_subgroups', 'chromium_internal_dual_source_blending', 'chromium_internal_relaxed_uniform_layout', 'f16')");
     auto program = p->program();
     auto& ast = program.AST();
     EXPECT_EQ(ast.Enables().Length(), 0u);
diff --git a/src/tint/lang/wgsl/resolver/builtin_enum_test.cc b/src/tint/lang/wgsl/resolver/builtin_enum_test.cc
index cc7bbeb..14e266d 100644
--- a/src/tint/lang/wgsl/resolver/builtin_enum_test.cc
+++ b/src/tint/lang/wgsl/resolver/builtin_enum_test.cc
@@ -70,7 +70,6 @@
 TEST_P(ResolverAddressSpaceUsedWithTemplateArgs, Test) {
     // fn f(p : ptr<ADDRESS_SPACE<T>, f32) {}
 
-    Enable(wgsl::Extension::kChromiumExperimentalFullPtrParameters);
     auto* tmpl = Ident(Source{{12, 34}}, GetParam(), "T");
     Func("f", Vector{Param("p", ty("ptr", tmpl, ty.f32()))}, ty.void_(), tint::Empty);
     EXPECT_FALSE(r()->Resolve());
diff --git a/src/tint/lang/wgsl/resolver/builtin_validation_test.cc b/src/tint/lang/wgsl/resolver/builtin_validation_test.cc
index 3bae4eb..638a54c 100644
--- a/src/tint/lang/wgsl/resolver/builtin_validation_test.cc
+++ b/src/tint/lang/wgsl/resolver/builtin_validation_test.cc
@@ -568,7 +568,7 @@
                          Vector{Expr(1_u), Expr(2_u)})),
          });
 
-    auto resolver = Resolver(this, {});
+    Resolver resolver{this, wgsl::AllowedFeatures{}};
     EXPECT_FALSE(resolver.Resolve());
     EXPECT_EQ(resolver.error(),
               "12:34 error: built-in function 'dot4I8Packed' requires the "
@@ -597,7 +597,7 @@
                          Vector{Expr(1_u), Expr(2_u)})),
          });
 
-    auto resolver = Resolver(this, {});
+    Resolver resolver{this, wgsl::AllowedFeatures{}};
     EXPECT_FALSE(resolver.Resolve());
     EXPECT_EQ(resolver.error(),
               "12:34 error: built-in function 'dot4U8Packed' requires the "
@@ -624,7 +624,7 @@
              Return(Call(Source{Source::Location{12, 34}}, "pack4xI8", Call<vec4<i32>>())),
          });
 
-    auto resolver = Resolver(this, {});
+    Resolver resolver{this, wgsl::AllowedFeatures{}};
     EXPECT_FALSE(resolver.Resolve());
     EXPECT_EQ(resolver.error(),
               "12:34 error: built-in function 'pack4xI8' requires the "
@@ -651,7 +651,7 @@
              Return(Call(Source{Source::Location{12, 34}}, "pack4xU8", Call<vec4<u32>>())),
          });
 
-    auto resolver = Resolver(this, {});
+    Resolver resolver{this, wgsl::AllowedFeatures{}};
     EXPECT_FALSE(resolver.Resolve());
     EXPECT_EQ(resolver.error(),
               "12:34 error: built-in function 'pack4xU8' requires the "
@@ -678,7 +678,7 @@
              Return(Call(Source{Source::Location{12, 34}}, "pack4xI8Clamp", Call<vec4<i32>>())),
          });
 
-    auto resolver = Resolver(this, {});
+    Resolver resolver{this, wgsl::AllowedFeatures{}};
     EXPECT_FALSE(resolver.Resolve());
     EXPECT_EQ(resolver.error(),
               "12:34 error: built-in function 'pack4xI8Clamp' requires the "
@@ -705,7 +705,7 @@
              Return(Call(Source{Source::Location{12, 34}}, "pack4xU8Clamp", Call<vec4<u32>>())),
          });
 
-    auto resolver = Resolver(this, {});
+    Resolver resolver{this, wgsl::AllowedFeatures{}};
     EXPECT_FALSE(resolver.Resolve());
     EXPECT_EQ(resolver.error(),
               "12:34 error: built-in function 'pack4xU8Clamp' requires the "
@@ -732,7 +732,7 @@
              Return(Call(Source{Source::Location{12, 34}}, "unpack4xI8", Call<u32>())),
          });
 
-    auto resolver = Resolver(this, {});
+    Resolver resolver{this, wgsl::AllowedFeatures{}};
     EXPECT_FALSE(resolver.Resolve());
     EXPECT_EQ(resolver.error(),
               "12:34 error: built-in function 'unpack4xI8' requires the "
@@ -759,7 +759,7 @@
              Return(Call(Source{Source::Location{12, 34}}, "unpack4xU8", Call<u32>())),
          });
 
-    auto resolver = Resolver(this, {});
+    Resolver resolver{this, wgsl::AllowedFeatures{}};
     EXPECT_FALSE(resolver.Resolve());
     EXPECT_EQ(resolver.error(),
               "12:34 error: built-in function 'unpack4xU8' requires the "
@@ -988,7 +988,7 @@
              CallStmt(Call(Source{Source::Location{12, 34}}, "textureBarrier")),
          });
 
-    auto resolver = Resolver(this, {});
+    Resolver resolver{this, wgsl::AllowedFeatures{}};
     EXPECT_FALSE(resolver.Resolve());
     EXPECT_EQ(resolver.error(),
               "12:34 error: built-in function 'textureBarrier' requires the "
diff --git a/src/tint/lang/wgsl/resolver/call_validation_test.cc b/src/tint/lang/wgsl/resolver/call_validation_test.cc
index 47da663..9a0d7c1 100644
--- a/src/tint/lang/wgsl/resolver/call_validation_test.cc
+++ b/src/tint/lang/wgsl/resolver/call_validation_test.cc
@@ -146,7 +146,8 @@
     EXPECT_EQ(r()->error(), "12:34 error: cannot take the address of expression");
 }
 
-TEST_F(ResolverCallValidationTest, PointerArgument_AddressOfFunctionMember) {
+TEST_F(ResolverCallValidationTest,
+       PointerArgument_AddressOfFunctionMember_WithoutUnrestrictedPointerParameters) {
     // struct S { m: i32; };
     // fn foo(p: ptr<function, i32>) {}
     // fn main() {
@@ -164,22 +165,23 @@
              CallStmt(Call("foo", AddressOf(Source{{12, 34}}, MemberAccessor("v", "m")))),
          });
 
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              "12:34 error: arguments of pointer type must not point to a subset of the "
-              "originating variable");
+    Resolver r{this, wgsl::AllowedFeatures{}};
+
+    EXPECT_FALSE(r.Resolve());
+    EXPECT_EQ(
+        r.error(),
+        R"(12:34 error: arguments of pointer type must not point to a subset of the originating variable)");
 }
 
 TEST_F(ResolverCallValidationTest,
-       PointerArgument_AddressOfFunctionMember_WithFullPtrParametersExt) {
-    // enable chromium_experimental_full_ptr_parameters;
+       PointerArgument_AddressOfFunctionMember_WithUnrestrictedPointerParameters) {
     // struct S { m: i32; };
     // fn foo(p: ptr<function, i32>) {}
     // fn main() {
     //   var v : S;
     //   foo(&v.m);
     // }
-    Enable(wgsl::Extension::kChromiumExperimentalFullPtrParameters);
+
     auto* S = Structure("S", Vector{
                                  Member("m", ty.i32()),
                              });
@@ -322,7 +324,7 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverCallValidationTest, LetPointer_NotWholeVar) {
+TEST_F(ResolverCallValidationTest, LetPointer_NotWholeVar_WithoutUnrestrictedPointerParameters) {
     // fn foo(p : ptr<function, i32>) {}
     // @fragment
     // fn main() {
@@ -344,14 +346,14 @@
          Vector{
              Stage(ast::PipelineStage::kFragment),
          });
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
+    Resolver r(this, wgsl::AllowedFeatures{});
+    EXPECT_FALSE(r.Resolve());
+    EXPECT_EQ(r.error(),
               "12:34 error: arguments of pointer type must not point to a subset of the "
               "originating variable");
 }
 
-TEST_F(ResolverCallValidationTest, LetPointer_NotWholeVar_WithFullPtrParametersExt) {
-    // enable chromium_experimental_full_ptr_parameters;
+TEST_F(ResolverCallValidationTest, LetPointer_NotWholeVar_WithUnrestrictedPointerParameters) {
     // fn foo(p : ptr<function, i32>) {}
     // @fragment
     // fn main() {
@@ -359,7 +361,7 @@
     //   let p: ptr<function, i32> = &(v[0]);
     //   x(p);
     // }
-    Enable(wgsl::Extension::kChromiumExperimentalFullPtrParameters);
+
     Func("foo",
          Vector{
              Param("p", ty.ptr<function, i32>()),
@@ -406,7 +408,8 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverCallValidationTest, ComplexPointerChain_NotWholeVar) {
+TEST_F(ResolverCallValidationTest,
+       ComplexPointerChain_NotWholeVar_WithoutUnrestrictedPointerParameters) {
     // fn foo(p : ptr<function, i32>) {}
     // @fragment
     // fn main() {
@@ -432,14 +435,15 @@
          Vector{
              Stage(ast::PipelineStage::kFragment),
          });
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
+    Resolver r{this, {}};
+    EXPECT_FALSE(r.Resolve());
+    EXPECT_EQ(r.error(),
               "12:34 error: arguments of pointer type must not point to a subset of the "
               "originating variable");
 }
 
-TEST_F(ResolverCallValidationTest, ComplexPointerChain_NotWholeVar_WithFullPtrParametersExt) {
-    // enable chromium_experimental_full_ptr_parameters;
+TEST_F(ResolverCallValidationTest,
+       ComplexPointerChain_NotWholeVar_WithUnrestrictedPointerParameters) {
     // fn foo(p : ptr<function, i32>) {}
     // @fragment
     // fn main() {
@@ -449,7 +453,7 @@
     //   let p3 = &(*p2)[0];
     //   foo(&*p);
     // }
-    Enable(wgsl::Extension::kChromiumExperimentalFullPtrParameters);
+
     Func("foo",
          Vector{
              Param("p", ty.ptr<function, i32>()),
diff --git a/src/tint/lang/wgsl/resolver/expression_kind_test.cc b/src/tint/lang/wgsl/resolver/expression_kind_test.cc
index ff4d507..94c91c8 100644
--- a/src/tint/lang/wgsl/resolver/expression_kind_test.cc
+++ b/src/tint/lang/wgsl/resolver/expression_kind_test.cc
@@ -309,7 +309,6 @@
             GlobalVar("v", ty("texture_storage_2d", "rgba8unorm", expr), Group(0_u), Binding(0_u));
             break;
         case Use::kAddressSpace:
-            Enable(wgsl::Extension::kChromiumExperimentalFullPtrParameters);
             Func(Symbols().New(), Vector{Param("p", ty("ptr", expr, ty.f32()))}, ty.void_(),
                  tint::Empty);
             break;
diff --git a/src/tint/lang/wgsl/resolver/function_validation_test.cc b/src/tint/lang/wgsl/resolver/function_validation_test.cc
index fd015f7..2a01da4 100644
--- a/src/tint/lang/wgsl/resolver/function_validation_test.cc
+++ b/src/tint/lang/wgsl/resolver/function_validation_test.cc
@@ -1047,7 +1047,7 @@
 
 enum class Expectation {
     kAlwaysPass,
-    kPassWithFullPtrParameterExtension,
+    kPassWithUnrestrictedPointerParameters,
     kAlwaysFail,
     kInvalid,
 };
@@ -1059,7 +1059,11 @@
 struct TestWithParams : ResolverTestWithParam<TestParams> {};
 
 using ResolverFunctionParameterValidationTest = TestWithParams;
-TEST_P(ResolverFunctionParameterValidationTest, AddressSpaceNoExtension) {
+TEST_P(ResolverFunctionParameterValidationTest, AddressSpaceWithoutUnrestrictedPointerParameters) {
+    auto features = wgsl::AllowedFeatures::Everything();
+    features.features.erase(wgsl::LanguageFeature::kUnrestrictedPointerParameters);
+    Resolver r(this, features);
+
     auto& param = GetParam();
     Structure("S", Vector{Member("a", ty.i32())});
     auto ptr_type = ty("ptr", Ident(Source{{12, 34}}, param.address_space), ty("S"));
@@ -1071,29 +1075,27 @@
     }
 
     if (param.expectation == Expectation::kAlwaysPass) {
-        ASSERT_TRUE(r()->Resolve()) << r()->error();
+        ASSERT_TRUE(r.Resolve()) << r.error();
     } else {
         StringStream ss;
         ss << param.address_space;
-        EXPECT_FALSE(r()->Resolve());
+        EXPECT_FALSE(r.Resolve());
         if (param.expectation == Expectation::kInvalid) {
             std::string err = R"(12:34 error: unresolved address space '${addr_space}'
 12:34 note: Possible values: 'function', 'pixel_local', 'private', 'push_constant', 'storage', 'uniform', 'workgroup')";
             err = tint::ReplaceAll(err, "${addr_space}", tint::ToString(param.address_space));
-            EXPECT_EQ(r()->error(), err);
+            EXPECT_EQ(r.error(), err);
         } else {
-            EXPECT_EQ(r()->error(),
-                      "12:34 error: function parameter of pointer type cannot be in '" +
-                          tint::ToString(param.address_space) + "' address space");
+            EXPECT_EQ(r.error(), "12:34 error: function parameter of pointer type cannot be in '" +
+                                     tint::ToString(param.address_space) + "' address space");
         }
     }
 }
-TEST_P(ResolverFunctionParameterValidationTest, AddressSpaceWithFullPtrParameterExtension) {
+TEST_P(ResolverFunctionParameterValidationTest, AddressSpaceWithUnrestrictedPointerParameters) {
     auto& param = GetParam();
     Structure("S", Vector{Member("a", ty.i32())});
     auto ptr_type = ty("ptr", Ident(Source{{12, 34}}, param.address_space), ty("S"));
     auto* arg = Param(Source{{12, 34}}, "p", ptr_type);
-    Enable(wgsl::Extension::kChromiumExperimentalFullPtrParameters);
     Func("f", Vector{arg}, ty.void_(), tint::Empty);
 
     if (param.address_space == core::AddressSpace::kPixelLocal) {
@@ -1101,7 +1103,7 @@
     }
 
     if (param.expectation == Expectation::kAlwaysPass ||
-        param.expectation == Expectation::kPassWithFullPtrParameterExtension) {
+        param.expectation == Expectation::kPassWithUnrestrictedPointerParameters) {
         ASSERT_TRUE(r()->Resolve()) << r()->error();
     } else {
         EXPECT_FALSE(r()->Resolve());
@@ -1120,17 +1122,19 @@
 INSTANTIATE_TEST_SUITE_P(
     ResolverTest,
     ResolverFunctionParameterValidationTest,
-    testing::Values(
-        TestParams{core::AddressSpace::kUndefined, Expectation::kInvalid},
-        TestParams{core::AddressSpace::kIn, Expectation::kAlwaysFail},
-        TestParams{core::AddressSpace::kOut, Expectation::kAlwaysFail},
-        TestParams{core::AddressSpace::kUniform, Expectation::kPassWithFullPtrParameterExtension},
-        TestParams{core::AddressSpace::kWorkgroup, Expectation::kPassWithFullPtrParameterExtension},
-        TestParams{core::AddressSpace::kHandle, Expectation::kInvalid},
-        TestParams{core::AddressSpace::kStorage, Expectation::kPassWithFullPtrParameterExtension},
-        TestParams{core::AddressSpace::kPixelLocal, Expectation::kAlwaysFail},
-        TestParams{core::AddressSpace::kPrivate, Expectation::kAlwaysPass},
-        TestParams{core::AddressSpace::kFunction, Expectation::kAlwaysPass}));
+    testing::Values(TestParams{core::AddressSpace::kUndefined, Expectation::kInvalid},
+                    TestParams{core::AddressSpace::kIn, Expectation::kAlwaysFail},
+                    TestParams{core::AddressSpace::kOut, Expectation::kAlwaysFail},
+                    TestParams{core::AddressSpace::kUniform,
+                               Expectation::kPassWithUnrestrictedPointerParameters},
+                    TestParams{core::AddressSpace::kWorkgroup,
+                               Expectation::kPassWithUnrestrictedPointerParameters},
+                    TestParams{core::AddressSpace::kHandle, Expectation::kInvalid},
+                    TestParams{core::AddressSpace::kStorage,
+                               Expectation::kPassWithUnrestrictedPointerParameters},
+                    TestParams{core::AddressSpace::kPixelLocal, Expectation::kAlwaysFail},
+                    TestParams{core::AddressSpace::kPrivate, Expectation::kAlwaysPass},
+                    TestParams{core::AddressSpace::kFunction, Expectation::kAlwaysPass}));
 
 }  // namespace
 }  // namespace tint::resolver
diff --git a/src/tint/lang/wgsl/resolver/type_validation_test.cc b/src/tint/lang/wgsl/resolver/type_validation_test.cc
index e5fcf1c..0264afc 100644
--- a/src/tint/lang/wgsl/resolver/type_validation_test.cc
+++ b/src/tint/lang/wgsl/resolver/type_validation_test.cc
@@ -1217,7 +1217,7 @@
 
     GlobalVar("a", st, Group(0_a), Binding(0_a));
 
-    auto resolver = Resolver(this, {});
+    auto resolver = Resolver{this, wgsl::AllowedFeatures{}};
     EXPECT_FALSE(resolver.Resolve());
     EXPECT_EQ(resolver.error(),
               "12:34 error: read-only storage textures require the "
@@ -1246,7 +1246,7 @@
 
     GlobalVar("a", st, Group(0_a), Binding(0_a));
 
-    auto resolver = Resolver(this, {});
+    Resolver resolver{this, wgsl::AllowedFeatures{}};
     EXPECT_FALSE(resolver.Resolve());
     EXPECT_EQ(resolver.error(),
               "12:34 error: read-write storage textures require the "
diff --git a/src/tint/lang/wgsl/resolver/uniformity_test.cc b/src/tint/lang/wgsl/resolver/uniformity_test.cc
index cbab7db..4674237 100644
--- a/src/tint/lang/wgsl/resolver/uniformity_test.cc
+++ b/src/tint/lang/wgsl/resolver/uniformity_test.cc
@@ -3788,8 +3788,6 @@
 
 TEST_F(UniformityAnalysisTest, LoadNonUniformGlobalThroughPointerParameter) {
     std::string src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage, read_write> non_uniform : i32;
 
 fn bar(p : ptr<storage, i32, read_write>) {
@@ -3805,15 +3803,15 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:8:5 error: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:6:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
-test:7:3 note: control flow depends on possibly non-uniform value
+test:5:3 note: control flow depends on possibly non-uniform value
   if (*p == 0) {
   ^^
 
-test:7:8 note: parameter 'p' of 'bar' may be non-uniform
+test:5:8 note: parameter 'p' of 'bar' may be non-uniform
   if (*p == 0) {
        ^
 )");
@@ -3821,8 +3819,6 @@
 
 TEST_F(UniformityAnalysisTest, LoadNonUniformGlobalThroughPointerParameter_ViaReturnValue) {
     std::string src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage, read_write> non_uniform : i32;
 
 fn bar(p : ptr<storage, i32, read_write>) -> i32 {
@@ -3838,15 +3834,15 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:12:5 error: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:10:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
-test:11:3 note: control flow depends on possibly non-uniform value
+test:9:3 note: control flow depends on possibly non-uniform value
   if (0 == bar(&non_uniform)) {
   ^^
 
-test:11:12 note: return value of 'bar' may be non-uniform
+test:9:12 note: return value of 'bar' may be non-uniform
   if (0 == bar(&non_uniform)) {
            ^^^^^^^^^^^^^^^^^
 )");
@@ -4011,8 +4007,6 @@
 
 TEST_F(UniformityAnalysisTest, LoadUniformThroughNonUniformPointer_ViaParameter) {
     std::string src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage, read_write> non_uniform : i32;
 
 fn bar(p : ptr<function, array<i32, 4>>) {
@@ -4033,15 +4027,15 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:10:5 error: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:8:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
-test:9:3 note: control flow depends on possibly non-uniform value
+test:7:3 note: control flow depends on possibly non-uniform value
   if (*local_p == 0) {
   ^^
 
-test:8:23 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+test:6:23 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
   let local_p = &(*p)[non_uniform];
                       ^^^^^^^^^^^
 )");
@@ -4049,8 +4043,6 @@
 
 TEST_F(UniformityAnalysisTest, LoadUniformThroughNonUniformPointer_ViaParameterChain) {
     std::string src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage, read_write> non_uniform : i32;
 
 fn zoo(p : ptr<function, i32>) {
@@ -4074,31 +4066,31 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:8:5 error: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:6:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
-test:7:3 note: control flow depends on possibly non-uniform value
+test:5:3 note: control flow depends on possibly non-uniform value
   if (*p == 0) {
   ^^
 
-test:7:8 note: parameter 'p' of 'zoo' may be non-uniform
+test:5:8 note: parameter 'p' of 'zoo' may be non-uniform
   if (*p == 0) {
        ^
 
-test:13:7 note: possibly non-uniform value passed via pointer here
+test:11:7 note: possibly non-uniform value passed via pointer here
   zoo(p);
       ^
 
-test:12:8 note: reading from 'p' may result in a non-uniform value
+test:10:8 note: reading from 'p' may result in a non-uniform value
 fn bar(p : ptr<function, i32>) {
        ^
 
-test:21:7 note: possibly non-uniform value passed via pointer here
+test:19:7 note: possibly non-uniform value passed via pointer here
   bar(p);
       ^
 
-test:20:14 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+test:18:14 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
   let p = &v[non_uniform];
              ^^^^^^^^^^^
 )");
@@ -4138,8 +4130,6 @@
 
 TEST_F(UniformityAnalysisTest, LoadNonUniformThroughUniformPointer_ViaParameter) {
     std::string src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage, read_write> non_uniform : i32;
 @group(0) @binding(1) var<storage, read> uniform_idx : i32;
 
@@ -4164,31 +4154,31 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:9:5 error: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:7:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
-test:8:3 note: control flow depends on possibly non-uniform value
+test:6:3 note: control flow depends on possibly non-uniform value
   if (*p == 0) {
   ^^
 
-test:8:8 note: parameter 'p' of 'zoo' may be non-uniform
+test:6:8 note: parameter 'p' of 'zoo' may be non-uniform
   if (*p == 0) {
        ^
 
-test:14:7 note: possibly non-uniform value passed via pointer here
+test:12:7 note: possibly non-uniform value passed via pointer here
   zoo(p);
       ^
 
-test:13:8 note: reading from 'p' may result in a non-uniform value
+test:11:8 note: reading from 'p' may result in a non-uniform value
 fn bar(p : ptr<function, i32>) {
        ^
 
-test:22:7 note: possibly non-uniform value passed via pointer here
+test:20:7 note: possibly non-uniform value passed via pointer here
   bar(p);
       ^
 
-test:19:34 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+test:17:34 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
   var v = array<i32, 4>(0, 0, 0, non_uniform);
                                  ^^^^^^^^^^^
 )");
@@ -5355,8 +5345,6 @@
 
 TEST_F(UniformityAnalysisTest, AssignUniformToPrivatePointerParameter_StillNonUniform) {
     std::string src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<private> non_uniform : i32;
 
 fn bar(p : ptr<private, i32>) {
@@ -5373,15 +5361,15 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:9:5 error: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:7:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
-test:8:3 note: control flow depends on possibly non-uniform value
+test:6:3 note: control flow depends on possibly non-uniform value
   if (*p == 0) {
   ^^
 
-test:8:8 note: parameter 'p' of 'bar' may be non-uniform
+test:6:8 note: parameter 'p' of 'bar' may be non-uniform
   if (*p == 0) {
        ^
 )");
@@ -5389,8 +5377,6 @@
 
 TEST_F(UniformityAnalysisTest, AssignUniformToWorkgroupPointerParameter_StillNonUniform) {
     std::string src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<workgroup> non_uniform : i32;
 
 fn bar(p : ptr<workgroup, i32>) {
@@ -5407,15 +5393,15 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:9:5 error: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:7:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
-test:8:3 note: control flow depends on possibly non-uniform value
+test:6:3 note: control flow depends on possibly non-uniform value
   if (*p == 0) {
   ^^
 
-test:8:8 note: parameter 'p' of 'bar' may be non-uniform
+test:6:8 note: parameter 'p' of 'bar' may be non-uniform
   if (*p == 0) {
        ^
 )");
@@ -5423,8 +5409,6 @@
 
 TEST_F(UniformityAnalysisTest, AssignUniformToStoragePointerParameter_StillNonUniform) {
     std::string src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage, read_write> non_uniform : i32;
 
 fn bar(p : ptr<storage, i32, read_write>) {
@@ -5441,15 +5425,15 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:9:5 error: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:7:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
-test:8:3 note: control flow depends on possibly non-uniform value
+test:6:3 note: control flow depends on possibly non-uniform value
   if (*p == 0) {
   ^^
 
-test:8:8 note: parameter 'p' of 'bar' may be non-uniform
+test:6:8 note: parameter 'p' of 'bar' may be non-uniform
   if (*p == 0) {
        ^
 )");
@@ -5457,8 +5441,6 @@
 
 TEST_F(UniformityAnalysisTest, LoadFromReadOnlyStoragePointerParameter_AlwaysUniform) {
     std::string src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage, read> non_uniform : i32;
 
 fn bar(p : ptr<storage, i32, read>) {
@@ -7799,8 +7781,6 @@
 TEST_F(UniformityAnalysisTest,
        ArrayElement_AssignUniformToElementWithNonUniformIndex_ViaPartialPointerParameter) {
     std::string src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 var<private> non_uniform : i32;
 
 fn bar(p : ptr<function, i32>) {
@@ -7819,15 +7799,15 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:15:5 error: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:13:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
-test:14:3 note: control flow depends on possibly non-uniform value
+test:12:3 note: control flow depends on possibly non-uniform value
   if (arr[0] == 0) {
   ^^
 
-test:12:17 note: reading from module-scope private variable 'non_uniform' may result in a non-uniform value
+test:10:17 note: reading from module-scope private variable 'non_uniform' may result in a non-uniform value
   let p = &(arr[non_uniform]);
                 ^^^^^^^^^^^
 )");
@@ -8663,8 +8643,6 @@
 
 TEST_F(UniformityAnalysisTest, ArrayLength_OnPtrArg) {
     std::string src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage, read_write> arr : array<f32>;
 
 fn bar(p : ptr<storage, array<f32>, read_write>) {
@@ -8683,8 +8661,6 @@
 
 TEST_F(UniformityAnalysisTest, ArrayLength_PtrArgRequiredToBeUniformForRetval_Pass) {
     std::string src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage, read_write> arr : array<f32>;
 
 fn length(p : ptr<storage, array<f32>, read_write>) -> u32 {
@@ -8704,8 +8680,6 @@
 // TODO(jrprice): This test requires variable pointers.
 TEST_F(UniformityAnalysisTest, DISABLED_ArrayLength_PtrArgRequiredToBeUniformForRetval_Fail) {
     std::string src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage, read_write> non_uniform : i32;
 @group(0) @binding(1) var<storage, read_write> arr1 : array<f32>;
 @group(0) @binding(2) var<storage, read_write> arr2 : array<f32>;
@@ -8741,8 +8715,6 @@
 
 TEST_F(UniformityAnalysisTest, ArrayLength_PtrArgRequiredToBeUniformForOtherPtrResult_Pass) {
     std::string src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage, read_write> arr : array<f32>;
 
 fn length(p : ptr<storage, array<f32>, read_write>, out : ptr<function, u32>) {
@@ -8765,8 +8737,6 @@
 TEST_F(UniformityAnalysisTest,
        DISABLED_ArrayLength_PtrArgRequiredToBeUniformForOtherPtrResult_Fail) {
     std::string src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage, read_write> non_uniform : i32;
 @group(0) @binding(1) var<storage, read_write> arr1 : array<f32>;
 @group(0) @binding(2) var<storage, read_write> arr2 : array<f32>;
@@ -8805,8 +8775,6 @@
     // Test that a single pointer argument can directly require uniformity as well as affecting the
     // uniformity of the return value.
     std::string src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 @group(0) @binding(0) var<storage, read_write> arr : array<u32>;
 
 fn bar(p : ptr<storage, array<u32>, read_write>) -> u32 {
@@ -8831,15 +8799,15 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:21:5 error: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:19:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
-test:19:3 note: control flow depends on possibly non-uniform value
+test:17:3 note: control flow depends on possibly non-uniform value
   if (0 == bar(p)) {
   ^^
 
-test:19:12 note: return value of 'bar' may be non-uniform
+test:17:12 note: return value of 'bar' may be non-uniform
   if (0 == bar(p)) {
            ^^^^^^
 )");
@@ -8864,8 +8832,6 @@
 
 TEST_F(UniformityAnalysisTest, WorkgroupUniformLoad_ViaPtrArg) {
     std::string src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 const wgsize = 4;
 var<workgroup> data : array<u32, wgsize>;
 
@@ -8913,8 +8879,6 @@
 
 TEST_F(UniformityAnalysisTest, WorkgroupUniformLoad_NonUniformPtr_ViaPtrArg) {
     std::string src = R"(
-enable chromium_experimental_full_ptr_parameters;
-
 const wgsize = 4;
 var<workgroup> data : array<u32, wgsize>;
 
@@ -8933,19 +8897,19 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:8:31 error: possibly non-uniform value passed here
+              R"(test:6:31 error: possibly non-uniform value passed here
   return workgroupUniformLoad(p);
                               ^
 
-test:8:31 note: parameter 'p' of 'foo' may be non-uniform
+test:6:31 note: parameter 'p' of 'foo' may be non-uniform
   return workgroupUniformLoad(p);
                               ^
 
-test:14:11 note: possibly non-uniform value passed here
+test:12:11 note: possibly non-uniform value passed here
   if (foo(&data[idx]) > 0) {
           ^^^^^^^^^^
 
-test:14:17 note: builtin 'idx' of 'main' may be non-uniform
+test:12:17 note: builtin 'idx' of 'main' may be non-uniform
   if (foo(&data[idx]) > 0) {
                 ^^^
 )");
diff --git a/src/tint/lang/wgsl/resolver/validator.cc b/src/tint/lang/wgsl/resolver/validator.cc
index 106d739..e6f7c31 100644
--- a/src/tint/lang/wgsl/resolver/validator.cc
+++ b/src/tint/lang/wgsl/resolver/validator.cc
@@ -845,8 +845,8 @@
                 case core::AddressSpace::kStorage:
                 case core::AddressSpace::kUniform:
                 case core::AddressSpace::kWorkgroup:
-                    ok = enabled_extensions_.Contains(
-                        wgsl::Extension::kChromiumExperimentalFullPtrParameters);
+                    ok = allowed_features_.features.count(
+                             wgsl::LanguageFeature::kUnrestrictedPointerParameters) != 0;
                     break;
                 default:
                     break;
@@ -1912,8 +1912,8 @@
         }
 
         if (param_type->Is<core::type::Pointer>() &&
-            !enabled_extensions_.Contains(
-                wgsl::Extension::kChromiumExperimentalFullPtrParameters)) {
+            !allowed_features_.features.count(
+                wgsl::LanguageFeature::kUnrestrictedPointerParameters)) {
             // https://gpuweb.github.io/gpuweb/wgsl/#function-restriction
             // Each argument of pointer type to a user-defined function must have the same memory
             // view as its root identifier.
diff --git a/src/tint/lang/wgsl/wgsl.def b/src/tint/lang/wgsl/wgsl.def
index 41cd202..e23de8a 100644
--- a/src/tint/lang/wgsl/wgsl.def
+++ b/src/tint/lang/wgsl/wgsl.def
@@ -72,9 +72,6 @@
   chromium_disable_uniformity_analysis
   // A Chromium-specific extension for push constants
   chromium_experimental_push_constant
-  // A Chromium-specific extension that enables passing of uniform, storage and workgroup
-  // address-spaced pointers as parameters, as well as pointers into sub-objects.
-  chromium_experimental_full_ptr_parameters
   // A Chromium-specific extension that adds basic subgroup functionality.
   chromium_experimental_subgroups
   // A Chromium-specific extension that relaxes memory layout requirements for uniform storage.
@@ -91,9 +88,10 @@
 
 // https://gpuweb.github.io/gpuweb/wgsl/#language-extensions-sec
 enum language_feature {
-  readonly_and_readwrite_storage_textures
   packed_4x8_integer_dot_product
   pointer_composite_access
+  readonly_and_readwrite_storage_textures
+  unrestricted_pointer_parameters
 
   // Language features used only for testing whose status will never change.
   chromium_testing_unimplemented
diff --git a/src/tint/lang/wgsl/writer/ir_to_program/ir_to_program.cc b/src/tint/lang/wgsl/writer/ir_to_program/ir_to_program.cc
index 47b6468..712fab3 100644
--- a/src/tint/lang/wgsl/writer/ir_to_program/ir_to_program.cc
+++ b/src/tint/lang/wgsl/writer/ir_to_program/ir_to_program.cc
@@ -267,9 +267,6 @@
                 attrs.Push(b.Invariant());
             }
 
-            if (ParamRequiresFullPtrParameters(param->Type())) {
-                Enable(wgsl::Extension::kChromiumExperimentalFullPtrParameters);
-            }
             return b.Param(name, ty, std::move(attrs));
         });
 
@@ -686,12 +683,6 @@
         tint::Switch(
             call,  //
             [&](const core::ir::UserCall* c) {
-                for (auto* arg : call->Args()) {
-                    if (ArgRequiresFullPtrParameters(arg)) {
-                        Enable(wgsl::Extension::kChromiumExperimentalFullPtrParameters);
-                        break;
-                    }
-                }
                 auto* expr = b.Call(NameFor(c->Target()), std::move(args));
                 if (call->Results().IsEmpty() || !call->Result(0)->IsUsed()) {
                     Append(b.CallStmt(expr));
@@ -1310,44 +1301,6 @@
                 return false;
         }
     }
-
-    /// @returns true if a parameter of the type @p ty requires the
-    /// kChromiumExperimentalFullPtrParameters extension to be enabled.
-    bool ParamRequiresFullPtrParameters(const core::type::Type* ty) {
-        if (auto* ptr = ty->As<core::type::Pointer>()) {
-            switch (ptr->AddressSpace()) {
-                case core::AddressSpace::kUniform:
-                case core::AddressSpace::kStorage:
-                case core::AddressSpace::kWorkgroup:
-                    return true;
-                default:
-                    return false;
-            }
-        }
-        return false;
-    }
-
-    /// @returns true if the argument @p arg requires the kChromiumExperimentalFullPtrParameters
-    /// extension to be enabled.
-    bool ArgRequiresFullPtrParameters(const core::ir::Value* arg) {
-        if (!arg->Type()->Is<core::type::Pointer>()) {
-            return false;
-        }
-
-        auto res = arg->As<core::ir::InstructionResult>();
-        while (res) {
-            auto* inst = res->Instruction();
-            if (inst->Is<core::ir::Access>()) {
-                return true;  // Passing pointer into sub-object
-            }
-            if (auto* let = inst->As<core::ir::Let>()) {
-                res = let->Value()->As<core::ir::InstructionResult>();
-            } else {
-                break;
-            }
-        }
-        return false;
-    }
 };
 
 }  // namespace
diff --git a/src/tint/lang/wgsl/writer/ir_to_program/ir_to_program_test.cc b/src/tint/lang/wgsl/writer/ir_to_program/ir_to_program_test.cc
index cd77dba..b3119c7 100644
--- a/src/tint/lang/wgsl/writer/ir_to_program/ir_to_program_test.cc
+++ b/src/tint/lang/wgsl/writer/ir_to_program/ir_to_program_test.cc
@@ -3212,105 +3212,6 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-// chromium_experimental_full_ptr_parameters
-////////////////////////////////////////////////////////////////////////////////
-TEST_F(IRToProgramTest, Enable_ChromiumExperimentalFullPtrParameters_StoragePtrParameter) {
-    auto* fn = b.Function("f", ty.void_());
-    fn->SetParams({b.FunctionParam("p", ty.ptr<storage, i32>())});
-
-    b.Append(fn->Block(), [&] { b.Return(fn); });
-
-    EXPECT_WGSL(R"(
-enable chromium_experimental_full_ptr_parameters;
-
-fn f(p : ptr<storage, i32, read_write>) {
-}
-)");
-}
-
-TEST_F(IRToProgramTest, Enable_ChromiumExperimentalFullPtrParameters_UniformPtrParameter) {
-    auto* fn = b.Function("f", ty.void_());
-    fn->SetParams({b.FunctionParam("p", ty.ptr<uniform, i32>())});
-
-    b.Append(fn->Block(), [&] { b.Return(fn); });
-
-    EXPECT_WGSL(R"(
-enable chromium_experimental_full_ptr_parameters;
-
-fn f(p : ptr<uniform, i32>) {
-}
-)");
-}
-
-TEST_F(IRToProgramTest, Enable_ChromiumExperimentalFullPtrParameters_WorkgroupPtrParameter) {
-    auto* fn = b.Function("f", ty.void_());
-    fn->SetParams({b.FunctionParam("p", ty.ptr<workgroup, i32>())});
-
-    b.Append(fn->Block(), [&] { b.Return(fn); });
-
-    EXPECT_WGSL(R"(
-enable chromium_experimental_full_ptr_parameters;
-
-fn f(p : ptr<workgroup, i32>) {
-}
-)");
-}
-
-TEST_F(IRToProgramTest, Enable_ChromiumExperimentalFullPtrParameters_SubObjectPtrArg) {
-    auto* x = b.Function("x", ty.void_());
-    x->SetParams({b.FunctionParam("p", ty.ptr<function, vec3<f32>>())});
-    b.Append(x->Block(), [&] { b.Return(x); });
-
-    auto* y = b.Function("y", ty.void_());
-    b.Append(y->Block(), [&] {
-        auto* m = b.Var<function, mat3x3<f32>>();
-        auto* v = b.Access(ty.ptr<function, vec3<f32>>(), m, 1_i);
-        b.Call(ty.void_(), x, v);
-        b.Return(y);
-    });
-
-    EXPECT_WGSL(R"(
-enable chromium_experimental_full_ptr_parameters;
-
-fn x(p : ptr<function, vec3<f32>>) {
-}
-
-fn y() {
-  var v : mat3x3<f32>;
-  x(&(v[1i]));
-}
-)");
-}
-
-TEST_F(IRToProgramTest, Enable_ChromiumExperimentalFullPtrParameters_SubObjectPtrArg_ViaLet) {
-    auto* x = b.Function("x", ty.void_());
-    x->SetParams({b.FunctionParam("p", ty.ptr<function, vec3<f32>>())});
-    b.Append(x->Block(), [&] { b.Return(x); });
-
-    auto* y = b.Function("y", ty.void_());
-    b.Append(y->Block(), [&] {
-        auto* m = b.Var<function, mat3x3<f32>>();
-        auto* v = b.Access(ty.ptr<function, vec3<f32>>(), m, 1_i);
-        auto* l = b.Let("l", v);
-        b.Call(ty.void_(), x, l);
-        b.Return(y);
-    });
-
-    EXPECT_WGSL(R"(
-enable chromium_experimental_full_ptr_parameters;
-
-fn x(p : ptr<function, vec3<f32>>) {
-}
-
-fn y() {
-  var v : mat3x3<f32>;
-  let l = &(v[1i]);
-  x(l);
-}
-)");
-}
-
-////////////////////////////////////////////////////////////////////////////////
 // chromium_experimental_subgroups
 ////////////////////////////////////////////////////////////////////////////////
 TEST_F(IRToProgramTest, Enable_ChromiumExperimentalSubgroups_SubgroupBallot) {
diff --git a/src/tint/utils/generator/text_generator.cc b/src/tint/utils/generator/text_generator.cc
index 905449a..0bcab39 100644
--- a/src/tint/utils/generator/text_generator.cc
+++ b/src/tint/utils/generator/text_generator.cc
@@ -68,8 +68,8 @@
 }
 
 void TextGenerator::TextBuffer::Insert(const std::string& line, size_t before, uint32_t indent) {
-    if (TINT_UNLIKELY(before >= lines.size())) {
-        TINT_ICE() << "TextBuffer::Insert() called with before >= lines.size()\n"
+    if (TINT_UNLIKELY(before > lines.size())) {
+        TINT_ICE() << "TextBuffer::Insert() called with before > lines.size()\n"
                    << "  before:" << before << "\n"
                    << "  lines.size(): " << lines.size();
         return;
@@ -86,8 +86,8 @@
 }
 
 void TextGenerator::TextBuffer::Insert(const TextBuffer& tb, size_t before, uint32_t indent) {
-    if (TINT_UNLIKELY(before >= lines.size())) {
-        TINT_ICE() << "TextBuffer::Insert() called with before >= lines.size()\n"
+    if (TINT_UNLIKELY(before > lines.size())) {
+        TINT_ICE() << "TextBuffer::Insert() called with before > lines.size()\n"
                    << "  before:" << before << "\n"
                    << "  lines.size(): " << lines.size();
         return;