Add Pass, PassManager, and StripDebugInfoPass.
diff --git a/source/opt/CMakeLists.txt b/source/opt/CMakeLists.txt
index 1d1c27d..24a201d 100644
--- a/source/opt/CMakeLists.txt
+++ b/source/opt/CMakeLists.txt
@@ -6,12 +6,15 @@
   libspirv.hpp
   module.h
   reflect.h
+  passes.h
+  pass_manager.h
 
   function.cpp
   instruction.cpp
   ir_loader.cpp
   libspirv.cpp
   module.cpp
+  passes.cpp
 )
 
 spvtools_default_compile_options(SPIRV-Tools-opt)
diff --git a/source/opt/pass_manager.h b/source/opt/pass_manager.h
new file mode 100644
index 0000000..7ebd6f8
--- /dev/null
+++ b/source/opt/pass_manager.h
@@ -0,0 +1,84 @@
+// Copyright (c) 2016 Google Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and/or associated documentation files (the
+// "Materials"), to deal in the Materials without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Materials, and to
+// permit persons to whom the Materials are furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Materials.
+//
+// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
+// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
+// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
+//    https://www.khronos.org/registry/
+//
+// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+
+#ifndef LIBSPIRV_OPT_PASS_MANAGER_H_
+#define LIBSPIRV_OPT_PASS_MANAGER_H_
+
+#include <cassert>
+#include <memory>
+#include <vector>
+
+#include "module.h"
+#include "passes.h"
+
+namespace spvtools {
+namespace opt {
+
+// The pass manager, responsible for tracking and running passes.
+// Clients should first call AddPass() to add passes and then call Run()
+// to run on a module. Passes are executed in the exact order of added.
+//
+// TODO(antiagainst): The pass manager is fairly simple right now. Eventually it
+// should support pass dependency, common functionality (like def-use analysis)
+// sharing, etc.
+class PassManager {
+ public:
+  // Adds a pass.
+  void AddPass(std::unique_ptr<Pass> pass) {
+    passes_.push_back(std::move(pass));
+  }
+  template <typename PassT>
+  void AddPass() {
+    passes_.emplace_back(new PassT);
+  }
+
+  // Returns the number of passes added.
+  uint32_t NumPasses() const { return static_cast<uint32_t>(passes_.size()); }
+  // Returns a pointer to the |index|th pass added.
+  Pass* GetPass(uint32_t index) const {
+    assert(index < passes_.size() && "index out of bound");
+    return passes_[index].get();
+  }
+
+  // Runs all passes on the given |module|.
+  void Run(ir::Module* module) {
+    for (const auto& pass : passes_) {
+      // TODO(antiagainst): Currently we ignore the return value of the pass,
+      // which indicates whether the module has been modified, since there is
+      // nothing shared between passes right now.
+      pass->Process(module);
+    }
+  }
+
+ private:
+  // A vector of passes. Order matters.
+  std::vector<std::unique_ptr<Pass>> passes_;
+};
+
+}  // namespace opt
+}  // namespace spvtools
+
+#endif  // LIBSPIRV_OPT_PASS_MANAGER_H_
diff --git a/source/opt/passes.cpp b/source/opt/passes.cpp
new file mode 100644
index 0000000..3749c53
--- /dev/null
+++ b/source/opt/passes.cpp
@@ -0,0 +1,45 @@
+// Copyright (c) 2016 Google Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and/or associated documentation files (the
+// "Materials"), to deal in the Materials without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Materials, and to
+// permit persons to whom the Materials are furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Materials.
+//
+// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
+// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
+// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
+//    https://www.khronos.org/registry/
+//
+// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+
+#include "passes.h"
+
+namespace spvtools {
+namespace opt {
+
+bool StripDebugInfoPass::Process(ir::Module* module) {
+  bool modified = !module->debugs().empty();
+  module->debugs().clear();
+
+  module->ForEachInst([&modified](ir::Instruction* inst) {
+    modified |= !inst->dbg_line_insts().empty();
+    inst->dbg_line_insts().clear();
+  });
+
+  return modified;
+}
+
+}  // namespace opt
+}  // namespace spvtools
diff --git a/source/opt/passes.h b/source/opt/passes.h
new file mode 100644
index 0000000..cb07e0b
--- /dev/null
+++ b/source/opt/passes.h
@@ -0,0 +1,64 @@
+// Copyright (c) 2016 Google Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and/or associated documentation files (the
+// "Materials"), to deal in the Materials without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Materials, and to
+// permit persons to whom the Materials are furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Materials.
+//
+// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
+// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
+// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
+//    https://www.khronos.org/registry/
+//
+// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+
+#ifndef LIBSPIRV_OPT_PASSES_H_
+#define LIBSPIRV_OPT_PASSES_H_
+
+#include <memory>
+
+#include "module.h"
+
+namespace spvtools {
+namespace opt {
+
+// A pass. All analysis and transformation is done via the Process() method.
+class Pass {
+ public:
+  // Returns a descriptive name for this pass.
+  virtual const char* name() const = 0;
+  // Processes the given |module| and returns true if the given |module| is
+  // modified for optimization.
+  virtual bool Process(ir::Module* module) = 0;
+};
+
+// A null pass that does nothing.
+class NullPass : public Pass {
+  const char* name() const override { return "Null"; }
+  bool Process(ir::Module*) override { return false; }
+};
+
+// The optimization pass for removing debug instructions (as documented in
+// Section 3.32.2 of the SPIR-V spec).
+class StripDebugInfoPass : public Pass {
+ public:
+  const char* name() const override { return "StripDebugInfo"; }
+  bool Process(ir::Module* module) override;
+};
+
+}  // namespace opt
+}  // namespace spvtools
+
+#endif  // LIBSPIRV_OPT_PASSES_H_
diff --git a/test/opt/CMakeLists.txt b/test/opt/CMakeLists.txt
index 9f786de..f83b793 100644
--- a/test/opt/CMakeLists.txt
+++ b/test/opt/CMakeLists.txt
@@ -28,3 +28,18 @@
   SRCS test_ir_loader.cpp
   LIBS SPIRV-Tools-opt ${SPIRV_TOOLS}
 )
