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