Import Tint changes from Dawn

Changes:
  - 8d5c7953376556f7eb07901ed8c4920ec8b21542 Support read-only and read-write storage textures as unsa... by Jiawei Shao <jiawei.shao@intel.com>
  - 72b95a5ad69184ed84c11be35214ab7d45210f48 [tint][fuzzers] Add --concurrent flag to tint_wgsl_fuzzer by Ben Clayton <bclayton@google.com>
  - c39a4aa86d9a9aae4e3983d3d55f105d5c09f1dc [tint][spirv][bench] Disable derivative UA by Ben Clayton <bclayton@google.com>
GitOrigin-RevId: 8d5c7953376556f7eb07901ed8c4920ec8b21542
Change-Id: Iac694ae33fa44622e870e7352b0673bebed85a12
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/158682
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/cmd/bench/bench.cc b/src/tint/cmd/bench/bench.cc
index 6d7a964..9a05df4 100644
--- a/src/tint/cmd/bench/bench.cc
+++ b/src/tint/cmd/bench/bench.cc
@@ -144,7 +144,9 @@
 
         auto spirv = ReadFile<uint32_t>(path);
         if (spirv) {
-            auto program = tint::spirv::reader::Read(spirv.Get(), {});
+            tint::spirv::reader::Options spirv_opts;
+            spirv_opts.allow_non_uniform_derivatives = true;
+            auto program = tint::spirv::reader::Read(spirv.Get(), spirv_opts);
             if (!program.IsValid()) {
                 return Failure{program.Diagnostics()};
             }
diff --git a/src/tint/cmd/fuzz/wgsl/BUILD.cmake b/src/tint/cmd/fuzz/wgsl/BUILD.cmake
index b485bef..9ba6530 100644
--- a/src/tint/cmd/fuzz/wgsl/BUILD.cmake
+++ b/src/tint/cmd/fuzz/wgsl/BUILD.cmake
@@ -53,6 +53,7 @@
   tint_lang_wgsl_program
   tint_lang_wgsl_sem
   tint_lang_wgsl_fuzz
+  tint_utils_cli
   tint_utils_containers
   tint_utils_diagnostic
   tint_utils_ice
@@ -62,6 +63,7 @@
   tint_utils_memory
   tint_utils_result
   tint_utils_rtti
+  tint_utils_strconv
   tint_utils_symbol
   tint_utils_text
   tint_utils_traits
@@ -113,6 +115,10 @@
   tint_utils_traits
 )
 
+tint_target_add_external_dependencies(tint_cmd_fuzz_wgsl_fuzz fuzz
+  "thread"
+)
+
 if(TINT_BUILD_WGSL_READER)
   tint_target_add_dependencies(tint_cmd_fuzz_wgsl_fuzz fuzz
     tint_lang_wgsl_reader
diff --git a/src/tint/cmd/fuzz/wgsl/BUILD.gn b/src/tint/cmd/fuzz/wgsl/BUILD.gn
index 908cd3b..5c4ccc6 100644
--- a/src/tint/cmd/fuzz/wgsl/BUILD.gn
+++ b/src/tint/cmd/fuzz/wgsl/BUILD.gn
@@ -44,6 +44,7 @@
       "wgsl_fuzz.h",
     ]
     deps = [
+      "${tint_src_dir}:thread",
       "${tint_src_dir}/api/common",
       "${tint_src_dir}/lang/core",
       "${tint_src_dir}/lang/core/constant",
@@ -86,6 +87,7 @@
       "${tint_src_dir}/lang/wgsl/ast",
       "${tint_src_dir}/lang/wgsl/program",
       "${tint_src_dir}/lang/wgsl/sem",
+      "${tint_src_dir}/utils/cli",
       "${tint_src_dir}/utils/containers",
       "${tint_src_dir}/utils/diagnostic",
       "${tint_src_dir}/utils/ice",
@@ -95,6 +97,7 @@
       "${tint_src_dir}/utils/memory",
       "${tint_src_dir}/utils/result",
       "${tint_src_dir}/utils/rtti",
+      "${tint_src_dir}/utils/strconv",
       "${tint_src_dir}/utils/symbol",
       "${tint_src_dir}/utils/text",
       "${tint_src_dir}/utils/traits",
diff --git a/src/tint/cmd/fuzz/wgsl/main_fuzz.cc b/src/tint/cmd/fuzz/wgsl/main_fuzz.cc
index 586493d..fa27edc 100644
--- a/src/tint/cmd/fuzz/wgsl/main_fuzz.cc
+++ b/src/tint/cmd/fuzz/wgsl/main_fuzz.cc
@@ -25,12 +25,64 @@
 // 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 <iostream>
+
 #include "src/tint/cmd/fuzz/wgsl/wgsl_fuzz.h"
+#include "src/tint/utils/cli/cli.h"
+
+namespace {
+
+tint::fuzz::wgsl::Options options;
+
+}
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
     if (size > 0) {
         std::string_view wgsl(reinterpret_cast<const char*>(data), size);
-        tint::fuzz::wgsl::Run(wgsl);
+        tint::fuzz::wgsl::Run(wgsl, options);
     }
     return 0;
 }
