[tint][fuzz] Support passing fuzz::wgsl::Options to the WGSL fuzzers

Provides a way to pass down command line flags to the sub-fuzzers

Change-Id: Ia0bf0cd753a81ac8de7625bf0962300ba19b864d
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/186643
Reviewed-by: Ryan Harrison <rharrison@chromium.org>
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
diff --git a/src/tint/cmd/fuzz/ir/fuzz.cc b/src/tint/cmd/fuzz/ir/fuzz.cc
index 3ea4363..a74fef3 100644
--- a/src/tint/cmd/fuzz/ir/fuzz.cc
+++ b/src/tint/cmd/fuzz/ir/fuzz.cc
@@ -64,7 +64,8 @@
 void Register(const IRFuzzer& fuzzer) {
     wgsl::Register({
         fuzzer.name,
-        [fn = fuzzer.fn](const Program& program, Slice<const std::byte> data) {
+        [fn = fuzzer.fn](const Program& program, const fuzz::wgsl::Options& options,
+                         Slice<const std::byte> data) {
             if (program.AST().Enables().Any(IsUnsupported)) {
                 return;
             }
diff --git a/src/tint/cmd/fuzz/wgsl/fuzz.cc b/src/tint/cmd/fuzz/wgsl/fuzz.cc
index 23bbe0f..17c2e1c 100644
--- a/src/tint/cmd/fuzz/wgsl/fuzz.cc
+++ b/src/tint/cmd/fuzz/wgsl/fuzz.cc
@@ -67,7 +67,7 @@
     Fuzzers().Push(fuzzer);
 }
 
-void Run(std::string_view wgsl, Slice<const std::byte> data, const Options& options) {
+void Run(std::string_view wgsl, const Options& options, Slice<const std::byte> data) {
     tint::SetInternalCompilerErrorReporter(&TintInternalCompilerErrorReporter);
 
 #if TINT_BUILD_WGSL_WRITER
@@ -116,7 +116,7 @@
                 if (options.verbose) {
                     std::cout << " • [" << i << "] Running: " << currently_running << std::endl;
                 }
-                fuzzer.fn(program, data);
+                fuzzer.fn(program, options, data);
             }));
         }
         for (auto& thread : threads) {
@@ -133,7 +133,7 @@
             if (options.verbose) {
                 std::cout << " • Running: " << currently_running << std::endl;
             }
-            fuzzer.fn(program, data);
+            fuzzer.fn(program, options, data);
         }
     }
 }
diff --git a/src/tint/cmd/fuzz/wgsl/fuzz.h b/src/tint/cmd/fuzz/wgsl/fuzz.h
index e4bc7a0..4533e5a 100644
--- a/src/tint/cmd/fuzz/wgsl/fuzz.h
+++ b/src/tint/cmd/fuzz/wgsl/fuzz.h
@@ -37,20 +37,63 @@
 #include "src/tint/utils/bytes/decoder.h"
 #include "src/tint/utils/containers/slice.h"
 #include "src/tint/utils/macros/static_init.h"
