Refactor fuzzer transform generation

Also splits out various utility classes from tint_common_fuzzer and
uses consistent naming for utility classes.

BUG=tint:1106

Change-Id: Ic343741eea799366850c46834865d50885554a84
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/65301
Auto-Submit: Ryan Harrison <rharrison@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ryan Harrison <rharrison@chromium.org>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
diff --git a/fuzzers/BUILD.gn b/fuzzers/BUILD.gn
index 0084407..acb510b 100644
--- a/fuzzers/BUILD.gn
+++ b/fuzzers/BUILD.gn
@@ -66,10 +66,12 @@
     ]
 
     sources = [
+      "data_builder.h",
       "random_generator.cc",
       "random_generator.h",
       "tint_common_fuzzer.cc",
       "tint_common_fuzzer.h",
+      "transform_builder.h",
     ]
   }
 
@@ -79,8 +81,8 @@
     sources = [
       "cli.cc",
       "cli.h",
-      "tint_init_fuzzer.cc",
-      "tint_init_fuzzer.h",
+      "fuzzer_init.cc",
+      "fuzzer_init.h",
     ]
   }
   if (tint_build_wgsl_reader) {
diff --git a/fuzzers/CMakeLists.txt b/fuzzers/CMakeLists.txt
index f50ec56..0c8b1ca 100644
--- a/fuzzers/CMakeLists.txt
+++ b/fuzzers/CMakeLists.txt
@@ -17,12 +17,14 @@
     ${NAME}.cc
     cli.cc
     cli.h
+    data_builder.h
+    fuzzer_init.cc
+    fuzzer_init.h
     random_generator.cc
     random_generator.h
     tint_common_fuzzer.cc
     tint_common_fuzzer.h
-    tint_init_fuzzer.cc
-    tint_init_fuzzer.h
+    transform_builder.h
     )
   target_link_libraries(${NAME} libtint-fuzz)
   tint_default_compile_options(${NAME})
diff --git a/fuzzers/data_builder.h b/fuzzers/data_builder.h
new file mode 100644
index 0000000..b6ec22f
--- /dev/null
+++ b/fuzzers/data_builder.h
@@ -0,0 +1,142 @@
+// Copyright 2021 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef FUZZERS_DATA_BUILDER_H_
+#define FUZZERS_DATA_BUILDER_H_
+
+#include <cassert>
+#include <functional>
+#include <string>
+#include <vector>
+
+#include "fuzzers/random_generator.h"
+
+namespace tint {
+namespace fuzzers {
+
+/// Builder for generic pseudo-random data using a data buffer as seed
+class DataBuilder {
+ public:
+  /// @brief Initialize random number generations
+  /// @param data - pointer to a data buffer to use as a seed
+  /// @param size - size of data buffer
+  explicit DataBuilder(const uint8_t* data, size_t size)
+      : generator_(data, size) {}
+
+  ~DataBuilder() {}
+
+  /// Generate pseudo-random data of a specific type
+  /// @tparam T - type of data to produce
+  /// @returns pseudo-random data of type T
+  template <typename T>
+  T build() {
+    return BuildImpl<T>::impl(this);
+  }
+
+  /// Generate pseudo-random data of a specific type in a vector
+  /// @tparam T - data type held vector
+  /// @returns pseudo-random data of type std::vector<T>
+  template <typename T>
+  std::vector<T> vector() {
+    auto count = build<uint8_t>();
+    std::vector<T> out(count);
+    for (uint8_t i = 0; i < count; i++) {
+      out[i] = build<T>();
+    }
+    return out;
+  }
+
+  /// Generate complex pseudo-random data of a specific type in a vector
+  /// @tparam T - data type held vector
+  /// @tparam Callback - callback that takes in a DataBuilder* and returns a T
+  /// @param generate - callback for generating each instance of T
+  /// @returns pseudo-random data of type std::vector<T>
+  template <typename T, typename Callback>
+  std::vector<T> vector(Callback generate) {
+    auto count = build<uint8_t>();
+    std::vector<T> out(count);
+    for (size_t i = 0; i < count; i++) {
+      out[i] = generate(this);
+    }
+    return out;
+  }
+
+  /// Generate an pseudo-random entry to a enum class.
+  /// Assumes enum is tightly packed starting at 0.
+  /// @tparam T - type of enum class
+  /// @param count - number of entries in enum class
+  /// @returns a random enum class entry
+  template <typename T>
+  T enum_class(uint32_t count) {
+    return static_cast<T>(generator_.Get4Bytes() % count);
+  }
+
+ private:
+  RandomGenerator generator_;
+
+  /// Get N bytes of pseudo-random data
+  /// @param out - pointer to location to save data
+  /// @param n - number of bytes to get
+  void build(void* out, size_t n) {
+    assert(out != nullptr && "|out| cannot be nullptr");
+    assert(n > 0 && "|n| must be > 0");
+
+    generator_.GetNBytes(reinterpret_cast<uint8_t*>(out), n);
+  }
+
+  /// Implementation of ::build<T>()
+  /// @tparam T - type of data to produce
+  template <typename T>
+  struct BuildImpl {
+    /// Generate a pseudo-random variable of type T
+    /// @param b - data builder to use
+    /// @returns a variable of type T filled with pseudo-random data
+    static T impl(DataBuilder* b) {
+      T out{};
+      b->build(&out, sizeof(T));
+      return out;
+    }
+  };
+
+  /// Specialization for std::string
+  template <>
+  struct BuildImpl<std::string> {
+    /// Generate a pseudo-random string
+    /// @param b - data builder to use
+    /// @returns a string filled with pseudo-random data
+    static std::string impl(DataBuilder* b) {
+      auto count = b->build<uint8_t>();
+      if (count == 0) {
+        return "";
+      }
+      std::vector<uint8_t> source(count);
+      b->build(source.data(), count);
+      return std::string(source.begin(), source.end());
+    }
+  };
+
+  /// Specialization for bool
+  template <>
+  struct BuildImpl<bool> {
+    /// Generate a pseudo-random bool
+    /// @param b - data builder to use
+    /// @returns a boolean with even odds of being true or false
+    static bool impl(DataBuilder* b) { return b->generator_.GetBool(); }
+  };
+};
+
+}  // namespace fuzzers
+}  // namespace tint
+
+#endif  // FUZZERS_DATA_BUILDER_H_
diff --git a/fuzzers/tint_init_fuzzer.cc b/fuzzers/fuzzer_init.cc
similarity index 95%
rename from fuzzers/tint_init_fuzzer.cc
rename to fuzzers/fuzzer_init.cc
index b8e38cd..f651713 100644
--- a/fuzzers/tint_init_fuzzer.cc
+++ b/fuzzers/fuzzer_init.cc
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "fuzzers/tint_init_fuzzer.h"
+#include "fuzzers/fuzzer_init.h"
 #include "fuzzers/cli.h"
 
 namespace tint {
diff --git a/fuzzers/tint_init_fuzzer.h b/fuzzers/fuzzer_init.h
similarity index 87%
rename from fuzzers/tint_init_fuzzer.h
rename to fuzzers/fuzzer_init.h
index 278ab5b..fa96ce5 100644
--- a/fuzzers/tint_init_fuzzer.h
+++ b/fuzzers/fuzzer_init.h
@@ -12,8 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef FUZZERS_TINT_INIT_FUZZER_H_
-#define FUZZERS_TINT_INIT_FUZZER_H_
+#ifndef FUZZERS_FUZZER_INIT_H_
+#define FUZZERS_FUZZER_INIT_H_
 
 #include "fuzzers/cli.h"
 
@@ -26,4 +26,4 @@
 }  // namespace fuzzers
 }  // namespace tint
 