+
+add_spvtools_unittest(TARGET pass_manager
+  SRCS test_pass_manager.cpp
+  LIBS SPIRV-Tools-opt ${SPIRV_TOOLS}
+)
+
+add_spvtools_unittest(TARGET pass_strip_debug_info
+  SRCS test_strip_debug_info.cpp pass_utils.cpp
+  LIBS SPIRV-Tools-opt ${SPIRV_TOOLS}
+)
+
+add_spvtools_unittest(TARGET pass_utils
+  SRCS test_utils.cpp pass_utils.cpp
+  LIBS SPIRV-Tools-opt ${SPIRV_TOOLS}
+)
diff --git a/test/opt/pass_fixture.h b/test/opt/pass_fixture.h
new file mode 100644
index 0000000..0c3b2df
--- /dev/null
+++ b/test/opt/pass_fixture.h
@@ -0,0 +1,114 @@
+// Copyright (c) 2016 Google Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and/or associated documentation files (the
+// "Materials"), to deal in the Materials without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Materials, and to
+// permit persons to whom the Materials are furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Materials.
+//
+// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
+// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
+// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
+//    https://www.khronos.org/registry/
+//
+// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+
+#ifndef LIBSPIRV_TEST_OPT_PASS_FIXTURE_H_
+#define LIBSPIRV_TEST_OPT_PASS_FIXTURE_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "opt/libspirv.hpp"
+#include "opt/pass_manager.h"
+#include "opt/passes.h"
+
+namespace spvtools {
+
+// Template class for testing passes. It contains some handy utility methods for
+// running passes and checking results.
+//
+// To write value-Parameterized tests:
+//   using ValueParamTest = PassTest<::testing::TestWithParam<std::string>>;
+// To use as normal fixture:
+//   using FixtureTest = PassTest<::testing::Test>;
+template <typename TestT>
+class PassTest : public TestT {
+ public:
+  PassTest()
+      : tools_(SPV_ENV_UNIVERSAL_1_1), manager_(new opt::PassManager()) {}
+
+  // Runs a single pass of class |PassT| on the binary assembled from the
+  // |original| assembly, and checks whether the optimized binary can be
+  // disassembled to the |expected| assembly. This does *not* involve pass
+  // manager. Callers are suggested to use SCOPED_TRACE() for better messages.
+  template <typename PassT>
+  void SinglePassRunAndCheck(const std::string& original,
+                             const std::string& expected) {
+    std::unique_ptr<ir::Module> module = tools_.BuildModule(original);
+    ASSERT_NE(nullptr, module);
+
+    const bool modified =
+        std::unique_ptr<PassT>(new PassT)->Process(module.get());
+    // Check whether the pass returns the correct modification indication.
+    EXPECT_EQ(original != expected, modified);
+
+    std::vector<uint32_t> binary;
+    module->ToBinary(&binary, /* skip_nop = */ false);
+
+    std::string optimized;
+    EXPECT_EQ(SPV_SUCCESS, tools_.Disassemble(binary, &optimized));
+    EXPECT_EQ(expected, optimized);
+  }
+
+  // Adds a pass to be run.
+  template <typename PassT>
+  void AddPass() {
+    manager_->AddPass<PassT>();
+  }
+
+  // Renews the pass manager, including clearing all previously added passes.
+  void RenewPassManger() { manager_.reset(new opt::PassManager()); }
+
+  // Runs the passes added thus far using a pass manager on the binary assembled
+  // from the |original| assembly, and checks whether the optimized binary can
+  // be disassembled to the |expected| assembly. Callers are suggested to use
+  // SCOPED_TRACE() for better messages.
+  void RunAndCheck(const std::string& original, const std::string& expected) {
+    assert(manager_->NumPasses());
+
+    std::unique_ptr<ir::Module> module = tools_.BuildModule(original);
+    ASSERT_NE(nullptr, module);
+
+    manager_->Run(module.get());
+
+    std::vector<uint32_t> binary;
+    module->ToBinary(&binary, /* skip_nop = */ false);
+
+    std::string optimized;
+    EXPECT_EQ(SPV_SUCCESS, tools_.Disassemble(binary, &optimized));
+    EXPECT_EQ(expected, optimized);
+  }
+
+ private:
+  SpvTools tools_;  // An instance for calling SPIRV-Tools functionalities.
+  std::unique_ptr<opt::PassManager> manager_;  // The pass manager.
+};
+
+}  // namespace spvtools
+
+#endif  // LIBSPIRV_TEST_OPT_PASS_FIXTURE_H_
diff --git a/test/opt/pass_utils.cpp b/test/opt/pass_utils.cpp
new file mode 100644
index 0000000..a6f2374
--- /dev/null
+++ b/test/opt/pass_utils.cpp
@@ -0,0 +1,77 @@
+// Copyright (c) 2016 Google Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and/or associated documentation files (the
+// "Materials"), to deal in the Materials without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Materials, and to
+// permit persons to whom the Materials are furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Materials.
+//
+// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
+// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
+// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
+//    https://www.khronos.org/registry/
+//
+// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+
+#include "pass_utils.h"
+
+#include <algorithm>
+#include <sstream>
+
+namespace {
+
+// Well, this is another place requiring the knowledge of the grammar and can be
+// stale when SPIR-V is updated. It would be nice to automatically generate
+// this, but the cost is just too high.
+
+const char* kDebugOpcodes[] = {
+    // clang-format off
+    "OpSourceContinued", "OpSource", "OpSourceExtension",
+    "OpName", "OpMemberName", "OpString",
+    "OpLine", "OpNoLine", "OpModuleProcessed"
+    // clang-format on
+};
+
+// Returns true if the given string contains any debug opcode substring.
+bool ContainsDebugOpcode(const char* inst) {
+  return std::any_of(std::begin(kDebugOpcodes), std::end(kDebugOpcodes),
+                     [inst](const char* op) {
+                       return std::string(inst).find(op) != std::string::npos;
+                     });
+}
+
+}  // anonymous namespace
+
+namespace spvtools {
+
+std::string SelectiveJoin(const std::vector<const char*>& strings,
+                          const std::function<bool(const char*)>& skip_dictator,
+                          char delimiter) {
+  std::ostringstream oss;
+  for (const auto* str : strings) {
+    if (!skip_dictator(str)) oss << str << delimiter;
+  }
+  return oss.str();
+}
+
+std::string JoinAllInsts(const std::vector<const char*>& insts) {
+  return SelectiveJoin(insts, [](const char*) { return false; });
+}
+
+std::string JoinNonDebugInsts(const std::vector<const char*>& insts) {
+  return SelectiveJoin(
+      insts, [](const char* inst) { return ContainsDebugOpcode(inst); });
+}
+
+}  // namespace spvtools
diff --git a/test/opt/pass_utils.h b/test/opt/pass_utils.h
new file mode 100644
index 0000000..6e047db
--- /dev/null
+++ b/test/opt/pass_utils.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2016 Google Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and/or associated documentation files (the
+// "Materials"), to deal in the Materials without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Materials, and to
+// permit persons to whom the Materials are furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Materials.
+//
+// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
+// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
+// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
+//    https://www.khronos.org/registry/
+//
+// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+
+#ifndef LIBSPIRV_TEST_OPT_PASS_UTILS_H_
+#define LIBSPIRV_TEST_OPT_PASS_UTILS_H_
+
+#include <functional>
+#include <string>
+#include <vector>
+
+namespace spvtools {
+
+// Returns the concatenated string from a vector of |strings|, with postfixing
+// each string with the given |delimiter|. if the |skip_dictator| returns true
+// for an original string, that string will be omitted.
+std::string SelectiveJoin(const std::vector<const char*>& strings,
+                          const std::function<bool(const char*)>& skip_dictator,
+                          char delimiter = '\n');
+
+// Concatenates a vector of strings into one string. Each string is postfixed
+// with '\n'.
+std::string JoinAllInsts(const std::vector<const char*>& insts);
+
+// Concatenates a vector of strings into one string. Each string is postfixed
+// with '\n'. If a string contains opcode for debug instruction, that string
+// will be ignored.
+std::string JoinNonDebugInsts(const std::vector<const char*>& insts);
+
+}  // namespace spvtools
+
+#endif  // LIBSPIRV_TEST_OPT_PASS_UTILS_H_
diff --git a/test/opt/test_pass_manager.cpp b/test/opt/test_pass_manager.cpp
new file mode 100644
index 0000000..cf2f2f6
--- /dev/null
+++ b/test/opt/test_pass_manager.cpp
@@ -0,0 +1,92 @@
+// Copyright (c) 2016 Google Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and/or associated documentation files (the
+// "Materials"), to deal in the Materials without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Materials, and to
+// permit persons to whom the Materials are furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Materials.
+//
+// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
+// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
+// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
+//    https://www.khronos.org/registry/
+//
+// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+
+#include "pass_fixture.h"
+
+namespace {
+
+using namespace spvtools;
+
+TEST(PassManager, Interface) {
+  opt::PassManager manager;
+  EXPECT_EQ(0u, manager.NumPasses());
+
+  manager.AddPass<opt::StripDebugInfoPass>();
+  EXPECT_EQ(1u, manager.NumPasses());
+  EXPECT_STREQ("StripDebugInfo", manager.GetPass(0)->name());
+
+  manager.AddPass(std::unique_ptr<opt::NullPass>(new opt::NullPass));
+  EXPECT_EQ(2u, manager.NumPasses());
+  EXPECT_STREQ("StripDebugInfo", manager.GetPass(0)->name());
+  EXPECT_STREQ("Null", manager.GetPass(1)->name());
+
+  manager.AddPass<opt::StripDebugInfoPass>();
+  EXPECT_EQ(3u, manager.NumPasses());
+  EXPECT_STREQ("StripDebugInfo", manager.GetPass(0)->name());
+  EXPECT_STREQ("Null", manager.GetPass(1)->name());
+  EXPECT_STREQ("StripDebugInfo", manager.GetPass(2)->name());
+}
+
+// A pass that appends an OpNop instruction to the debug section.
+class AppendOpNopPass : public opt::Pass {
+  const char* name() const override { return "AppendOpNop"; }
+  bool Process(ir::Module* module) override {
+    module->AddDebugInst(ir::Instruction());
+    return true;
+  }
+};
+
+// A pass that duplicates the last instruction in the debug section.
+class DuplicateInstPass : public opt::Pass {
+  const char* name() const override { return "DuplicateInst"; }
+  bool Process(ir::Module* module) override {
+    ir::Instruction inst = module->debugs().back();
+    module->AddDebugInst(std::move(inst));
+    return true;
+  }
+};
+
+using PassManagerTest = PassTest<::testing::Test>;
+
+TEST_F(PassManagerTest, Run) {
+  const std::string text = "OpMemoryModel Logical GLSL450\nOpSource ESSL 310\n";
+
+  AddPass<AppendOpNopPass>();
+  AddPass<AppendOpNopPass>();
+  RunAndCheck(text.c_str(), (text + "OpNop\nOpNop\n").c_str());
+
+  RenewPassManger();
+  AddPass<AppendOpNopPass>();
+  AddPass<DuplicateInstPass>();
+  RunAndCheck(text.c_str(), (text + "OpNop\nOpNop\n").c_str());
+
+  RenewPassManger();
+  AddPass<DuplicateInstPass>();
+  AddPass<AppendOpNopPass>();
+  RunAndCheck(text.c_str(), (text + "OpSource ESSL 310\nOpNop\n").c_str());
+}
+
+}  // anonymous namespace
diff --git a/test/opt/test_strip_debug_info.cpp b/test/opt/test_strip_debug_info.cpp
new file mode 100644
index 0000000..964b33c
--- /dev/null
+++ b/test/opt/test_strip_debug_info.cpp
@@ -0,0 +1,108 @@
+// Copyright (c) 2016 Google Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and/or associated documentation files (the
+// "Materials"), to deal in the Materials without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Materials, and to
+// permit persons to whom the Materials are furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Materials.
+//
+// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
+// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
+// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
+//    https://www.khronos.org/registry/
+//
+// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+
+#include "pass_fixture.h"
+#include "pass_utils.h"
+
+namespace {
+
+using namespace spvtools;
+
+using StripLineDebugInfoTest = PassTest<::testing::Test>;
+
+TEST_F(StripLineDebugInfoTest, LineNoLine) {
+  std::vector<const char*> text = {
+      "OpCapability Shader",
+      "%1 = OpExtInstImport \"GLSL.std.450\"",
+      "OpMemoryModel Logical GLSL450",
+      "OpEntryPoint Vertex %2 \"main\"",
+      "%3 = OpString \"minimal.vert\"",
+      "OpNoLine",
+      "OpLine %3 10 10",
+      "%4 = OpTypeVoid",
+      "OpLine %3 100 100",
+      "%5 = OpTypeFunction %4",
+      "%2 = OpFunction %4 None %5",
+      "OpLine %3 1 1",
+      "OpNoLine",
+      "OpLine %3 2 2",
+      "OpLine %3 3 3",
+      "%6 = OpLabel",
+      "OpLine %3 4 4",
+      "OpNoLine",
+      "OpReturn",
+      "OpLine %3 4 4",
+      "OpNoLine",
+      "OpFunctionEnd",
+  };
+  SinglePassRunAndCheck<opt::StripDebugInfoPass>(JoinAllInsts(text),
+                                                 JoinNonDebugInsts(text));
+
+  // Let's add more debug instruction before the "OpString" instruction.
+  const std::vector<const char*> more_text = {
+      "OpSourceContinued \"I'm a happy shader! Yay! ;)\"",
+      "OpSourceContinued \"wahahaha\"",
+      "OpSource ESSL 310",
+      "OpSource ESSL 310",
+      "OpSourceContinued \"wahahaha\"",
+      "OpSourceContinued \"wahahaha\"",
+      "OpSourceExtension \"save-the-world-extension\"",
+      "OpName %2 \"main\"",
+      "OpModuleProcessed \"42\"",
+      "OpModuleProcessed \"43\"",
+      "OpModuleProcessed \"44\"",
+  };
+  text.insert(text.begin() + 4, more_text.cbegin(), more_text.cend());
+  SinglePassRunAndCheck<opt::StripDebugInfoPass>(JoinAllInsts(text),
+                                                 JoinNonDebugInsts(text));
+}
+
+using StripDebugInfoTest = PassTest<::testing::TestWithParam<const char*>>;
+
+TEST_P(StripDebugInfoTest, Kind) {
+  std::vector<const char*> text = {
+      "OpCapability Shader", "OpMemoryModel Logical GLSL450", GetParam(),
+  };
+  SinglePassRunAndCheck<opt::StripDebugInfoPass>(JoinAllInsts(text),
+                                                 JoinNonDebugInsts(text));
+}
+
+// Test each possible non-line debug instruction.
+// clang-format off
+INSTANTIATE_TEST_CASE_P(
+    SingleKindDebugInst, StripDebugInfoTest,
+    ::testing::ValuesIn(std::vector<const char*>({
+        "OpSourceContinued \"I'm a happy shader! Yay! ;)\"",
+        "OpSource ESSL 310",
+        "OpSourceExtension \"save-the-world-extension\"",
+        "OpName %main \"main\"",
+        "OpMemberName %struct 0 \"field\"",
+        "%1 = OpString \"name.vert\"",
+        "OpModuleProcessed \"42\"",
+    })));
+// clang-format on
+
+}  // anonymous namespace
diff --git a/test/opt/test_utils.cpp b/test/opt/test_utils.cpp
new file mode 100644
index 0000000..4a96559
--- /dev/null
+++ b/test/opt/test_utils.cpp
@@ -0,0 +1,58 @@
+// Copyright (c) 2016 Google Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and/or associated documentation files (the
+// "Materials"), to deal in the Materials without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Materials, and to
+// permit persons to whom the Materials are furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Materials.
+//
+// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
+// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
+// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
+//    https://www.khronos.org/registry/
+//
+// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+
+#include <gtest/gtest.h>
+
+#include "pass_utils.h"
+
+namespace {
+
+using namespace spvtools;
+
+TEST(JoinAllInsts, Cases) {
+  EXPECT_EQ("", JoinAllInsts({}));
+  EXPECT_EQ("a\n", JoinAllInsts({"a"}));
+  EXPECT_EQ("a\nb\n", JoinAllInsts({"a", "b"}));
+  EXPECT_EQ("a\nb\nc\n", JoinAllInsts({"a", "b", "c"}));
+  EXPECT_EQ("hello,\nworld!\n\n\n", JoinAllInsts({"hello,", "world!", "\n"}));
+}
+
+TEST(JoinNonDebugInsts, Cases) {
+  EXPECT_EQ("", JoinNonDebugInsts({}));
+  EXPECT_EQ("a\n", JoinNonDebugInsts({"a"}));
+  EXPECT_EQ("", JoinNonDebugInsts({"OpName"}));
+  EXPECT_EQ("a\nb\n", JoinNonDebugInsts({"a", "b"}));
+  EXPECT_EQ("", JoinNonDebugInsts({"OpName", "%1 = OpString \"42\""}));
+  EXPECT_EQ("Opstring\n", JoinNonDebugInsts({"OpName", "Opstring"}));
+  EXPECT_EQ("the only remaining string\n",
+            JoinNonDebugInsts(
+                {"OpSourceContinued", "OpSource", "OpSourceExtension",
+                 "lgtm OpName", "hello OpMemberName", "this is a OpString",
+                 "lonely OpLine", "happy OpNoLine", "OpModuleProcessed",
+                 "the only remaining string"}));
+}
+
+}  // anonymous namespace