+
+extern "C" int LLVMFuzzerInitialize(int* argc, const char*** argv) {
+    tint::cli::OptionSet opts;
+
+    tint::Vector<std::string_view, 8> arguments;
+    for (int i = 1; i < *argc; i++) {
+        std::string_view arg((*argv)[i]);
+        if (!arg.empty()) {
+            arguments.Push(arg);
+        }
+    }
+
+    auto show_help = [&] {
+        std::cerr << "Custom fuzzer options:" << std::endl;
+        opts.ShowHelp(std::cerr);
+        std::cerr << std::endl;
+        // Change args to show libfuzzer help
+        std::cerr << "Standard libfuzzer ";  // libfuzzer will print 'Usage:'
+        static const char* help[] = {(*argv)[0], "-help=1"};
+        *argc = 2;
+        *argv = help;
+    };
+
+    auto& opt_help = opts.Add<tint::cli::BoolOption>("help", "shows the usage");
+    auto& opt_concurrent =
+        opts.Add<tint::cli::BoolOption>("concurrent", "runs the fuzzers concurrently");
+
+    tint::cli::ParseOptions parse_opts;
+    parse_opts.ignore_unknown = true;
+    if (auto res = opts.Parse(arguments, parse_opts); !res) {
+        show_help();
+        std::cerr << res.Failure();
+        return 0;
+    }
+
+    if (opt_help.value.value_or(false)) {
+        show_help();
+        return 0;
+    }
+
+    options.run_concurrently = opt_concurrent.value.value_or(false);
+    return 0;
+}
diff --git a/src/tint/cmd/fuzz/wgsl/wgsl_fuzz.cc b/src/tint/cmd/fuzz/wgsl/wgsl_fuzz.cc
index a139e91..69d3587 100644
--- a/src/tint/cmd/fuzz/wgsl/wgsl_fuzz.cc
+++ b/src/tint/cmd/fuzz/wgsl/wgsl_fuzz.cc
@@ -28,6 +28,7 @@
 #include "src/tint/cmd/fuzz/wgsl/wgsl_fuzz.h"
 
 #include <iostream>
+#include <thread>
 
 #include "src/tint/lang/wgsl/reader/reader.h"
 #include "src/tint/utils/containers/vector.h"