-#endif  // FUZZERS_TINT_INIT_FUZZER_H_
+#endif  // FUZZERS_FUZZER_INIT_H_
diff --git a/fuzzers/random_generator.cc b/fuzzers/random_generator.cc
index 20b04db..a485b0e 100644
--- a/fuzzers/random_generator.cc
+++ b/fuzzers/random_generator.cc
@@ -58,6 +58,9 @@
 
 RandomGenerator::RandomGenerator(uint64_t seed) : engine_(seed) {}
 
+RandomGenerator::RandomGenerator(const uint8_t* data, size_t size)
+    : engine_(RandomGenerator::CalculateSeed(data, size)) {}
+
 uint32_t RandomGenerator::GetUInt32(uint32_t lower, uint32_t upper) {
   return RandomUInt(&engine_, lower, upper);
 }
diff --git a/fuzzers/random_generator.h b/fuzzers/random_generator.h
index 1879045..4fe6acc 100644
--- a/fuzzers/random_generator.h
+++ b/fuzzers/random_generator.h
@@ -27,6 +27,12 @@
   /// @brief Initializes the internal engine
   /// @param seed - seed value passed to engine
   explicit RandomGenerator(uint64_t seed);
+
+  /// @brief Wrapper that invokes CalculateSeed for caller
+  /// @param data - data fuzzer to calculate seed from
+  /// @param size - size of data buffer
+  explicit RandomGenerator(const uint8_t* data, size_t size);
+
   ~RandomGenerator() {}
 
   /// Get uint32_t value from uniform distribution.
diff --git a/fuzzers/tint_all_transforms_fuzzer.cc b/fuzzers/tint_all_transforms_fuzzer.cc
index 083ee28..88b6c82 100644
--- a/fuzzers/tint_all_transforms_fuzzer.cc
+++ b/fuzzers/tint_all_transforms_fuzzer.cc
@@ -12,41 +12,21 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "fuzzers/fuzzer_init.h"
 #include "fuzzers/random_generator.h"
 #include "fuzzers/tint_common_fuzzer.h"
