1// Copyright (c) 2016 Google Inc. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15#include <initializer_list> 16#include <memory> 17#include <string> 18#include <utility> 19#include <vector> 20 21#include "gmock/gmock.h" 22#include "source/util/make_unique.h" 23#include "test/opt/module_utils.h" 24#include "test/opt/pass_fixture.h" 25 26namespace spvtools { 27namespace opt { 28namespace { 29 30using spvtest::GetIdBound; 31using ::testing::Eq; 32 33// A null pass whose constructors accept arguments 34class NullPassWithArgs : public NullPass { 35 public: 36 NullPassWithArgs(uint32_t) {} 37 NullPassWithArgs(std::string) {} 38 NullPassWithArgs(const std::vector<int>&) {} 39 NullPassWithArgs(const std::vector<int>&, uint32_t) {} 40 41 const char* name() const override { return "null-with-args"; } 42}; 43 44TEST(PassManager, Interface) { 45 PassManager manager; 46 EXPECT_EQ(0u, manager.NumPasses()); 47 48 manager.AddPass<StripDebugInfoPass>(); 49 EXPECT_EQ(1u, manager.NumPasses()); 50 EXPECT_STREQ("strip-debug", manager.GetPass(0)->name()); 51 52 manager.AddPass(MakeUnique<NullPass>()); 53 EXPECT_EQ(2u, manager.NumPasses()); 54 EXPECT_STREQ("strip-debug", manager.GetPass(0)->name()); 55 EXPECT_STREQ("null", manager.GetPass(1)->name()); 56 57 manager.AddPass<StripDebugInfoPass>(); 58 EXPECT_EQ(3u, manager.NumPasses()); 59 EXPECT_STREQ("strip-debug", manager.GetPass(0)->name()); 60 EXPECT_STREQ("null", manager.GetPass(1)->name()); 61 EXPECT_STREQ("strip-debug", manager.GetPass(2)->name()); 62 63 manager.AddPass<NullPassWithArgs>(1u); 64 manager.AddPass<NullPassWithArgs>("null pass args"); 65 manager.AddPass<NullPassWithArgs>(std::initializer_list<int>{1, 2}); 66 manager.AddPass<NullPassWithArgs>(std::initializer_list<int>{1, 2}, 3); 67 EXPECT_EQ(7u, manager.NumPasses()); 68 EXPECT_STREQ("strip-debug", manager.GetPass(0)->name()); 69 EXPECT_STREQ("null", manager.GetPass(1)->name()); 70 EXPECT_STREQ("strip-debug", manager.GetPass(2)->name()); 71 EXPECT_STREQ("null-with-args", manager.GetPass(3)->name()); 72 EXPECT_STREQ("null-with-args", manager.GetPass(4)->name()); 73 EXPECT_STREQ("null-with-args", manager.GetPass(5)->name()); 74 EXPECT_STREQ("null-with-args", manager.GetPass(6)->name()); 75} 76 77// A pass that appends an OpNop instruction to the debug1 section. 78class AppendOpNopPass : public Pass { 79 public: 80 const char* name() const override { return "AppendOpNop"; } 81 Status Process() override { 82 context()->AddDebug1Inst(MakeUnique<Instruction>(context())); 83 return Status::SuccessWithChange; 84 } 85}; 86 87// A pass that appends specified number of OpNop instructions to the debug1 88// section. 89class AppendMultipleOpNopPass : public Pass { 90 public: 91 explicit AppendMultipleOpNopPass(uint32_t num_nop) : num_nop_(num_nop) {} 92 93 const char* name() const override { return "AppendOpNop"; } 94 Status Process() override { 95 for (uint32_t i = 0; i < num_nop_; i++) { 96 context()->AddDebug1Inst(MakeUnique<Instruction>(context())); 97 } 98 return Status::SuccessWithChange; 99 } 100 101 private: 102 uint32_t num_nop_; 103}; 104 105// A pass that duplicates the last instruction in the debug1 section. 106class DuplicateInstPass : public Pass { 107 public: 108 const char* name() const override { return "DuplicateInst"; } 109 Status Process() override { 110 auto inst = MakeUnique<Instruction>(*(--context()->debug1_end())); 111 context()->AddDebug1Inst(std::move(inst)); 112 return Status::SuccessWithChange; 113 } 114}; 115 116using PassManagerTest = PassTest<::testing::Test>; 117 118TEST_F(PassManagerTest, Run) { 119 const std::string text = "OpMemoryModel Logical GLSL450\nOpSource ESSL 310\n"; 120 121 AddPass<AppendOpNopPass>(); 122 AddPass<AppendOpNopPass>(); 123 RunAndCheck(text, text + "OpNop\nOpNop\n"); 124 125 RenewPassManger(); 126 AddPass<AppendOpNopPass>(); 127 AddPass<DuplicateInstPass>(); 128 RunAndCheck(text, text + "OpNop\nOpNop\n"); 129 130 RenewPassManger(); 131 AddPass<DuplicateInstPass>(); 132 AddPass<AppendOpNopPass>(); 133 RunAndCheck(text, text + "OpSource ESSL 310\nOpNop\n"); 134 135 RenewPassManger(); 136 AddPass<AppendMultipleOpNopPass>(3); 137 RunAndCheck(text, text + "OpNop\nOpNop\nOpNop\n"); 138} 139 140// A pass that appends an OpTypeVoid instruction that uses a given id. 141class AppendTypeVoidInstPass : public Pass { 142 public: 143 explicit AppendTypeVoidInstPass(uint32_t result_id) : result_id_(result_id) {} 144 145 const char* name() const override { return "AppendTypeVoidInstPass"; } 146 Status Process() override { 147 auto inst = MakeUnique<Instruction>(context(), spv::Op::OpTypeVoid, 0, 148 result_id_, std::vector<Operand>{}); 149 context()->AddType(std::move(inst)); 150 return Status::SuccessWithChange; 151 } 152 153 private: 154 uint32_t result_id_; 155}; 156 157TEST(PassManager, RecomputeIdBoundAutomatically) { 158 PassManager manager; 159 std::unique_ptr<Module> module(new Module()); 160 IRContext context(SPV_ENV_UNIVERSAL_1_2, std::move(module), 161 manager.consumer()); 162 EXPECT_THAT(GetIdBound(*context.module()), Eq(0u)); 163 164 manager.Run(&context); 165 manager.AddPass<AppendOpNopPass>(); 166 // With no ID changes, the ID bound does not change. 167 EXPECT_THAT(GetIdBound(*context.module()), Eq(0u)); 168 169 // Now we force an Id of 100 to be used. 170 manager.AddPass(MakeUnique<AppendTypeVoidInstPass>(100)); 171 EXPECT_THAT(GetIdBound(*context.module()), Eq(0u)); 172 manager.Run(&context); 173 // The Id has been updated automatically, even though the pass 174 // did not update it. 175 EXPECT_THAT(GetIdBound(*context.module()), Eq(101u)); 176 177 // Try one more time! 178 manager.AddPass(MakeUnique<AppendTypeVoidInstPass>(200)); 179 manager.Run(&context); 180 EXPECT_THAT(GetIdBound(*context.module()), Eq(201u)); 181 182 // Add another pass, but which uses a lower Id. 183 manager.AddPass(MakeUnique<AppendTypeVoidInstPass>(10)); 184 manager.Run(&context); 185 // The Id stays high. 186 EXPECT_THAT(GetIdBound(*context.module()), Eq(201u)); 187} 188 189} // anonymous namespace 190} // namespace opt 191} // namespace spvtools 192