@@ -38,7 +39,7 @@
 namespace {
 
 Vector<ProgramFuzzer, 32> fuzzers;
-std::string_view currently_running;
+thread_local std::string_view currently_running;
 
 [[noreturn]] void TintInternalCompilerErrorReporter(const tint::InternalCompilerError& err) {
     std::cerr << "ICE while running fuzzer: '" << currently_running << "'" << std::endl;
@@ -52,7 +53,7 @@
     fuzzers.Push(fuzzer);
 }
 
-void Run(std::string_view wgsl) {
+void Run(std::string_view wgsl, const Options& options) {
     tint::SetInternalCompilerErrorReporter(&TintInternalCompilerErrorReporter);
 
     // Ensure that fuzzers are sorted. Without this, the fuzzers may be registered in any order,
@@ -69,10 +70,26 @@
     }
 
     // Run each of the program fuzzer functions
-    TINT_DEFER(currently_running = "");
-    for (auto& fuzzer : fuzzers) {
-        currently_running = fuzzer.name;
-        fuzzer.fn(program);
+    if (options.run_concurrently) {
+        size_t n = fuzzers.Length();
+        tint::Vector<std::thread, 32> threads;
+        threads.Resize(n);
+        for (size_t i = 0; i < n; i++) {
+            threads[i] = std::thread([i, &program] {
+                auto& fuzzer = fuzzers[i];
+                currently_running = fuzzer.name;
+                fuzzer.fn(program);
+            });
+        }
+        for (auto& thread : threads) {
+            thread.join();
+        }
+    } else {
+        TINT_DEFER(currently_running = "");
+        for (auto& fuzzer : fuzzers) {
+            currently_running = fuzzer.name;
+            fuzzer.fn(program);
+        }
     }
 }
 
diff --git a/src/tint/cmd/fuzz/wgsl/wgsl_fuzz.h b/src/tint/cmd/fuzz/wgsl/wgsl_fuzz.h
index 2231743..909c394 100644
--- a/src/tint/cmd/fuzz/wgsl/wgsl_fuzz.h
+++ b/src/tint/cmd/fuzz/wgsl/wgsl_fuzz.h
@@ -47,9 +47,16 @@
     Fn* fn = nullptr;
 };
 
+/// Options for Run()
+struct Options {
+    /// If true, the fuzzers will be run concurrently on separate threads.
+    bool run_concurrently = false;
+};
+
 /// Runs all the registered WGSL fuzzers with the supplied WGSL
 /// @param wgsl the input WGSL
-void Run(std::string_view wgsl);
+/// @param options the options for running the fuzzers
+void Run(std::string_view wgsl, const Options& options);
 
 /// Registers the fuzzer function with the WGSL fuzzer executable.
 /// @param fuzzer the fuzzer
diff --git a/src/tint/lang/spirv/reader/ast_parser/ast_parser.cc b/src/tint/lang/spirv/reader/ast_parser/ast_parser.cc
index 2c7d268..0bf7f61 100644
--- a/src/tint/lang/spirv/reader/ast_parser/ast_parser.cc
+++ b/src/tint/lang/spirv/reader/ast_parser/ast_parser.cc
@@ -2583,9 +2583,6 @@
         } else {
             const auto access =
                 usage.IsStorageReadWriteTexture() ? core::Access::kReadWrite : core::Access::kWrite;
-            if (access == core::Access::kReadWrite) {
-                Enable(wgsl::Extension::kChromiumExperimentalReadWriteStorageTexture);
-            }
             const auto format = enum_converter_.ToTexelFormat(image_type->format());
             if (format == core::TexelFormat::kUndefined) {
                 return nullptr;
diff --git a/src/tint/lang/spirv/reader/ast_parser/function.cc b/src/tint/lang/spirv/reader/ast_parser/function.cc
index 99e728a..198f58f 100644
--- a/src/tint/lang/spirv/reader/ast_parser/function.cc
+++ b/src/tint/lang/spirv/reader/ast_parser/function.cc
@@ -5325,7 +5325,6 @@
         if (memory != uint32_t(spv::Scope::Workgroup)) {
             return Fail() << "textureBarrier requires workgroup memory scope";
         }
-        parser_impl_.Enable(wgsl::Extension::kChromiumExperimentalReadWriteStorageTexture);
         AddStatement(builder_.CallStmt(builder_.Call("textureBarrier")));
         semantics &= ~static_cast<uint32_t>(spv::MemorySemanticsMask::ImageMemory);
     }
diff --git a/src/tint/lang/spirv/reader/ast_parser/handle_test.cc b/src/tint/lang/spirv/reader/ast_parser/handle_test.cc
index 4d8caf4..154ccc2 100644
--- a/src/tint/lang/spirv/reader/ast_parser/handle_test.cc
+++ b/src/tint/lang/spirv/reader/ast_parser/handle_test.cc
@@ -4233,9 +4233,8 @@
 
     EXPECT_TRUE(p->error().empty()) << p->error();
     const auto got = test::ToString(p->program());
-    auto* expect = R"(enable chromium_experimental_read_write_storage_texture;
-
-@group(0) @binding(0) var RWTexture2D : texture_storage_2d<rgba32float, read_write>;
+    auto* expect =
+        R"(@group(0) @binding(0) var RWTexture2D : texture_storage_2d<rgba32float, read_write>;
 
 const x_9 = vec2u(1u);
 
diff --git a/src/tint/lang/spirv/reader/ast_parser/parse.cc b/src/tint/lang/spirv/reader/ast_parser/parse.cc
index af503d2..94c4355 100644
--- a/src/tint/lang/spirv/reader/ast_parser/parse.cc
+++ b/src/tint/lang/spirv/reader/ast_parser/parse.cc
@@ -71,7 +71,6 @@
                     case wgsl::Extension::kChromiumExperimentalFullPtrParameters:
                     case wgsl::Extension::kChromiumExperimentalPixelLocal:
                     case wgsl::Extension::kChromiumExperimentalPushConstant:
-                    case wgsl::Extension::kChromiumExperimentalReadWriteStorageTexture:
                     case wgsl::Extension::kChromiumExperimentalSubgroups:
                     case wgsl::Extension::kChromiumInternalDualSourceBlending:
                     case wgsl::Extension::kChromiumInternalRelaxedUniformLayout: {
@@ -82,6 +81,7 @@
                         return Program(std::move(builder));
                     }
                     case wgsl::Extension::kF16:
+                    case wgsl::Extension::kChromiumExperimentalReadWriteStorageTexture:
                         break;
                 }
             }
