Add library for spirv-fuzz (#2618)

Adds a library for spirv-fuzz, consisting of a Fuzzer class that will
transform a module with respect to (a) facts about the module provided
via a FactManager class, and (b) a source of random numbers and
parameters to control the transformation process provided via a
FuzzerContext class.  Transformations will be applied via classes that
implement a FuzzerPass interface, and both facts and transformations
will be represented via protobuf messages.  Currently there are no
concrete facts, transformations nor fuzzer passes; these will follow.
diff --git a/include/spirv-tools/libspirv.h b/include/spirv-tools/libspirv.h
index 5cea101..82717e9 100644
--- a/include/spirv-tools/libspirv.h
+++ b/include/spirv-tools/libspirv.h
@@ -370,6 +370,8 @@
 
 typedef struct spv_reducer_options_t spv_reducer_options_t;
 
+typedef struct spv_fuzzer_options_t spv_fuzzer_options_t;
+
 // Type Definitions
 
 typedef spv_const_binary_t* spv_const_binary;
@@ -385,6 +387,8 @@
 typedef const spv_optimizer_options_t* spv_const_optimizer_options;
 typedef spv_reducer_options_t* spv_reducer_options;
 typedef const spv_reducer_options_t* spv_const_reducer_options;
+typedef spv_fuzzer_options_t* spv_fuzzer_options;
+typedef const spv_fuzzer_options_t* spv_const_fuzzer_options;
 
 // Platform API
 
@@ -590,6 +594,19 @@
 SPIRV_TOOLS_EXPORT void spvReducerOptionsSetFailOnValidationError(
     spv_reducer_options options, bool fail_on_validation_error);
 
+// Creates a fuzzer options object with default options. Returns a valid
+// options object. The object remains valid until it is passed into
+// |spvFuzzerOptionsDestroy|.
+SPIRV_TOOLS_EXPORT spv_fuzzer_options spvFuzzerOptionsCreate();
+
+// Destroys the given fuzzer options object.
+SPIRV_TOOLS_EXPORT void spvFuzzerOptionsDestroy(spv_fuzzer_options options);
+
+// Sets the seed with which the random number generator used by the fuzzer
+// should be initialized.
+SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetRandomSeed(
+    spv_fuzzer_options options, uint32_t seed);
+
 // Encodes the given SPIR-V assembly text to its binary representation. The
 // length parameter specifies the number of bytes for text. Encoded binary will
 // be stored into *binary. Any error will be written into *diagnostic if
diff --git a/include/spirv-tools/libspirv.hpp b/include/spirv-tools/libspirv.hpp
index da27404..b0fd4a2 100644
--- a/include/spirv-tools/libspirv.hpp
+++ b/include/spirv-tools/libspirv.hpp
@@ -191,6 +191,26 @@
   spv_reducer_options options_;
 };
 
+// A C++ wrapper around a fuzzer options object.
+class FuzzerOptions {
+ public:
+  FuzzerOptions() : options_(spvFuzzerOptionsCreate()) {}
+  ~FuzzerOptions() { spvFuzzerOptionsDestroy(options_); }
+
+  // Allow implicit conversion to the underlying object.
+  operator spv_fuzzer_options() const {  // NOLINT(google-explicit-constructor)
+    return options_;
+  }
+
+  // See spvFuzzerOptionsSetRandomSeed.
+  void set_random_seed(uint32_t seed) {
+    spvFuzzerOptionsSetRandomSeed(options_, seed);
+  }
+
+ private:
+  spv_fuzzer_options options_;
+};
+
 // C++ interface for SPIRV-Tools functionalities. It wraps the context
 // (including target environment and the corresponding SPIR-V grammar) and
 // provides methods for assembling, disassembling, and validating.
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt
index 0c0820d..93938f0 100644
--- a/source/CMakeLists.txt
+++ b/source/CMakeLists.txt
@@ -198,6 +198,7 @@
 
 add_subdirectory(opt)
 add_subdirectory(reduce)
