// Copyright (c) 2016 Google Inc. // // 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 "gmock/gmock.h" #include #include "module_utils.h" #include "opt/make_unique.h" #include "pass_fixture.h" namespace { using namespace spvtools; using spvtest::GetIdBound; using ::testing::Eq; // A null pass whose construtors accept arguments class NullPassWithArgs : public opt::NullPass { public: NullPassWithArgs(uint32_t) {} NullPassWithArgs(std::string) {} NullPassWithArgs(const std::vector&) {} NullPassWithArgs(const std::vector&, uint32_t) {} const char* name() const override { return "null-with-args"; } }; TEST(PassManager, Interface) { opt::PassManager manager; EXPECT_EQ(0u, manager.NumPasses()); manager.AddPass(); EXPECT_EQ(1u, manager.NumPasses()); EXPECT_STREQ("strip-debug", manager.GetPass(0)->name()); manager.AddPass(MakeUnique()); EXPECT_EQ(2u, manager.NumPasses()); EXPECT_STREQ("strip-debug", manager.GetPass(0)->name()); EXPECT_STREQ("null", manager.GetPass(1)->name()); manager.AddPass(); EXPECT_EQ(3u, manager.NumPasses()); EXPECT_STREQ("strip-debug", manager.GetPass(0)->name()); EXPECT_STREQ("null", manager.GetPass(1)->name()); EXPECT_STREQ("strip-debug", manager.GetPass(2)->name()); manager.AddPass(1u); manager.AddPass("null pass args"); manager.AddPass(std::initializer_list{1, 2}); manager.AddPass(std::initializer_list{1, 2}, 3); EXPECT_EQ(7u, manager.NumPasses()); EXPECT_STREQ("strip-debug", manager.GetPass(0)->name()); EXPECT_STREQ("null", manager.GetPass(1)->name()); EXPECT_STREQ("strip-debug", manager.GetPass(2)->name()); EXPECT_STREQ("null-with-args", manager.GetPass(3)->name()); EXPECT_STREQ("null-with-args", manager.GetPass(4)->name()); EXPECT_STREQ("null-with-args", manager.GetPass(5)->name()); EXPECT_STREQ("null-with-args", manager.GetPass(6)->name()); } // A pass that appends an OpNop instruction to the debug section. class AppendOpNopPass : public opt::Pass { public: const char* name() const override { return "AppendOpNop"; } Status Process(ir::Module* module) override { module->AddDebugInst(MakeUnique()); return Status::SuccessWithChange; } }; // A pass that appends specified number of OpNop instructions to the debug // section. class AppendMultipleOpNopPass : public opt::Pass { public: explicit AppendMultipleOpNopPass(uint32_t num_nop) : num_nop_(num_nop) {} const char* name() const override { return "AppendOpNop"; } Status Process(ir::Module* module) override { for (uint32_t i = 0; i < num_nop_; i++) { module->AddDebugInst(MakeUnique()); } return Status::SuccessWithChange; } private: uint32_t num_nop_; }; // A pass that duplicates the last instruction in the debug section. class DuplicateInstPass : public opt::Pass { public: const char* name() const override { return "DuplicateInst"; } Status Process(ir::Module* module) override { auto inst = MakeUnique(*(--module->debug_end())); module->AddDebugInst(std::move(inst)); return Status::SuccessWithChange; } }; using PassManagerTest = PassTest<::testing::Test>; TEST_F(PassManagerTest, Run) { const std::string text = "OpMemoryModel Logical GLSL450\nOpSource ESSL 310\n"; AddPass(); AddPass(); RunAndCheck(text.c_str(), (text + "OpNop\nOpNop\n").c_str()); RenewPassManger(); AddPass(); AddPass(); RunAndCheck(text.c_str(), (text + "OpNop\nOpNop\n").c_str()); RenewPassManger(); AddPass(); AddPass(); RunAndCheck(text.c_str(), (text + "OpSource ESSL 310\nOpNop\n").c_str()); RenewPassManger(); AddPass(3); RunAndCheck(text.c_str(), (text + "OpNop\nOpNop\nOpNop\n").c_str()); } // A pass that appends an OpTypeVoid instruction that uses a given id. class AppendTypeVoidInstPass : public opt::Pass { public: explicit AppendTypeVoidInstPass(uint32_t result_id) : result_id_(result_id) {} const char* name() const override { return "AppendTypeVoidInstPass"; } Status Process(ir::Module* module) override { auto inst = MakeUnique(SpvOpTypeVoid, 0, result_id_, std::vector{}); module->AddType(std::move(inst)); return Status::SuccessWithChange; } private: uint32_t result_id_; }; TEST(PassManager, RecomputeIdBoundAutomatically) { ir::Module module; EXPECT_THAT(GetIdBound(module), Eq(0u)); opt::PassManager manager; manager.Run(&module); manager.AddPass(); // With no ID changes, the ID bound does not change. EXPECT_THAT(GetIdBound(module), Eq(0u)); // Now we force an Id of 100 to be used. manager.AddPass(MakeUnique(100)); EXPECT_THAT(GetIdBound(module), Eq(0u)); manager.Run(&module); // The Id has been updated automatically, even though the pass // did not update it. EXPECT_THAT(GetIdBound(module), Eq(101u)); // Try one more time! manager.AddPass(MakeUnique(200)); manager.Run(&module); EXPECT_THAT(GetIdBound(module), Eq(201u)); // Add another pass, but which uses a lower Id. manager.AddPass(MakeUnique(10)); manager.Run(&module); // The Id stays high. EXPECT_THAT(GetIdBound(module), Eq(201u)); } } // anonymous namespace