diff --git a/src/tint/lang/spirv/reader/ast_parser/parser_test.cc b/src/tint/lang/spirv/reader/ast_parser/parser_test.cc
index f7b2ded..7375186 100644
--- a/src/tint/lang/spirv/reader/ast_parser/parser_test.cc
+++ b/src/tint/lang/spirv/reader/ast_parser/parser_test.cc
@@ -91,63 +91,6 @@
     EXPECT_EQ(program.Diagnostics().count(), 0u) << errs;
 }
 
-constexpr auto kShaderWithReadWriteStorageTexture = R"(
-               OpCapability Shader
-               OpCapability StorageImageExtendedFormats
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint GLCompute %100 "main"
-               OpExecutionMode %100 LocalSize 8 8 1
-               OpSource HLSL 600
-               OpName %type_2d_image "type.2d.image"
-               OpName %RWTexture2D "RWTexture2D"
-               OpName %100 "main"
-               OpDecorate %RWTexture2D DescriptorSet 0
-               OpDecorate %RWTexture2D Binding 0
-      %float = OpTypeFloat 32
-    %float_0 = OpConstant %float 0
-    %v4float = OpTypeVector %float 4
-       %uint = OpTypeInt 32 0
-     %uint_1 = OpConstant %uint 1
-     %v2uint = OpTypeVector %uint 2
-      %coord = OpConstantComposite %v2uint %uint_1 %uint_1
-%type_2d_image = OpTypeImage %float 2D 2 0 0 2 Rgba32f
-%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
-       %void = OpTypeVoid
-         %20 = OpTypeFunction %void
-%RWTexture2D = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
-        %100 = OpFunction %void None %20
-         %22 = OpLabel
-         %30 = OpLoad %type_2d_image %RWTexture2D
-         %31 = OpImageRead %v4float %30 %coord None
-         %32 = OpFAdd %v4float %31 %31
-               OpImageWrite %30 %coord %32 None
-               OpReturn
-               OpFunctionEnd
-  )";
-
-TEST_F(ParserTest, AllowChromiumExtensions_False) {
-    auto spv = test::Assemble(kShaderWithReadWriteStorageTexture);
-    Options options;
-    options.allow_chromium_extensions = false;
-    auto program = Parse(spv, options);
-    auto errs = program.Diagnostics().str();
-    EXPECT_FALSE(program.IsValid()) << errs;
-    EXPECT_THAT(errs,
-                ::testing::HasSubstr(
-                    "error: module requires chromium_experimental_read_write_storage_texture, but "
-                    "'allow-chromium-extensions' was not passed"));
-}
-
-TEST_F(ParserTest, AllowChromiumExtensions_True) {
-    auto spv = test::Assemble(kShaderWithReadWriteStorageTexture);
-    Options options;
-    options.allow_chromium_extensions = true;
-    auto program = Parse(spv, options);
-    auto errs = program.Diagnostics().str();
-    EXPECT_TRUE(program.IsValid()) << errs;
-    EXPECT_EQ(program.Diagnostics().count(), 0u) << errs;
-}
-
 // TODO(dneto): uint32 vec, valid SPIR-V
 // TODO(dneto): uint32 vec, invalid SPIR-V
 