-#include "src/tint/utils/reflection/reflection.h"
 
 namespace tint::fuzz::wgsl {
 
+/// Options for Run()
+struct Options {
+    /// If not empty, only run the fuzzers with the given substring.
+    std::string filter;
+    /// If true, the fuzzers will be run concurrently on separate threads.
+    bool run_concurrently = false;
+    /// If true, print the fuzzer name to stdout before running.
+    bool verbose = false;
+};
+
 /// ProgramFuzzer describes a fuzzer function that takes a WGSL program as input
 struct ProgramFuzzer {
     /// @param name the name of the fuzzer
-    /// @param fn the fuzzer function
-    /// @returns a ProgramFuzzer that invokes the function @p fn with the Program, along with any
-    /// additional arguments which are deserialized from the fuzzer input.
+    /// @param fn the fuzzer function with the signature `void(const Program&, const Options&, ...)`
+    /// @returns a ProgramFuzzer that invokes the function @p fn with the Program, Options, along
+    /// with any additional arguments which are deserialized from the fuzzer input.
+    template <typename... ARGS>
+    static ProgramFuzzer Create(std::string_view name,
+                                void (*fn)(const Program&, const Options&, ARGS...)) {
+        if constexpr (sizeof...(ARGS) > 0) {
+            auto fn_with_decode = [fn](const Program& program, const Options& options,
+                                       Slice<const std::byte> data) {
+                if (!data.data) {
+                    return;
+                }
+                bytes::BufferReader reader{data};
+                auto data_args = bytes::Decode<std::tuple<std::decay_t<ARGS>...>>(reader);
+                if (data_args == Success) {
+                    auto all_args =
+                        std::tuple_cat(std::tuple<const Program&, const Options&>{program, options},
+                                       data_args.Get());
+                    std::apply(*fn, all_args);
+                }
+            };
+            return ProgramFuzzer{name, std::move(fn_with_decode)};
+        } else {
+            return ProgramFuzzer{
+                name,
+                [fn](const Program& program, const Options& options, Slice<const std::byte>) {
+                    fn(program, options);
+                },
+            };
+        }
+    }
+
+    /// @param name the name of the fuzzer
+    /// @param fn the fuzzer function with the signature `void(const Program&, ...)`
+    /// @returns a ProgramFuzzer that invokes the function @p fn with the Program, along
+    /// with any additional arguments which are deserialized from the fuzzer input.
     template <typename... ARGS>
     static ProgramFuzzer Create(std::string_view name, void (*fn)(const Program&, ARGS...)) {
         if constexpr (sizeof...(ARGS) > 0) {
-            auto fn_with_decode = [fn](const Program& program, Slice<const std::byte> data) {
+            auto fn_with_decode = [fn](const Program& program, const Options&,
+                                       Slice<const std::byte> data) {
                 if (!data.data) {
                     return;
                 }
@@ -66,7 +109,9 @@
         } else {
             return ProgramFuzzer{
                 name,
-                [fn](const Program& program, Slice<const std::byte>) { fn(program); },
+                [fn](const Program& program, const Options&, Slice<const std::byte>) {
+                    fn(program);
+                },
             };
         }
     }
@@ -74,30 +119,26 @@
     /// Name of the fuzzer function
     std::string_view name;
     /// The fuzzer function
-    std::function<void(const Program&, Slice<const std::byte> data)> fn;
-};
-
-/// Options for Run()
-struct Options {
-    /// If not empty, only run the fuzzers with the given substring.
-    std::string filter;
-    /// If true, the fuzzers will be run concurrently on separate threads.
-    bool run_concurrently = false;
-    /// If true, print the fuzzer name to stdout before running.
-    bool verbose = false;
+    std::function<void(const Program&, const Options&, Slice<const std::byte> data)> fn;
 };
 
 /// Runs all the registered WGSL fuzzers with the supplied WGSL
 /// @param wgsl the input WGSL
-/// @param data additional data used for fuzzing
 /// @param options the options for running the fuzzers
-void Run(std::string_view wgsl, Slice<const std::byte> data, const Options& options);
+/// @param data additional data used for fuzzing
+void Run(std::string_view wgsl, const Options& options, Slice<const std::byte> data);
 
 /// Registers the fuzzer function with the WGSL fuzzer executable.
 /// @param fuzzer the fuzzer
 void Register(const ProgramFuzzer& fuzzer);
 
 /// TINT_WGSL_PROGRAM_FUZZER registers the fuzzer function to run as part of `tint_wgsl_fuzzer`
+/// The function must have one of the signatures:
+/// • `void(const Program&, ...)`
+/// • `void(const Program&, const Options&, ...)`
+/// Where `...` is any number of deserializable parameters which are decoded from the base64
+/// content of the WGSL comments.
+/// @see bytes::Decode()
 #define TINT_WGSL_PROGRAM_FUZZER(FUNCTION)         \
     TINT_STATIC_INIT(::tint::fuzz::wgsl::Register( \
         ::tint::fuzz::wgsl::ProgramFuzzer::Create(#FUNCTION, FUNCTION)))
diff --git a/src/tint/cmd/fuzz/wgsl/main_fuzz.cc b/src/tint/cmd/fuzz/wgsl/main_fuzz.cc
index 3420d40..2608906 100644
--- a/src/tint/cmd/fuzz/wgsl/main_fuzz.cc
+++ b/src/tint/cmd/fuzz/wgsl/main_fuzz.cc
@@ -42,7 +42,7 @@
     if (size > 0) {
         std::string_view wgsl(reinterpret_cast<const char*>(input), size);
         auto data = tint::DecodeBase64FromComments(wgsl);
-        tint::fuzz::wgsl::Run(wgsl, data.Slice(), options);
+        tint::fuzz::wgsl::Run(wgsl, options, data.Slice());
     }
     return 0;
 }