+add_subdirectory(fuzz)
 add_subdirectory(link)
 
 set(SPIRV_SOURCES
@@ -233,6 +234,7 @@
   ${CMAKE_CURRENT_SOURCE_DIR}/spirv_constant.h
   ${CMAKE_CURRENT_SOURCE_DIR}/spirv_definition.h
   ${CMAKE_CURRENT_SOURCE_DIR}/spirv_endian.h
+  ${CMAKE_CURRENT_SOURCE_DIR}/spirv_fuzzer_options.h
   ${CMAKE_CURRENT_SOURCE_DIR}/spirv_optimizer_options.h
   ${CMAKE_CURRENT_SOURCE_DIR}/spirv_reducer_options.h
   ${CMAKE_CURRENT_SOURCE_DIR}/spirv_target_env.h
@@ -260,6 +262,7 @@
   ${CMAKE_CURRENT_SOURCE_DIR}/print.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/software_version.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/spirv_endian.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/spirv_fuzzer_options.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/spirv_optimizer_options.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/spirv_reducer_options.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/spirv_target_env.cpp
diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt
new file mode 100644
index 0000000..af25904
--- /dev/null
+++ b/source/fuzz/CMakeLists.txt
@@ -0,0 +1,90 @@
+# Copyright (c) 2019 Google LLC
+
+# 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.
+
+if(SPIRV_BUILD_FUZZER)
+  set(PROTOBUF_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/protobufs/spvtoolsfuzz.proto)
+
+  add_custom_command(
+        OUTPUT protobufs/spvtoolsfuzz.pb.cc protobufs/spvtoolsfuzz.pb.h
+        COMMAND protobuf::protoc
+        -I=${CMAKE_CURRENT_SOURCE_DIR}/protobufs
+        --cpp_out=protobufs
+        ${PROTOBUF_SOURCE}
+        DEPENDS ${PROTOBUF_SOURCE}
+        COMMENT "Generate protobuf sources from proto definition file."
+  )
+
+  set(SPIRV_TOOLS_FUZZ_SOURCES
+        fact_manager.h
+        fuzzer.h
+        fuzzer_context.h
+        fuzzer_pass.h
+        protobufs/spirvfuzz_protobufs.h
+        pseudo_random_generator.h
+        random_generator.h
+        ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.h
+
+        fact_manager.cpp
+        fuzzer.cpp
+        fuzzer_context.cpp
+        fuzzer_pass.cpp
+        pseudo_random_generator.cpp
+        random_generator.cpp
+        ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc
+        )
+
+  if(MSVC)
+    # Enable parallel builds across four cores for this lib
+    add_definitions(/MP4)
+  endif()
+
+  spvtools_pch(SPIRV_TOOLS_FUZZ_SOURCES pch_source_fuzz)
+
+  add_library(SPIRV-Tools-fuzz ${SPIRV_TOOLS_FUZZ_SOURCES})
+
+  spvtools_default_compile_options(SPIRV-Tools-fuzz)
+  target_compile_definitions(SPIRV-Tools-fuzz PUBLIC -DGOOGLE_PROTOBUF_NO_RTTI -DGOOGLE_PROTOBUF_USE_UNALIGNED=0)
+
+  # Compilation of the auto-generated protobuf source file will yield warnings,
+  # which we have no control over and thus wish to ignore.
+  if(${COMPILER_IS_LIKE_GNU})
+    set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc PROPERTIES COMPILE_FLAGS -w)
+  endif()
+  if(MSVC)
+    set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc PROPERTIES COMPILE_FLAGS /w)
+  endif()
+
+  target_include_directories(SPIRV-Tools-fuzz
+        PUBLIC ${spirv-tools_SOURCE_DIR}/include
+        PUBLIC ${SPIRV_HEADER_INCLUDE_DIR}
+        PRIVATE ${spirv-tools_BINARY_DIR}
+        PRIVATE ${CMAKE_BINARY_DIR})
+
+  # The fuzzer reuses a lot of functionality from the SPIRV-Tools library.
+  target_link_libraries(SPIRV-Tools-fuzz
+        PUBLIC ${SPIRV_TOOLS}
+        PUBLIC SPIRV-Tools-opt
+        PUBLIC protobuf::libprotobuf)
+
+  set_property(TARGET SPIRV-Tools-fuzz PROPERTY FOLDER "SPIRV-Tools libraries")
+  spvtools_check_symbol_exports(SPIRV-Tools-fuzz)
+
+  if(ENABLE_SPIRV_TOOLS_INSTALL)
+      install(TARGETS SPIRV-Tools-fuzz
+            RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+            LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+            ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
+  endif(ENABLE_SPIRV_TOOLS_INSTALL)
+
+endif(SPIRV_BUILD_FUZZER)
diff --git a/source/fuzz/fact_manager.cpp b/source/fuzz/fact_manager.cpp
new file mode 100644
index 0000000..1c19287
--- /dev/null
+++ b/source/fuzz/fact_manager.cpp
@@ -0,0 +1,46 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <utility>
+
+#include "source/fuzz/fact_manager.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FactManager::FactManager() = default;
+
+FactManager::~FactManager() = default;
+
+bool FactManager::AddFacts(const protobufs::FactSequence& initial_facts,
+                           opt::IRContext* context) {
+  for (auto& fact : initial_facts.fact()) {
+    if (!AddFact(fact, context)) {
+      // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2621) Provide
+      //  information about the fact that could not be added.
+      return false;
+    }
+  }
+  return true;
+}
+
+bool FactManager::AddFact(const spvtools::fuzz::protobufs::Fact&,
+                          spvtools::opt::IRContext*) {
+  assert(0 && "No facts are yet supported.");
+  return true;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/fact_manager.h b/source/fuzz/fact_manager.h
new file mode 100644
index 0000000..49b6d6d
--- /dev/null
+++ b/source/fuzz/fact_manager.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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 SOURCE_FUZZ_FACT_MANAGER_H_
+#define SOURCE_FUZZ_FACT_MANAGER_H_
+
+#include <utility>
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/opt/constants.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Keeps track of facts about the module being transformed on which the fuzzing
+// process can depend. Some initial facts can be provided, for example about
+// guarantees on the values of inputs to SPIR-V entry points. Transformations
+// may then rely on these facts, can add further facts that they establish.
+// Facts are intended to be simple properties that either cannot be deduced from
+// the module (such as properties that are guaranteed to hold for entry point
+// inputs), or that are established by transformations, likely to be useful for
+// future transformations, and not completely trivial to deduce straight from
+// the module.
+class FactManager {
+ public:
+  FactManager();
+
+  ~FactManager();
+
+  // Adds all the facts from |facts|, checking them for validity with respect to
+  // |context|. Returns true if and only if all facts are valid.
+  bool AddFacts(const protobufs::FactSequence& facts, opt::IRContext* context);
+
+  // Adds |fact| to the fact manager, checking it for validity with respect to
+  // |context|. Returns true if and only if the fact is valid.
+  bool AddFact(const protobufs::Fact& fact, opt::IRContext* context);
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // #define SOURCE_FUZZ_FACT_MANAGER_H_
diff --git a/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp
new file mode 100644
index 0000000..2f28d79
--- /dev/null
+++ b/source/fuzz/fuzzer.cpp
@@ -0,0 +1,109 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer.h"
+
+#include <cassert>
+#include <sstream>
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/pseudo_random_generator.h"
+#include "source/opt/build_module.h"
+#include "source/spirv_fuzzer_options.h"
+#include "source/util/make_unique.h"
+
+namespace spvtools {
+namespace fuzz {
+
+namespace {
+const uint32_t kIdBoundGap = 100;
+}
+
+struct Fuzzer::Impl {
+  explicit Impl(spv_target_env env) : target_env(env) {}
+
+  const spv_target_env target_env;  // Target environment.
+  MessageConsumer consumer;         // Message consumer.
+};
+
+Fuzzer::Fuzzer(spv_target_env env) : impl_(MakeUnique<Impl>(env)) {}
+
+Fuzzer::~Fuzzer() = default;
+
+void Fuzzer::SetMessageConsumer(MessageConsumer c) {
+  impl_->consumer = std::move(c);
+}
+
+Fuzzer::FuzzerResultStatus Fuzzer::Run(
+    const std::vector<uint32_t>& binary_in,
+    const protobufs::FactSequence& initial_facts,
+    std::vector<uint32_t>* binary_out, protobufs::TransformationSequence*,
+    spv_const_fuzzer_options options) const {
+  // Check compatibility between the library version being linked with and the
+  // header files being used.
+  GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+  spvtools::SpirvTools tools(impl_->target_env);
+  if (!tools.IsValid()) {
+    impl_->consumer(SPV_MSG_ERROR, nullptr, {},
+                    "Failed to create SPIRV-Tools interface; stopping.");
+    return Fuzzer::FuzzerResultStatus::kFailedToCreateSpirvToolsInterface;
+  }
+
+  // Initial binary should be valid.
+  if (!tools.Validate(&binary_in[0], binary_in.size())) {
+    impl_->consumer(SPV_MSG_ERROR, nullptr, {},
+                    "Initial binary is invalid; stopping.");
+    return Fuzzer::FuzzerResultStatus::kInitialBinaryInvalid;
+  }
+
+  // Build the module from the input binary.
+  std::unique_ptr<opt::IRContext> ir_context = BuildModule(
+      impl_->target_env, impl_->consumer, binary_in.data(), binary_in.size());
+  assert(ir_context);
+
+  // Make a PRNG, either from a given seed or from a random device.
+  PseudoRandomGenerator random_generator(
+      options->has_random_seed ? options->random_seed
+                               : (uint32_t)std::random_device()());
+
+  // The fuzzer will introduce new ids into the module.  The module's id bound
+  // gives the smallest id that can be used for this purpose.  We add an offset
+  // to this so that there is a sizeable gap between the ids used in the
+  // original module and the ids used for fuzzing, as a readability aid.
+  //
+  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2541) consider the
+  //  case where the maximum id bound is reached.
+  auto minimum_fresh_id = ir_context->module()->id_bound() + kIdBoundGap;
+  FuzzerContext fuzzer_context(&random_generator, minimum_fresh_id);
+
+  FactManager fact_manager;
+  if (!fact_manager.AddFacts(initial_facts, ir_context.get())) {
+    return Fuzzer::FuzzerResultStatus::kInitialFactsInvalid;
+  }
+
+  // Fuzzer passes will be created and applied here and will populate the
+  // output sequence of transformations.  Currently there are no passes.
+  // TODO(afd) Implement fuzzer passes and invoke them here.
+
+  // Encode the module as a binary.
+  ir_context->module()->ToBinary(binary_out, false);
+
+  return Fuzzer::FuzzerResultStatus::kComplete;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/fuzzer.h b/source/fuzz/fuzzer.h
new file mode 100644
index 0000000..f81f297
--- /dev/null
+++ b/source/fuzz/fuzzer.h
@@ -0,0 +1,74 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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 SOURCE_FUZZ_FUZZER_H_
+#define SOURCE_FUZZ_FUZZER_H_
+
+#include <memory>
+#include <vector>
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "spirv-tools/libspirv.hpp"
+
+namespace spvtools {
+namespace fuzz {
+
+// Transforms a SPIR-V module into a semantically equivalent SPIR-V module by
+// running a number of randomized fuzzer passes.
+class Fuzzer {
+ public:
+  // Possible statuses that can result from running the fuzzer.
+  enum class FuzzerResultStatus {
+    kComplete,
+    kFailedToCreateSpirvToolsInterface,
+    kInitialBinaryInvalid,
+    kInitialFactsInvalid,
+  };
+
+  // Constructs a fuzzer from the given target environment.
+  explicit Fuzzer(spv_target_env env);
+
+  // Disables copy/move constructor/assignment operations.
+  Fuzzer(const Fuzzer&) = delete;
+  Fuzzer(Fuzzer&&) = delete;
+  Fuzzer& operator=(const Fuzzer&) = delete;
+  Fuzzer& operator=(Fuzzer&&) = delete;
+
+  ~Fuzzer();
+
+  // Sets the message consumer to the given |consumer|. The |consumer| will be
+  // invoked once for each message communicated from the library.
+  void SetMessageConsumer(MessageConsumer consumer);
+
+  // Transforms |binary_in| to |binary_out| by running a number of randomized
+  // fuzzer passes, controlled via |options|.  Initial facts about the input
+  // binary and the context in which it will execute are provided via
+  // |initial_facts|.  The transformation sequence that was applied is returned
+  // via |transformation_sequence_out|.
+  FuzzerResultStatus Run(
+      const std::vector<uint32_t>& binary_in,
+      const protobufs::FactSequence& initial_facts,
+      std::vector<uint32_t>* binary_out,
+      protobufs::TransformationSequence* transformation_sequence_out,
+      spv_const_fuzzer_options options) const;
+
+ private:
+  struct Impl;                  // Opaque struct for holding internal data.
+  std::unique_ptr<Impl> impl_;  // Unique pointer to internal data.
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_H_
diff --git a/source/fuzz/fuzzer_context.cpp b/source/fuzz/fuzzer_context.cpp
new file mode 100644
index 0000000..11de612
--- /dev/null
+++ b/source/fuzz/fuzzer_context.cpp
@@ -0,0 +1,33 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
+                             uint32_t min_fresh_id)
+    : random_generator_(random_generator), next_fresh_id_(min_fresh_id) {}
+
+FuzzerContext::~FuzzerContext() = default;
+
+uint32_t FuzzerContext::GetFreshId() { return next_fresh_id_++; }
+
+RandomGenerator* FuzzerContext::GetRandomGenerator() {
+  return random_generator_;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/fuzzer_context.h b/source/fuzz/fuzzer_context.h
new file mode 100644
index 0000000..5ab0f6f
--- /dev/null
+++ b/source/fuzz/fuzzer_context.h
@@ -0,0 +1,52 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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 SOURCE_FUZZ_FUZZER_CONTEXT_H_
+#define SOURCE_FUZZ_FUZZER_CONTEXT_H_
+
+#include "source/fuzz/random_generator.h"
+#include "source/opt/function.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Encapsulates all parameters that control the fuzzing process, such as the
+// source of randomness and the probabilities with which transformations are
+// applied.
+class FuzzerContext {
+ public:
+  // Constructs a fuzzer context with a given random generator and the minimum
+  // value that can be used for fresh ids.
+  FuzzerContext(RandomGenerator* random_generator, uint32_t min_fresh_id);
+
+  ~FuzzerContext();
+
+  // Provides the random generator used to control fuzzing.
+  RandomGenerator* GetRandomGenerator();
+
+  // Yields an id that is guaranteed not to be used in the module being fuzzed,
+  // or to have been issued before.
+  uint32_t GetFreshId();
+
+ private:
+  // The source of randomness.
+  RandomGenerator* random_generator_;
+  // The next fresh id to be issued.
+  uint32_t next_fresh_id_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_CONTEXT_H_
diff --git a/source/fuzz/fuzzer_pass.cpp b/source/fuzz/fuzzer_pass.cpp
new file mode 100644
index 0000000..823f2e0
--- /dev/null
+++ b/source/fuzz/fuzzer_pass.cpp
@@ -0,0 +1,31 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPass::FuzzerPass(opt::IRContext* ir_context, FactManager* fact_manager,
+                       FuzzerContext* fuzzer_context,
+                       protobufs::TransformationSequence* transformations)
+    : ir_context_(ir_context),
+      fact_manager_(fact_manager),
+      fuzzer_context_(fuzzer_context),
+      transformations_(transformations) {}
+
+FuzzerPass::~FuzzerPass() = default;
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/fuzzer_pass.h b/source/fuzz/fuzzer_pass.h
new file mode 100644
index 0000000..4d0861e
--- /dev/null
+++ b/source/fuzz/fuzzer_pass.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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 SOURCE_FUZZ_FUZZER_PASS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Interface for applying a pass of transformations to a module.
+class FuzzerPass {
+ public:
+  FuzzerPass(opt::IRContext* ir_context, FactManager* fact_manager,
+             FuzzerContext* fuzzer_context,
+             protobufs::TransformationSequence* transformations);
+
+  virtual ~FuzzerPass();
+
+  // Applies the pass to the module |ir_context_|, assuming and updating
+  // facts from |fact_manager_|, and using |fuzzer_context_| to guide the
+  // process.  Appends to |transformations_| all transformations that were
+  // applied during the pass.
+  virtual void Apply() = 0;
+
+ protected:
+  opt::IRContext* GetIRContext() const { return ir_context_; }
+
+  FactManager* GetFactManager() const { return fact_manager_; }
+
+  FuzzerContext* GetFuzzerContext() const { return fuzzer_context_; }
+
+  protobufs::TransformationSequence* GetTransformations() const {
+    return transformations_;
+  }
+
+ private:
+  opt::IRContext* ir_context_;
+  FactManager* fact_manager_;
+  FuzzerContext* fuzzer_context_;
+  protobufs::TransformationSequence* transformations_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // #define SOURCE_FUZZ_FUZZER_PASS_H_
diff --git a/source/fuzz/protobufs/spirvfuzz_protobufs.h b/source/fuzz/protobufs/spirvfuzz_protobufs.h
new file mode 100644
index 0000000..b801626
--- /dev/null
+++ b/source/fuzz/protobufs/spirvfuzz_protobufs.h
@@ -0,0 +1,52 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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 SOURCE_FUZZ_SPIRVFUZZ_PROTOBUFS_H_
+#define SOURCE_FUZZ_SPIRVFUZZ_PROTOBUFS_H_
+
+// This header file serves to act as a barrier between the protobuf header
+// files and files that include them.  It uses compiler pragmas to disable
+// diagnostics, in order to ignore warnings generated during the processing
+// of these header files without having to compromise on freedom from warnings
+// in the rest of the project.
+
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-parameter"
+#elif defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wconversion"
+#pragma GCC diagnostic ignored "-Wshadow"
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#elif defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable : 4244)
+#endif
+
+// The following should be the only place in the project where protobuf files
+// are directly included.  This is so that they can be compiled in a manner
+// where warnings are ignored.
+
+#include "google/protobuf/util/json_util.h"
+#include "source/fuzz/protobufs/spvtoolsfuzz.pb.h"
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#elif defined(__GNUC__)
+#pragma GCC diagnostic pop
+#elif defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+#endif  // SOURCE_FUZZ_SPIRVFUZZ_PROTOBUFS_H_
diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto
new file mode 100644
index 0000000..e87819a
--- /dev/null
+++ b/source/fuzz/protobufs/spvtoolsfuzz.proto
@@ -0,0 +1,38 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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.
+
+// This file is specifically named spvtools_fuzz.proto so that the string
+// 'spvtools_fuzz' appears in the names of global-scope symbols that protoc
+// generates when targeting C++.  This is to reduce the potential for name
+// clashes with other globally-scoped symbols.
+
+syntax = "proto3";
+
+package spvtools.fuzz.protobufs;
+
+message FactSequence {
+  repeated Fact fact = 1;
+}
+
+message Fact {
+  // Currently there are no facts.
+}
+
+message TransformationSequence {
+  repeated Transformation transformation = 1;
+}
+
+message Transformation {
+  // Currently there are no transformations.
+}
diff --git a/source/fuzz/pseudo_random_generator.cpp b/source/fuzz/pseudo_random_generator.cpp
new file mode 100644
index 0000000..773b89d
--- /dev/null
+++ b/source/fuzz/pseudo_random_generator.cpp
@@ -0,0 +1,47 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <cassert>
+
+#include "source/fuzz/pseudo_random_generator.h"
+
+namespace spvtools {
+namespace fuzz {
+
+PseudoRandomGenerator::PseudoRandomGenerator(uint32_t seed) : mt_(seed) {}
+
+PseudoRandomGenerator::~PseudoRandomGenerator() = default;
+
+uint32_t PseudoRandomGenerator::RandomUint32(uint32_t bound) {
+  assert(bound > 0 && "Bound must be positive");
+  return static_cast<uint32_t>(
+      std::uniform_int_distribution<>(0, bound - 1)(mt_));
+}
+
+bool PseudoRandomGenerator::RandomBool() {
+  return static_cast<bool>(std::uniform_int_distribution<>(0, 1)(mt_));
+}
+
+uint32_t PseudoRandomGenerator::RandomPercentage() {
+  // We use 101 because we want a result in the closed interval [0, 100], and
+  // RandomUint32 is not inclusive of its bound.
+  return RandomUint32(101);
+}
+
+double PseudoRandomGenerator::RandomDouble() {
+  return std::uniform_real_distribution<double>(0.0, 1.0)(mt_);
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/pseudo_random_generator.h b/source/fuzz/pseudo_random_generator.h
new file mode 100644
index 0000000..d2f5292
--- /dev/null
+++ b/source/fuzz/pseudo_random_generator.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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 SOURCE_FUZZ_PSEUDO_RANDOM_GENERATOR_H_
+#define SOURCE_FUZZ_PSEUDO_RANDOM_GENERATOR_H_
+
+#include <random>
+
+#include "source/fuzz/random_generator.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Generates random data from a pseudo-random number generator.
+class PseudoRandomGenerator : public RandomGenerator {
+ public:
+  explicit PseudoRandomGenerator(uint32_t seed);
+
+  ~PseudoRandomGenerator() override;
+
+  uint32_t RandomUint32(uint32_t bound) override;
+
+  uint32_t RandomPercentage() override;
+
+  bool RandomBool() override;
+
+  double RandomDouble() override;
+
+ private:
+  std::mt19937 mt_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_PSEUDO_RANDOM_GENERATOR_H_
diff --git a/source/fuzz/random_generator.cpp b/source/fuzz/random_generator.cpp
new file mode 100644
index 0000000..9ec4845
--- /dev/null
+++ b/source/fuzz/random_generator.cpp
@@ -0,0 +1,25 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/random_generator.h"
+
+namespace spvtools {
+namespace fuzz {
+
+RandomGenerator::RandomGenerator() = default;
+
+RandomGenerator::~RandomGenerator() = default;
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/random_generator.h b/source/fuzz/random_generator.h
new file mode 100644
index 0000000..9c46798
--- /dev/null
+++ b/source/fuzz/random_generator.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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 SOURCE_FUZZ_RANDOM_GENERATOR_H_
+#define SOURCE_FUZZ_RANDOM_GENERATOR_H_
+
+#include <stdint.h>
+
+namespace spvtools {
+namespace fuzz {
+
+class RandomGenerator {
+ public:
+  RandomGenerator();
+
+  virtual ~RandomGenerator();
+
+  // Returns a value in the half-open interval [0, bound).
+  virtual uint32_t RandomUint32(uint32_t bound) = 0;
+
+  // Returns a value in the closed interval [0, 100].
+  virtual uint32_t RandomPercentage() = 0;
+
+  // Returns a boolean.
+  virtual bool RandomBool() = 0;
+
+  // Returns a double in the closed interval [0, 1]
+  virtual double RandomDouble() = 0;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_RANDOM_GENERATOR_H_
diff --git a/source/spirv_fuzzer_options.cpp b/source/spirv_fuzzer_options.cpp
new file mode 100644
index 0000000..f6319ca
--- /dev/null
+++ b/source/spirv_fuzzer_options.cpp
@@ -0,0 +1,31 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/spirv_fuzzer_options.h"
+
+spv_fuzzer_options_t::spv_fuzzer_options_t() = default;
+
+SPIRV_TOOLS_EXPORT spv_fuzzer_options spvFuzzerOptionsCreate() {
+  return new spv_fuzzer_options_t();
+}
+
+SPIRV_TOOLS_EXPORT void spvFuzzerOptionsDestroy(spv_fuzzer_options options) {
+  delete options;
+}
+
+SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetRandomSeed(
+    spv_fuzzer_options options, uint32_t seed) {
+  options->has_random_seed = true;
+  options->random_seed = seed;
+}
diff --git a/source/spirv_fuzzer_options.h b/source/spirv_fuzzer_options.h
new file mode 100644
index 0000000..1193bfd
--- /dev/null
+++ b/source/spirv_fuzzer_options.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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 SOURCE_SPIRV_FUZZER_OPTIONS_H_
+#define SOURCE_SPIRV_FUZZER_OPTIONS_H_
+
+#include "spirv-tools/libspirv.h"
+
+#include <string>
+#include <utility>
+
+// Manages command line options passed to the SPIR-V Fuzzer. New struct
+// members may be added for any new option.
+struct spv_fuzzer_options_t {
+  spv_fuzzer_options_t();
+
+  // See spvFuzzerOptionsSetRandomSeed.
+  bool has_random_seed = false;
+  uint32_t random_seed = 0;
+};
+
+#endif  // SOURCE_SPIRV_FUZZER_OPTIONS_H_
diff --git a/utils/check_symbol_exports.py b/utils/check_symbol_exports.py
index d4c8579..a8f3785 100755
--- a/utils/check_symbol_exports.py
+++ b/utils/check_symbol_exports.py
@@ -56,6 +56,13 @@
     #   _ZN               :  something in a namespace
     #   _Z[0-9]+spv[A-Z_] :  C++ symbol starting with spv[A-Z_]
     symbol_ok_pattern = re.compile(r'^(spv[A-Z]|_ZN|_Z[0-9]+spv[A-Z_])')
+
+    # In addition, the following pattern whitelists global functions that are added
+    # by the protobuf compiler:
+    #   - AddDescriptors_spvtoolsfuzz_2eproto()
+    #   - InitDefaults_spvtoolsfuzz_2eproto()
+    symbol_whitelist_pattern = re.compile(r'_Z[0-9]+(InitDefaults|AddDescriptors)_spvtoolsfuzz_2eprotov')
+
     seen = set()
     result = 0
     for line in command_output(['objdump', '-t', library], '.').split('\n'):
@@ -65,7 +72,7 @@
             if symbol not in seen:
                 seen.add(symbol)
                 #print("look at '{}'".format(symbol))
-                if not symbol_ok_pattern.match(symbol):
+                if not (symbol_whitelist_pattern.match(symbol) or symbol_ok_pattern.match(symbol)):
                     print('{}: error: Unescaped exported symbol: {}'.format(PROG, symbol))
                     result = 1
     return result