diff --git a/src/tint/lang/wgsl/inspector/inspector_test.cc b/src/tint/lang/wgsl/inspector/inspector_test.cc
index 80435da..052c464 100644
--- a/src/tint/lang/wgsl/inspector/inspector_test.cc
+++ b/src/tint/lang/wgsl/inspector/inspector_test.cc
@@ -3126,11 +3126,9 @@
             expectedResourceType = ResourceBinding::ResourceType::kWriteOnlyStorageTexture;
             break;
         case core::Access::kRead:
-            Enable(wgsl::Extension::kChromiumExperimentalReadWriteStorageTexture);
             expectedResourceType = ResourceBinding::ResourceType::kReadOnlyStorageTexture;
             break;
         case core::Access::kReadWrite:
-            Enable(wgsl::Extension::kChromiumExperimentalReadWriteStorageTexture);
             expectedResourceType = ResourceBinding::ResourceType::kReadWriteStorageTexture;
             break;
         case core::Access::kUndefined:
diff --git a/src/tint/lang/wgsl/resolver/builtin_validation_test.cc b/src/tint/lang/wgsl/resolver/builtin_validation_test.cc
index 4e4fe92..c317db4 100644
--- a/src/tint/lang/wgsl/resolver/builtin_validation_test.cc
+++ b/src/tint/lang/wgsl/resolver/builtin_validation_test.cc
@@ -809,29 +809,13 @@
         R"(12:34 error: the sourceLaneIndex argument of subgroupBroadcast must be a const-expression)");
 }
 