-#include "fuzzers/tint_init_fuzzer.h"
+#include "fuzzers/transform_builder.h"
 
 namespace tint {
 namespace fuzzers {
 
-struct Config {
-  Config(const uint8_t* data, size_t size) : builder(data, size) {}
-  DataBuilder builder;
-  transform::Manager manager;
-  transform::DataMap inputs;
-};
-
-void AddPlatformIndependentPasses(Config* config) {
-  GenerateFirstIndexOffsetInputs(&config->builder, &config->inputs);
-  GenerateBindingRemapperInputs(&config->builder, &config->inputs);
-  GenerateSingleEntryPointInputs(&config->builder, &config->inputs);
-  GenerateVertexPullingInputs(&config->builder, &config->inputs);
-
-  config->manager.Add<transform::Robustness>();
-  config->manager.Add<transform::FirstIndexOffset>();
-  config->manager.Add<transform::BindingRemapper>();
-  config->manager.Add<transform::Renamer>();
-  config->manager.Add<tint::transform::SingleEntryPoint>();
-  config->manager.Add<tint::transform::VertexPulling>();
-}
-
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
   {
-    Config config(data, size);
-    AddPlatformIndependentPasses(&config);
+    TransformBuilder tb(data, size);
+    tb.AddPlatformIndependentPasses();
 
     fuzzers::CommonFuzzer fuzzer(InputFormat::kWGSL, OutputFormat::kSpv);
-    fuzzer.SetTransformManager(&(config.manager), std::move(config.inputs));
+    fuzzer.SetTransformManager(tb.manager(), tb.data_map());
     fuzzer.SetDumpInput(GetCliParams().dump_input);
 
     fuzzer.Run(data, size);
@@ -54,11 +34,11 @@
 
 #if TINT_BUILD_HLSL_WRITER
   {
-    Config config(data, size);
-    AddPlatformIndependentPasses(&config);
+    TransformBuilder tb(data, size);
+    tb.AddPlatformIndependentPasses();
 
     fuzzers::CommonFuzzer fuzzer(InputFormat::kWGSL, OutputFormat::kHLSL);
-    fuzzer.SetTransformManager(&config.manager, std::move(config.inputs));
+    fuzzer.SetTransformManager(tb.manager(), tb.data_map());
     fuzzer.SetDumpInput(GetCliParams().dump_input);
 
     fuzzer.Run(data, size);
@@ -67,11 +47,11 @@
 
 #if TINT_BUILD_MSL_WRITER
   {
-    Config config(data, size);
-    AddPlatformIndependentPasses(&config);
+    TransformBuilder tb(data, size);
+    tb.AddPlatformIndependentPasses();
 
     fuzzers::CommonFuzzer fuzzer(InputFormat::kWGSL, OutputFormat::kMSL);
-    fuzzer.SetTransformManager(&config.manager, std::move(config.inputs));
+    fuzzer.SetTransformManager(tb.manager(), tb.data_map());
     fuzzer.SetDumpInput(GetCliParams().dump_input);
 
     fuzzer.Run(data, size);
@@ -79,11 +59,11 @@
 #endif  // TINT_BUILD_MSL_WRITER
 #if TINT_BUILD_SPV_WRITER
   {
-    Config config(data, size);
-    AddPlatformIndependentPasses(&config);
+    TransformBuilder tb(data, size);
+    tb.AddPlatformIndependentPasses();
 
     fuzzers::CommonFuzzer fuzzer(InputFormat::kWGSL, OutputFormat::kSpv);
-    fuzzer.SetTransformManager(&config.manager, std::move(config.inputs));
+    fuzzer.SetTransformManager(tb.manager(), tb.data_map());
     fuzzer.SetDumpInput(GetCliParams().dump_input);
 
     fuzzer.Run(data, size);
diff --git a/fuzzers/tint_ast_fuzzer/fuzzer.cc b/fuzzers/tint_ast_fuzzer/fuzzer.cc
index cdb1693..ca60cf8 100644
--- a/fuzzers/tint_ast_fuzzer/fuzzer.cc
+++ b/fuzzers/tint_ast_fuzzer/fuzzer.cc
@@ -115,7 +115,7 @@
 
     CommonFuzzer fuzzer(InputFormat::kWGSL, target.output_format);
     fuzzer.EnableInspector();
-    fuzzer.SetTransformManager(&transform_manager, std::move(transform_inputs));
+    fuzzer.SetTransformManager(&transform_manager, &transform_inputs);
 
     fuzzer.Run(data, size);
     if (fuzzer.HasErrors()) {
diff --git a/fuzzers/tint_binding_remapper_fuzzer.cc b/fuzzers/tint_binding_remapper_fuzzer.cc
index c701078..4deb2d8 100644
--- a/fuzzers/tint_binding_remapper_fuzzer.cc
+++ b/fuzzers/tint_binding_remapper_fuzzer.cc
@@ -12,23 +12,19 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "fuzzers/fuzzer_init.h"
 #include "fuzzers/tint_common_fuzzer.h"
-#include "fuzzers/tint_init_fuzzer.h"
+#include "fuzzers/transform_builder.h"
 
 namespace tint {
 namespace fuzzers {
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  transform::Manager transform_manager;
-  transform::DataMap transform_inputs;
-  DataBuilder b(data, size);
-
-  GenerateBindingRemapperInputs(&b, &transform_inputs);
-
-  transform_manager.Add<tint::transform::BindingRemapper>();
+  TransformBuilder tb(data, size);
+  tb.AddTransform<transform::BindingRemapper>();
 
   fuzzers::CommonFuzzer fuzzer(InputFormat::kWGSL, OutputFormat::kWGSL);
-  fuzzer.SetTransformManager(&transform_manager, std::move(transform_inputs));
+  fuzzer.SetTransformManager(tb.manager(), tb.data_map());
   fuzzer.SetDumpInput(GetCliParams().dump_input);
 
   return fuzzer.Run(data, size);
diff --git a/fuzzers/tint_common_fuzzer.cc b/fuzzers/tint_common_fuzzer.cc
index f15c733..ac4de23 100644
--- a/fuzzers/tint_common_fuzzer.cc
+++ b/fuzzers/tint_common_fuzzer.cc
@@ -52,26 +52,6 @@
   FatalError(diagnostics);
 }
 
-transform::VertexAttributeDescriptor GenerateVertexAttributeDescriptor(
-    DataBuilder* b) {
-  transform::VertexAttributeDescriptor desc{};
-  desc.format = b->enum_class<transform::VertexFormat>(
-      static_cast<uint8_t>(transform::VertexFormat::kLastEntry) + 1);
-  desc.offset = b->build<uint32_t>();
-  desc.shader_location = b->build<uint32_t>();
-  return desc;
-}
-
-transform::VertexBufferLayoutDescriptor GenerateVertexBufferLayoutDescriptor(
-    DataBuilder* b) {
-  transform::VertexBufferLayoutDescriptor desc;
-  desc.array_stride = b->build<uint32_t>();
-  desc.step_mode = b->enum_class<transform::VertexStepMode>(
-      static_cast<uint8_t>(transform::VertexStepMode::kLastEntry) + 1);
-  desc.attributes = b->vector(GenerateVertexAttributeDescriptor);
-  return desc;
-}
-
 bool SPIRVToolsValidationCheck(const tint::Program& program,
                                const std::vector<uint32_t>& spirv) {
   spvtools::SpirvTools tools(SPV_ENV_VULKAN_1_1);
@@ -94,76 +74,6 @@
 
 }  // namespace
 
-DataBuilder::DataBuilder(const uint8_t* data, size_t size)
-    : generator_(RandomGenerator::CalculateSeed(data, size)) {}
-
-std::string DataBuilder::string() {
-  auto count = build<uint8_t>();
-  if (count == 0) {
-    return "";
-  }
-  std::vector<uint8_t> source(count);
-  build(source.data(), count);
-  return std::string(source.begin(), source.end());
-}
-
-void DataBuilder::build(void* out, size_t n) {
-  assert(out != nullptr && "|out| cannot be nullptr");
-  assert(n > 0 && "|n| must be > 0");
-
-  generator_.GetNBytes(reinterpret_cast<uint8_t*>(out), n);
-}
-
-void GenerateBindingRemapperInputs(DataBuilder* b,
-                                   tint::transform::DataMap* inputs) {
-  struct Config {
-    uint8_t old_group;
-    uint8_t old_binding;
-    uint8_t new_group;
-    uint8_t new_binding;
-    ast::Access new_access;
-  };
-
-  std::vector<Config> configs = b->vector<Config>();
-  transform::BindingRemapper::BindingPoints binding_points;
-  transform::BindingRemapper::AccessControls accesses;
-  for (const auto& config : configs) {
-    binding_points[{config.old_binding, config.old_group}] = {
-        config.new_binding, config.new_group};
-    accesses[{config.old_binding, config.old_group}] = config.new_access;
-  }
-
-  inputs->Add<transform::BindingRemapper::Remappings>(binding_points, accesses);
-}
-
-void GenerateFirstIndexOffsetInputs(DataBuilder* b,
-                                    tint::transform::DataMap* inputs) {
-  struct Config {
-    uint32_t group;
-    uint32_t binding;
-  };
-
-  Config config = b->build<Config>();
-  inputs->Add<tint::transform::FirstIndexOffset::BindingPoint>(config.binding,
-                                                               config.group);
-}
-
-void GenerateSingleEntryPointInputs(DataBuilder* b,
-                                    tint::transform::DataMap* inputs) {
-  std::string input = b->string();
-  transform::SingleEntryPoint::Config cfg(input);
-  inputs->Add<transform::SingleEntryPoint::Config>(cfg);
-}
-
-void GenerateVertexPullingInputs(DataBuilder* b,
-                                 tint::transform::DataMap* inputs) {
-  transform::VertexPulling::Config cfg;
-  cfg.entry_point_name = b->string();
-  cfg.vertex_state = b->vector(GenerateVertexBufferLayoutDescriptor);
-  cfg.pulling_group = b->build<uint32_t>();
-  inputs->Add<transform::VertexPulling::Config>(cfg);
-}
-
 void GenerateSpirvOptions(DataBuilder* b, writer::spirv::Options* options) {
   *options = b->build<writer::spirv::Options>();
 }
@@ -181,10 +91,7 @@
 }
 
 CommonFuzzer::CommonFuzzer(InputFormat input, OutputFormat output)
-    : input_(input),
-      output_(output),
-      transform_manager_(nullptr),
-      inspector_enabled_(false) {}
+    : input_(input), output_(output) {}
 
 CommonFuzzer::~CommonFuzzer() = default;
 
@@ -345,7 +252,7 @@
   }
 
   if (transform_manager_) {
-    auto out = transform_manager_->Run(&program, transform_inputs_);
+    auto out = transform_manager_->Run(&program, *transform_inputs_);
     if (!out.program.IsValid()) {
       // Transforms can produce error messages for bad input.
       // Catch ICEs and errors from non transform systems.
diff --git a/fuzzers/tint_common_fuzzer.h b/fuzzers/tint_common_fuzzer.h
index 96aac01..975e563 100644
--- a/fuzzers/tint_common_fuzzer.h
+++ b/fuzzers/tint_common_fuzzer.h
@@ -15,71 +15,20 @@
 #ifndef FUZZERS_TINT_COMMON_FUZZER_H_
 #define FUZZERS_TINT_COMMON_FUZZER_H_
 
+#include <cassert>
 #include <cstring>
 #include <memory>
 #include <string>
 #include <utility>
 #include <vector>
 
-#include "fuzzers/random_generator.h"
 #include "include/tint/tint.h"
 
+#include "fuzzers/data_builder.h"
+
 namespace tint {
 namespace fuzzers {
 
-class DataBuilder {
- public:
-  DataBuilder(const uint8_t* data, size_t size);
-
-  template <typename T>
-  T build() {
-    T out{};
-    build(&out, sizeof(T));
-    return out;
-  }
-
-  std::string string();
-
-  template <typename T>
-  std::vector<T> vector() {
-    auto count = build<uint8_t>();
-    std::vector<T> out(count);
-    for (uint8_t i = 0; i < count; i++) {
-      out[i] = build<T>();
-    }
-    return out;
-  }
-
-  template <typename T>
-  std::vector<T> vector(T (*generate)(DataBuilder*)) {
-    auto count = build<uint8_t>();
-    std::vector<T> out(count);
-    for (uint8_t i = 0; i < count; i++) {
-      out[i] = generate(this);
-    }
-    return out;
-  }
-
-  template <typename T>
-  T enum_class(uint8_t count) {
-    auto val = build<uint8_t>();
-    return static_cast<T>(val % count);
-  }
-
- private:
-  void build(void* out, size_t n);
-
-  RandomGenerator generator_;
-};
-
-void GenerateBindingRemapperInputs(DataBuilder* b,
-                                   tint::transform::DataMap* inputs);
-void GenerateFirstIndexOffsetInputs(DataBuilder* b,
-                                    tint::transform::DataMap* inputs);
-void GenerateSingleEntryPointInputs(DataBuilder* b,
-                                    tint::transform::DataMap* inputs);
-void GenerateVertexPullingInputs(DataBuilder* b,
-                                 tint::transform::DataMap* inputs);
 void GenerateSpirvOptions(DataBuilder* b, writer::spirv::Options* options);
 void GenerateWgslOptions(DataBuilder* b, writer::wgsl::Options* options);
 void GenerateHlslOptions(DataBuilder* b, writer::hlsl::Options* options);
@@ -94,9 +43,10 @@
   explicit CommonFuzzer(InputFormat input, OutputFormat output);
   ~CommonFuzzer();
 
-  void SetTransformManager(transform::Manager* tm, transform::DataMap inputs) {
+  void SetTransformManager(transform::Manager* tm, transform::DataMap* inputs) {
+    assert((!tm || inputs) && "DataMap must be !nullptr if Manager !nullptr");
     transform_manager_ = tm;
-    transform_inputs_ = std::move(inputs);
+    transform_inputs_ = inputs;
   }
   void EnableInspector() { inspector_enabled_ = true; }
 
@@ -137,9 +87,9 @@
  private:
   InputFormat input_;
   OutputFormat output_;
-  transform::Manager* transform_manager_;
-  transform::DataMap transform_inputs_;
-  bool inspector_enabled_;
+  transform::Manager* transform_manager_ = nullptr;
+  transform::DataMap* transform_inputs_ = nullptr;
+  bool inspector_enabled_ = false;
   bool dump_input_ = false;
   tint::diag::List diagnostics_;
 
diff --git a/fuzzers/tint_first_index_offset_fuzzer.cc b/fuzzers/tint_first_index_offset_fuzzer.cc
index cf96e4a..f33529f 100644
--- a/fuzzers/tint_first_index_offset_fuzzer.cc
+++ b/fuzzers/tint_first_index_offset_fuzzer.cc
@@ -12,22 +12,19 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "fuzzers/fuzzer_init.h"
 #include "fuzzers/tint_common_fuzzer.h"
-#include "fuzzers/tint_init_fuzzer.h"
+#include "fuzzers/transform_builder.h"
 
 namespace tint {
 namespace fuzzers {
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  tint::transform::Manager transform_manager;
-  tint::transform::DataMap transform_inputs;
-  DataBuilder b(data, size);
+  TransformBuilder tb(data, size);
+  tb.AddTransform<transform::FirstIndexOffset>();
 
-  GenerateFirstIndexOffsetInputs(&b, &transform_inputs);
-  transform_manager.Add<tint::transform::FirstIndexOffset>();
-
-  tint::fuzzers::CommonFuzzer fuzzer(InputFormat::kWGSL, OutputFormat::kWGSL);
-  fuzzer.SetTransformManager(&transform_manager, std::move(transform_inputs));
+  fuzzers::CommonFuzzer fuzzer(InputFormat::kWGSL, OutputFormat::kWGSL);
+  fuzzer.SetTransformManager(tb.manager(), tb.data_map());
   fuzzer.SetDumpInput(GetCliParams().dump_input);
 
   return fuzzer.Run(data, size);
diff --git a/fuzzers/tint_inspector_fuzzer.cc b/fuzzers/tint_inspector_fuzzer.cc
index 9175ea2..0fa7198 100644
--- a/fuzzers/tint_inspector_fuzzer.cc
+++ b/fuzzers/tint_inspector_fuzzer.cc
@@ -12,21 +12,21 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "fuzzers/fuzzer_init.h"
 #include "fuzzers/tint_common_fuzzer.h"
-#include "fuzzers/tint_init_fuzzer.h"
+#include "fuzzers/transform_builder.h"
 
 namespace tint {
 namespace fuzzers {
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  transform::Manager transform_manager;
-  transform::DataMap transform_inputs;
-  transform_manager.Add<transform::Robustness>();
+  TransformBuilder tb(data, size);
+  tb.AddTransform<transform::Robustness>();
 
-  tint::fuzzers::CommonFuzzer fuzzer(InputFormat::kWGSL, OutputFormat::kWGSL);
+  fuzzers::CommonFuzzer fuzzer(InputFormat::kWGSL, OutputFormat::kWGSL);
   fuzzer.EnableInspector();
+  fuzzer.SetTransformManager(tb.manager(), tb.data_map());
   fuzzer.SetDumpInput(GetCliParams().dump_input);
-  fuzzer.SetTransformManager(&transform_manager, std::move(transform_inputs));
 
   return fuzzer.Run(data, size);
 }
diff --git a/fuzzers/tint_regex_fuzzer/fuzzer.cc b/fuzzers/tint_regex_fuzzer/fuzzer.cc
index 9eef8e3..1de67c7 100644
--- a/fuzzers/tint_regex_fuzzer/fuzzer.cc
+++ b/fuzzers/tint_regex_fuzzer/fuzzer.cc
@@ -143,7 +143,7 @@
 
     CommonFuzzer fuzzer(InputFormat::kWGSL, target.output_format);
     fuzzer.EnableInspector();
-    fuzzer.SetTransformManager(&transform_manager, std::move(transform_inputs));
+    fuzzer.SetTransformManager(&transform_manager, &transform_inputs);
 
     fuzzer.Run(data, size);
   }
diff --git a/fuzzers/tint_renamer_fuzzer.cc b/fuzzers/tint_renamer_fuzzer.cc
index 7528915..db70352 100644
--- a/fuzzers/tint_renamer_fuzzer.cc
+++ b/fuzzers/tint_renamer_fuzzer.cc
@@ -12,8 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "fuzzers/fuzzer_init.h"
 #include "fuzzers/tint_common_fuzzer.h"
-#include "fuzzers/tint_init_fuzzer.h"
 
 namespace tint {
 namespace fuzzers {
diff --git a/fuzzers/tint_robustness_fuzzer.cc b/fuzzers/tint_robustness_fuzzer.cc
index 3ae6121..e0296bb 100644
--- a/fuzzers/tint_robustness_fuzzer.cc
+++ b/fuzzers/tint_robustness_fuzzer.cc
@@ -12,8 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "fuzzers/fuzzer_init.h"
 #include "fuzzers/tint_common_fuzzer.h"
-#include "fuzzers/tint_init_fuzzer.h"
 
 namespace tint {
 namespace fuzzers {
diff --git a/fuzzers/tint_single_entry_point_fuzzer.cc b/fuzzers/tint_single_entry_point_fuzzer.cc
index 988e4d2..a226b1f 100644
--- a/fuzzers/tint_single_entry_point_fuzzer.cc
+++ b/fuzzers/tint_single_entry_point_fuzzer.cc
@@ -12,22 +12,19 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "fuzzers/fuzzer_init.h"
 #include "fuzzers/tint_common_fuzzer.h"
-#include "fuzzers/tint_init_fuzzer.h"
+#include "fuzzers/transform_builder.h"
 
 namespace tint {
 namespace fuzzers {
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  tint::transform::Manager transform_manager;
-  tint::transform::DataMap transform_inputs;
-  DataBuilder b(data, size);
+  TransformBuilder tb(data, size);
+  tb.AddTransform<transform::SingleEntryPoint>();
 
-  GenerateSingleEntryPointInputs(&b, &transform_inputs);
-  transform_manager.Add<tint::transform::SingleEntryPoint>();
-
-  tint::fuzzers::CommonFuzzer fuzzer(InputFormat::kWGSL, OutputFormat::kWGSL);
-  fuzzer.SetTransformManager(&transform_manager, std::move(transform_inputs));
+  fuzzers::CommonFuzzer fuzzer(InputFormat::kWGSL, OutputFormat::kWGSL);
+  fuzzer.SetTransformManager(tb.manager(), tb.data_map());
   fuzzer.SetDumpInput(GetCliParams().dump_input);
 
   return fuzzer.Run(data, size);
diff --git a/fuzzers/tint_spv_reader_fuzzer.cc b/fuzzers/tint_spv_reader_fuzzer.cc
index 1eb54ea..945b0f6 100644
--- a/fuzzers/tint_spv_reader_fuzzer.cc
+++ b/fuzzers/tint_spv_reader_fuzzer.cc
@@ -14,8 +14,8 @@
 
 #include <vector>
 
+#include "fuzzers/fuzzer_init.h"
 #include "fuzzers/tint_common_fuzzer.h"
-#include "fuzzers/tint_init_fuzzer.h"
 
 namespace tint {
 namespace fuzzers {
diff --git a/fuzzers/tint_spv_reader_hlsl_writer_fuzzer.cc b/fuzzers/tint_spv_reader_hlsl_writer_fuzzer.cc
index 505e8e1..5af7f9d 100644
--- a/fuzzers/tint_spv_reader_hlsl_writer_fuzzer.cc
+++ b/fuzzers/tint_spv_reader_hlsl_writer_fuzzer.cc
@@ -14,8 +14,8 @@
 
 #include <vector>
 
+#include "fuzzers/fuzzer_init.h"
 #include "fuzzers/tint_common_fuzzer.h"
-#include "fuzzers/tint_init_fuzzer.h"
 
 namespace tint {
 namespace fuzzers {
diff --git a/fuzzers/tint_spv_reader_msl_writer_fuzzer.cc b/fuzzers/tint_spv_reader_msl_writer_fuzzer.cc
index 6117d01..ed2ca58 100644
--- a/fuzzers/tint_spv_reader_msl_writer_fuzzer.cc
+++ b/fuzzers/tint_spv_reader_msl_writer_fuzzer.cc
@@ -14,8 +14,8 @@
 
 #include <vector>
 
+#include "fuzzers/fuzzer_init.h"
 #include "fuzzers/tint_common_fuzzer.h"
-#include "fuzzers/tint_init_fuzzer.h"
 
 namespace tint {
 namespace fuzzers {
diff --git a/fuzzers/tint_spv_reader_spv_writer_fuzzer.cc b/fuzzers/tint_spv_reader_spv_writer_fuzzer.cc
index 3d1a5c5..c3a03a4 100644
--- a/fuzzers/tint_spv_reader_spv_writer_fuzzer.cc
+++ b/fuzzers/tint_spv_reader_spv_writer_fuzzer.cc
@@ -14,8 +14,8 @@
 
 #include <vector>
 
+#include "fuzzers/fuzzer_init.h"
 #include "fuzzers/tint_common_fuzzer.h"
-#include "fuzzers/tint_init_fuzzer.h"
 
 namespace tint {
 namespace fuzzers {
diff --git a/fuzzers/tint_spv_reader_wgsl_writer_fuzzer.cc b/fuzzers/tint_spv_reader_wgsl_writer_fuzzer.cc
index d3883f8..3de88f5 100644
--- a/fuzzers/tint_spv_reader_wgsl_writer_fuzzer.cc
+++ b/fuzzers/tint_spv_reader_wgsl_writer_fuzzer.cc
@@ -14,8 +14,8 @@
 
 #include <vector>
 
+#include "fuzzers/fuzzer_init.h"
 #include "fuzzers/tint_common_fuzzer.h"
-#include "fuzzers/tint_init_fuzzer.h"
 
 namespace tint {
 namespace fuzzers {
diff --git a/fuzzers/tint_vertex_pulling_fuzzer.cc b/fuzzers/tint_vertex_pulling_fuzzer.cc
index a4f33a4..ff6182b 100644
--- a/fuzzers/tint_vertex_pulling_fuzzer.cc
+++ b/fuzzers/tint_vertex_pulling_fuzzer.cc
@@ -12,22 +12,19 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "fuzzers/fuzzer_init.h"
 #include "fuzzers/tint_common_fuzzer.h"
-#include "fuzzers/tint_init_fuzzer.h"
+#include "fuzzers/transform_builder.h"
 
 namespace tint {
 namespace fuzzers {
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  tint::transform::Manager transform_manager;
-  tint::transform::DataMap transform_inputs;
-  DataBuilder b(data, size);
-
-  GenerateVertexPullingInputs(&b, &transform_inputs);
-  transform_manager.Add<tint::transform::VertexPulling>();
+  TransformBuilder tb(data, size);
+  tb.AddTransform<transform::VertexPulling>();
 
   tint::fuzzers::CommonFuzzer fuzzer(InputFormat::kWGSL, OutputFormat::kWGSL);
-  fuzzer.SetTransformManager(&transform_manager, std::move(transform_inputs));
+  fuzzer.SetTransformManager(tb.manager(), tb.data_map());
   fuzzer.SetDumpInput(GetCliParams().dump_input);
 
   return fuzzer.Run(data, size);
diff --git a/fuzzers/tint_wgsl_reader_fuzzer.cc b/fuzzers/tint_wgsl_reader_fuzzer.cc
index f66e54b..71e9026 100644
--- a/fuzzers/tint_wgsl_reader_fuzzer.cc
+++ b/fuzzers/tint_wgsl_reader_fuzzer.cc
@@ -14,8 +14,8 @@
 
 #include <string>
 
+#include "fuzzers/fuzzer_init.h"
 #include "fuzzers/tint_common_fuzzer.h"
-#include "fuzzers/tint_init_fuzzer.h"
 
 namespace tint {
 namespace fuzzers {
diff --git a/fuzzers/tint_wgsl_reader_hlsl_writer_fuzzer.cc b/fuzzers/tint_wgsl_reader_hlsl_writer_fuzzer.cc
index 0465b7a..b9974c7 100644
--- a/fuzzers/tint_wgsl_reader_hlsl_writer_fuzzer.cc
+++ b/fuzzers/tint_wgsl_reader_hlsl_writer_fuzzer.cc
@@ -14,8 +14,8 @@
 
 #include <string>
 
+#include "fuzzers/fuzzer_init.h"
 #include "fuzzers/tint_common_fuzzer.h"
-#include "fuzzers/tint_init_fuzzer.h"
 
 namespace tint {
 namespace fuzzers {
diff --git a/fuzzers/tint_wgsl_reader_msl_writer_fuzzer.cc b/fuzzers/tint_wgsl_reader_msl_writer_fuzzer.cc
index e075a30..dd75513 100644
--- a/fuzzers/tint_wgsl_reader_msl_writer_fuzzer.cc
+++ b/fuzzers/tint_wgsl_reader_msl_writer_fuzzer.cc
@@ -14,8 +14,8 @@
 
 #include <string>
 
+#include "fuzzers/fuzzer_init.h"
 #include "fuzzers/tint_common_fuzzer.h"
-#include "fuzzers/tint_init_fuzzer.h"
 
 namespace tint {
 namespace fuzzers {
diff --git a/fuzzers/tint_wgsl_reader_spv_writer_fuzzer.cc b/fuzzers/tint_wgsl_reader_spv_writer_fuzzer.cc
index 74985c6..14275ee 100644
--- a/fuzzers/tint_wgsl_reader_spv_writer_fuzzer.cc
+++ b/fuzzers/tint_wgsl_reader_spv_writer_fuzzer.cc
@@ -14,8 +14,8 @@
 
 #include <string>
 
+#include "fuzzers/fuzzer_init.h"
 #include "fuzzers/tint_common_fuzzer.h"
-#include "fuzzers/tint_init_fuzzer.h"
 
 namespace tint {
 namespace fuzzers {
diff --git a/fuzzers/tint_wgsl_reader_wgsl_writer_fuzzer.cc b/fuzzers/tint_wgsl_reader_wgsl_writer_fuzzer.cc
index bbdb5ba..daf60e6 100644
--- a/fuzzers/tint_wgsl_reader_wgsl_writer_fuzzer.cc
+++ b/fuzzers/tint_wgsl_reader_wgsl_writer_fuzzer.cc
@@ -14,8 +14,8 @@
 
 #include <string>
 
+#include "fuzzers/fuzzer_init.h"
 #include "fuzzers/tint_common_fuzzer.h"
-#include "fuzzers/tint_init_fuzzer.h"
 
 namespace tint {
 namespace fuzzers {
diff --git a/fuzzers/transform_builder.h b/fuzzers/transform_builder.h
new file mode 100644
index 0000000..280c762
--- /dev/null
+++ b/fuzzers/transform_builder.h
@@ -0,0 +1,206 @@
+// Copyright 2021 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef FUZZERS_TRANSFORM_BUILDER_H_
+#define FUZZERS_TRANSFORM_BUILDER_H_
+
+#include <string>
+#include <vector>
+
+#include "include/tint/tint.h"
+
+#include "fuzzers/data_builder.h"
+
+namespace tint {
+namespace fuzzers {
+
+/// Fuzzer utility class to build inputs for transforms and setup the transform
+/// manager.
+class TransformBuilder {
+ public:
+  /// @brief Initialize the data source using a data buffer as a seed
+  /// @param data - pointer to a data buffer to use as a seed
+  /// @param size - size of data buffer
+  explicit TransformBuilder(const uint8_t* data, size_t size)
+      : builder_(data, size) {}
+  ~TransformBuilder() {}
+
+  transform::Manager* manager() { return &manager_; }
+  transform::DataMap* data_map() { return &data_map_; }
+  DataBuilder* builder() { return &builder_; }
+
+  /// Adds a transform and needed data to |manager_| and |data_map_|.
+  /// @tparam T - A class that inherits from transform::Transform and has an
+  ///             explicit specialization in AddTransformImpl.
+  template <typename T>
+  void AddTransform() {
+    static_assert(std::is_base_of<transform::Transform, T>::value,
+                  "T is not a transform::Transform");
+    AddTransformImpl<T>::impl(this);
+  }
+
+  /// Helper that invokes Add*Transform for all of the platform independent
+  /// passes.
+  void AddPlatformIndependentPasses() {
+    AddTransform<transform::Robustness>();
+    AddTransform<transform::FirstIndexOffset>();
+    AddTransform<transform::BindingRemapper>();
+    AddTransform<transform::Renamer>();
+    AddTransform<transform::SingleEntryPoint>();
+    AddTransform<transform::VertexPulling>();
+  }
+
+ private:
+  DataBuilder builder_;
+  transform::Manager manager_;
+  transform::DataMap data_map_;
+
+  /// Implementation of AddTransform, specialized for each transform that is
+  /// implemented. Default implementation intentionally deleted to cause compile
+  /// error if unimplemented type passed in.
+  /// @tparam T - A fuzzer transform
+  template <typename T>
+  struct AddTransformImpl;
+
+  /// Implementation of AddTransform for transform::Robustness
+  template <>
+  struct AddTransformImpl<transform::Robustness> {
+    /// Add instance of transform::Robustness to TransformBuilder
+    /// @param tb - TransformBuilder to add transform to
+    static void impl(TransformBuilder* tb) {
+      tb->manager()->Add<transform::Robustness>();
+    }
+  };
+
+  /// Implementation of AddTransform for transform::FirstIndexOffset
+  template <>
+  struct AddTransformImpl<transform::FirstIndexOffset> {
+    /// Add instance of transform::FirstIndexOffset to TransformBuilder
+    /// @param tb - TransformBuilder to add transform to
+    static void impl(TransformBuilder* tb) {
+      struct Config {
+        uint32_t group;
+        uint32_t binding;
+      };
+
+      Config config = tb->builder()->build<Config>();
+
+      tb->data_map()->Add<tint::transform::FirstIndexOffset::BindingPoint>(
+          config.binding, config.group);
+      tb->manager()->Add<transform::FirstIndexOffset>();
+    }
+  };
+
+  /// Implementation of AddTransform for transform::BindingRemapper
+  template <>
+  struct AddTransformImpl<transform::BindingRemapper> {
+    /// Add instance of transform::BindingRemapper to TransformBuilder
+    /// @param tb - TransformBuilder to add transform to
+    static void impl(TransformBuilder* tb) {
+      struct Config {
+        uint8_t old_group;
+        uint8_t old_binding;
+        uint8_t new_group;
+        uint8_t new_binding;
+        ast::Access new_access;
+      };
+
+      std::vector<Config> configs = tb->builder()->vector<Config>();
+      transform::BindingRemapper::BindingPoints binding_points;
+      transform::BindingRemapper::AccessControls accesses;
+      for (const auto& config : configs) {
+        binding_points[{config.old_binding, config.old_group}] = {
+            config.new_binding, config.new_group};
+        accesses[{config.old_binding, config.old_group}] = config.new_access;
+      }
+
+      tb->data_map()->Add<transform::BindingRemapper::Remappings>(
+          binding_points, accesses, tb->builder()->build<bool>());
+      tb->manager()->Add<transform::BindingRemapper>();
+    }
+  };
+
+  /// Implementation of AddTransform for transform::Renamer
+  template <>
+  struct AddTransformImpl<transform::Renamer> {
+    /// Add instance of transform::Renamer to TransformBuilder
+    /// @param tb - TransformBuilder to add transform to
+    static void impl(TransformBuilder* tb) {
+      tb->manager()->Add<transform::Renamer>();
+    }
+  };
+
+  /// Implementation of AddTransform for transform::SingleEntryPoint
+  template <>
+  struct AddTransformImpl<transform::SingleEntryPoint> {
+    /// Add instance of transform::SingleEntryPoint to TransformBuilder
+    /// @param tb - TransformBuilder to add transform to
+    static void impl(TransformBuilder* tb) {
+      auto input = tb->builder()->build<std::string>();
+      transform::SingleEntryPoint::Config cfg(input);
+
+      tb->data_map()->Add<transform::SingleEntryPoint::Config>(cfg);
+      tb->manager()->Add<transform::SingleEntryPoint>();
+    }
+  };  // struct AddTransformImpl<transform::SingleEntryPoint>
+
+  /// Implementation of AddTransform for transform::VertexPulling
+  template <>
+  struct AddTransformImpl<transform::VertexPulling> {
+    /// Add instance of transform::VertexPulling to TransformBuilder
+    /// @param tb - TransformBuilder to add transform to
+    static void impl(TransformBuilder* tb) {
+      transform::VertexPulling::Config cfg;
+      cfg.entry_point_name = tb->builder()->build<std::string>();
+      cfg.vertex_state =
+          tb->builder()->vector<transform::VertexBufferLayoutDescriptor>(
+              GenerateVertexBufferLayoutDescriptor);
+      cfg.pulling_group = tb->builder()->build<uint32_t>();
+
+      tb->data_map()->Add<transform::VertexPulling::Config>(cfg);
+      tb->manager()->Add<transform::VertexPulling>();
+    }
+
+   private:
+    /// Generate an instance of transform::VertexAttributeDescriptor
+    /// @param b - DataBuilder to use
+    static transform::VertexAttributeDescriptor
+    GenerateVertexAttributeDescriptor(DataBuilder* b) {
+      transform::VertexAttributeDescriptor desc{};
+      desc.format = b->enum_class<transform::VertexFormat>(
+          static_cast<uint8_t>(transform::VertexFormat::kLastEntry) + 1);
+      desc.offset = b->build<uint32_t>();
+      desc.shader_location = b->build<uint32_t>();
+      return desc;
+    }
+
+    /// Generate an instance of VertexBufferLayoutDescriptor
+    /// @param b - DataBuilder to use
+    static transform::VertexBufferLayoutDescriptor
+    GenerateVertexBufferLayoutDescriptor(DataBuilder* b) {
+      transform::VertexBufferLayoutDescriptor desc;
+      desc.array_stride = b->build<uint32_t>();
+      desc.step_mode = b->enum_class<transform::VertexStepMode>(
+          static_cast<uint8_t>(transform::VertexStepMode::kLastEntry) + 1);
+      desc.attributes = b->vector<transform::VertexAttributeDescriptor>(
+          GenerateVertexAttributeDescriptor);
+      return desc;
+    }
+  };
+};  // class TransformBuilder
+
+}  // namespace fuzzers
+}  // namespace tint
+
+#endif  // FUZZERS_TRANSFORM_BUILDER_H_