-TEST_F(ResolverBuiltinValidationTest, TextureBarrierWithoutExtension) {
+TEST_F(ResolverBuiltinValidationTest, TextureBarrier) {
     // fn func { textureBarrier(); }
     Func("func", tint::Empty, ty.void_(),
          Vector{
              CallStmt(Call(Source{Source::Location{12, 34}}, "textureBarrier")),
          });
 
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(
-        r()->error(),
-        R"(12:34 error: cannot call built-in function 'textureBarrier' without extension chromium_experimental_read_write_storage_texture)");
-}
-
-TEST_F(ResolverBuiltinValidationTest, TextureBarrierWithExtension) {
-    // enable chromium_experimental_read_write_storage_texture;
-    // fn func { textureBarrier(); }
-    Enable(wgsl::Extension::kChromiumExperimentalReadWriteStorageTexture);
-
-    Func("func", tint::Empty, ty.void_(),
-         Vector{
-             CallStmt(Call(Source{Source::Location{12, 34}}, "textureBarrier")),
-         });
-
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
diff --git a/src/tint/lang/wgsl/resolver/type_validation_test.cc b/src/tint/lang/wgsl/resolver/type_validation_test.cc
index 710fdf4..bf9d840 100644
--- a/src/tint/lang/wgsl/resolver/type_validation_test.cc
+++ b/src/tint/lang/wgsl/resolver/type_validation_test.cc
@@ -1196,7 +1196,7 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(StorageTextureAccessTest, ReadOnlyAccess_WithoutExtension_Fail) {
+TEST_F(StorageTextureAccessTest, ReadOnlyAccess_Pass) {
     // @group(0) @binding(0)
     // var a : texture_storage_1d<r32uint, read>;
 
@@ -1205,27 +1205,10 @@
 
     GlobalVar("a", st, Group(0_a), Binding(0_a));
 
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              "12:34 error: read-only storage textures require the "
-              "chromium_experimental_read_write_storage_texture extension to be enabled");
-}
-
-TEST_F(StorageTextureAccessTest, ReadOnlyAccess_WithExtension_Pass) {
-    // enable chromium_experimental_read_write_storage_texture;
-    // @group(0) @binding(0)
-    // var a : texture_storage_1d<r32uint, read>;
-
-    Enable(wgsl::Extension::kChromiumExperimentalReadWriteStorageTexture);
-    auto st = ty.storage_texture(Source{{12, 34}}, core::type::TextureDimension::k1d,
-                                 core::TexelFormat::kR32Uint, core::Access::kRead);
-
-    GlobalVar("a", st, Group(0_a), Binding(0_a));
-
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(StorageTextureAccessTest, RWAccess_WithoutExtension_Fail) {
+TEST_F(StorageTextureAccessTest, RWAccess_Pass) {
     // @group(0) @binding(0)
     // var a : texture_storage_1d<r32uint, read_write>;
 
@@ -1234,23 +1217,6 @@
 
     GlobalVar("a", st, Group(0_a), Binding(0_a));
 
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              "12:34 error: read-write storage textures require the "
-              "chromium_experimental_read_write_storage_texture extension to be enabled");
-}
-
-TEST_F(StorageTextureAccessTest, RWAccess_WithExtension_Pass) {
-    // enable chromium_experimental_read_write_storage_texture;
-    // @group(0) @binding(0)
-    // var a : texture_storage_1d<r32uint, read_write>;
-
-    Enable(wgsl::Extension::kChromiumExperimentalReadWriteStorageTexture);
-    auto st = ty.storage_texture(Source{{12, 34}}, core::type::TextureDimension::k1d,
-                                 core::TexelFormat::kR32Uint, core::Access::kReadWrite);
-
-    GlobalVar("a", st, Group(0_a), Binding(0_a));
-
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
diff --git a/src/tint/lang/wgsl/resolver/validator.cc b/src/tint/lang/wgsl/resolver/validator.cc
index d3c0205..da7536d 100644
--- a/src/tint/lang/wgsl/resolver/validator.cc
+++ b/src/tint/lang/wgsl/resolver/validator.cc
@@ -333,25 +333,7 @@
 bool Validator::StorageTexture(const core::type::StorageTexture* t, const Source& source) const {
     switch (t->access()) {
         case core::Access::kRead:
-            if (!enabled_extensions_.Contains(
-                    wgsl::Extension::kChromiumExperimentalReadWriteStorageTexture)) {
-                AddError(
-                    "read-only storage textures require the "
-                    "chromium_experimental_read_write_storage_texture extension to be enabled",
-                    source);
-                return false;
-            }
-            break;
         case core::Access::kReadWrite:
-            if (!enabled_extensions_.Contains(
-                    wgsl::Extension::kChromiumExperimentalReadWriteStorageTexture)) {
-                AddError(
-                    "read-write storage textures require the "
-                    "chromium_experimental_read_write_storage_texture extension to be enabled",
-                    source);
-                return false;
-            }
-            break;
         case core::Access::kWrite:
             break;
         case core::Access::kUndefined:
diff --git a/src/tint/lang/wgsl/sem/builtin_fn.cc b/src/tint/lang/wgsl/sem/builtin_fn.cc
index 968320f..ea49f47 100644
--- a/src/tint/lang/wgsl/sem/builtin_fn.cc
+++ b/src/tint/lang/wgsl/sem/builtin_fn.cc
@@ -112,9 +112,6 @@
     if (IsSubgroup()) {
         return wgsl::Extension::kChromiumExperimentalSubgroups;
     }
-    if (fn_ == wgsl::BuiltinFn::kTextureBarrier) {
-        return wgsl::Extension::kChromiumExperimentalReadWriteStorageTexture;
-    }
     return wgsl::Extension::kUndefined;
 }
 
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 4a87f27..e3650e6 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
@@ -605,9 +605,6 @@
                 }
 
                 switch (c->Func()) {
-                    case wgsl::BuiltinFn::kTextureBarrier:
-                        Enable(wgsl::Extension::kChromiumExperimentalReadWriteStorageTexture);
-                        break;
                     case wgsl::BuiltinFn::kSubgroupBallot:
                     case wgsl::BuiltinFn::kSubgroupBroadcast:
                         Enable(wgsl::Extension::kChromiumExperimentalSubgroups);
@@ -942,9 +939,6 @@
                 return b.ty.sampled_texture(t->dim(), el);
             },
             [&](const core::type::StorageTexture* t) {
-                if (t->access() == core::Access::kRead || t->access() == core::Access::kReadWrite) {
-                    Enable(wgsl::Extension::kChromiumExperimentalReadWriteStorageTexture);
-                }
                 return b.ty.storage_texture(t->dim(), t->texel_format(), t->access());
             },
             [&](const core::type::Sampler* s) { return b.ty.sampler(s->kind()); },
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 86dbbca..53af431 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,55 +3212,6 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-// chromium_experimental_read_write_storage_texture
-////////////////////////////////////////////////////////////////////////////////
-TEST_F(IRToProgramTest, Enable_ChromiumExperimentalReadWriteStorageTexture_TextureBarrier) {
-    auto* fn = b.Function("f", ty.void_());
-    b.Append(fn->Block(), [&] {
-        b.Append(mod.instructions.Create<wgsl::ir::BuiltinCall>(
-            b.InstructionResult(ty.void_()), wgsl::BuiltinFn::kTextureBarrier, Empty));
-        b.Return(fn);
-    });
-
-    EXPECT_WGSL(R"(
-enable chromium_experimental_read_write_storage_texture;
-
-fn f() {
-  textureBarrier();
-}
-)");
-}
-
-TEST_F(IRToProgramTest, Enable_ChromiumExperimentalReadWriteStorageTexture_ReadOnlyStorageTexture) {
-    auto* T = b.Var("T", ty.ptr<handle>(ty.Get<core::type::StorageTexture>(
-                             core::type::TextureDimension::k2d, core::TexelFormat::kR32Float,
-                             core::Access::kRead, ty.f32())));
-    T->SetBindingPoint(0, 0);
-    b.ir.root_block->Append(T);
-
-    EXPECT_WGSL(R"(
-enable chromium_experimental_read_write_storage_texture;
-
-@group(0) @binding(0) var T : texture_storage_2d<r32float, read>;
-)");
-}
-
-TEST_F(IRToProgramTest,
-       Enable_ChromiumExperimentalReadWriteStorageTexture_ReadWriteOnlyStorageTexture) {
-    auto* T = b.Var("T", ty.ptr<handle>(ty.Get<core::type::StorageTexture>(
-                             core::type::TextureDimension::k2d, core::TexelFormat::kR32Float,
-                             core::Access::kReadWrite, ty.f32())));
-    T->SetBindingPoint(0, 0);
-    b.ir.root_block->Append(T);
-
-    EXPECT_WGSL(R"(
-enable chromium_experimental_read_write_storage_texture;
-
-@group(0) @binding(0) var T : texture_storage_2d<r32float, read_write>;
-)");
-}
-
-////////////////////////////////////////////////////////////////////////////////
 // chromium_experimental_subgroups
 ////////////////////////////////////////////////////////////////////////////////
 TEST_F(IRToProgramTest, Enable_ChromiumExperimentalSubgroups_SubgroupBallot) {
diff --git a/src/tint/utils/cli/cli.cc b/src/tint/utils/cli/cli.cc
index 5095b73..557d8bc 100644
--- a/src/tint/utils/cli/cli.cc
+++ b/src/tint/utils/cli/cli.cc
@@ -37,6 +37,7 @@
 
 namespace tint::cli {
 
+Option::Option() = default;
 Option::~Option() = default;
 
 void OptionSet::ShowHelp(std::ostream& s_out) {
@@ -131,7 +132,8 @@
     }
 }
 
-Result<OptionSet::Unconsumed> OptionSet::Parse(VectorRef<std::string_view> arguments_raw) {
+Result<OptionSet::Unconsumed> OptionSet::Parse(VectorRef<std::string_view> arguments_raw,
+                                               const ParseOptions& parse_options /* = {} */) {
     // Build a map of name to option, and set defaults
     Hashmap<std::string, Option*, 32> options_by_name;
     for (auto* opt : options.Objects()) {
@@ -171,7 +173,7 @@
             if (auto err = (*opt)->Parse(arguments); !err.empty()) {
                 return Failure{err};
             }
-        } else {
+        } else if (!parse_options.ignore_unknown) {
             StringStream err;
             err << "unknown flag: " << arg << std::endl;
             auto names = options_by_name.Keys();
diff --git a/src/tint/utils/cli/cli.h b/src/tint/utils/cli/cli.h
index 5bfa35a..b315884 100644
--- a/src/tint/utils/cli/cli.h
+++ b/src/tint/utils/cli/cli.h
@@ -101,6 +101,9 @@
     /// An alias to std::string, used to hold error messages.
     using Error = std::string;
 
+    /// Constructor
+    Option();
+
     /// Destructor
     virtual ~Option();
 
@@ -162,6 +165,18 @@
         }
         return err;
     }
+
+  private:
+    Option(const Option&) = delete;
+    Option& operator=(const Option&) = delete;
+    Option(Option&&) = delete;
+    Option& operator=(Option&&) = delete;
+};
+
+/// Options for OptionSet::Parse
+struct ParseOptions {
+    /// If true, then unknown flags will be ignored instead of treated as an error
+    bool ignore_unknown = false;
 };
 
 /// OptionSet is a set of Options, which can parse the command line arguments.
@@ -186,8 +201,10 @@
 
     /// Parses all the options in @p options.
     /// @param arguments the command line arguments, excluding the initial executable name
+    /// @param parse_options the optional parser options
     /// @return a Result holding a list of arguments that were not consumed as options
-    Result<Unconsumed> Parse(VectorRef<std::string_view> arguments);
+    Result<Unconsumed> Parse(VectorRef<std::string_view> arguments,
+                             const ParseOptions& parse_options = {});
 
   private:
     /// The list of options to parse
diff --git a/src/tint/utils/cli/cli_test.cc b/src/tint/utils/cli/cli_test.cc
index 6673a01..4db57f8 100644
--- a/src/tint/utils/cli/cli_test.cc
+++ b/src/tint/utils/cli/cli_test.cc
@@ -159,6 +159,28 @@
 )");
 }
 
+TEST_F(CLITest, UnknownFlag) {
+    OptionSet opts;
+    opts.Add<BoolOption>("my_option", "a boolean value");
+
+    auto res = opts.Parse(Split("--myoption false", " "));
+    ASSERT_FALSE(res) << res;
+    EXPECT_EQ(res.Failure().reason.str(), R"(error: unknown flag: --myoption
+Did you mean '--my_option'?)");
+}
+
+TEST_F(CLITest, UnknownFlag_Ignored) {
+    OptionSet opts;
+    auto& opt = opts.Add<BoolOption>("my_option", "a boolean value");
+
+    ParseOptions parse_opts;
+    parse_opts.ignore_unknown = true;
+
+    auto res = opts.Parse(Split("--myoption false", " "), parse_opts);
+    ASSERT_TRUE(res) << res;
+    EXPECT_EQ(opt.value, std::nullopt);
+}
+
 TEST_F(CLITest, ParseBool_Flag) {
     OptionSet opts;
     auto& opt = opts.Add<BoolOption>("my_option", "a boolean value");