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 
26 namespace spvtools {
27 namespace opt {
28 namespace {
29 
30 using spvtest::GetIdBound;
31 using ::testing::Eq;
32 
33 // A null pass whose construtors accept arguments
34 class NullPassWithArgs : public NullPass {
35  public:
NullPassWithArgs(uint32_t)36   NullPassWithArgs(uint32_t) {}
NullPassWithArgs(std::string)37   NullPassWithArgs(std::string) {}
NullPassWithArgs(const std::vector<int> &)38   NullPassWithArgs(const std::vector<int>&) {}
NullPassWithArgs(const std::vector<int> &,uint32_t)39   NullPassWithArgs(const std::vector<int>&, uint32_t) {}
40 
name() const41   const char* name() const override { return "null-with-args"; }
42 };
43 
TEST(PassManager,Interface)44 TEST(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.
78 class AppendOpNopPass : public Pass {
79  public:
name() const80   const char* name() const override { return "AppendOpNop"; }
Process()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.
89 class AppendMultipleOpNopPass : public Pass {
90  public:
AppendMultipleOpNopPass(uint32_t num_nop)91   explicit AppendMultipleOpNopPass(uint32_t num_nop) : num_nop_(num_nop) {}
92 
name() const93   const char* name() const override { return "AppendOpNop"; }
Process()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.
106 class DuplicateInstPass : public Pass {
107  public:
name() const108   const char* name() const override { return "DuplicateInst"; }
Process()109   Status Process() override {
110     auto inst =
111         MakeUnique<Instruction>(*(--context()->debug1_end())->Clone(context()));
112     context()->AddDebug1Inst(std::move(inst));
113     return Status::SuccessWithChange;
114   }
115 };
116 
117 using PassManagerTest = PassTest<::testing::Test>;
118 
TEST_F(PassManagerTest,Run)119 TEST_F(PassManagerTest, Run) {
120   const std::string text = "OpMemoryModel Logical GLSL450\nOpSource ESSL 310\n";
121 
122   AddPass<AppendOpNopPass>();
123   AddPass<AppendOpNopPass>();
124   RunAndCheck(text.c_str(), (text + "OpNop\nOpNop\n").c_str());
125 
126   RenewPassManger();
127   AddPass<AppendOpNopPass>();
128   AddPass<DuplicateInstPass>();
129   RunAndCheck(text.c_str(), (text + "OpNop\nOpNop\n").c_str());
130 
131   RenewPassManger();
132   AddPass<DuplicateInstPass>();
133   AddPass<AppendOpNopPass>();
134   RunAndCheck(text.c_str(), (text + "OpSource ESSL 310\nOpNop\n").c_str());
135 
136   RenewPassManger();
137   AddPass<AppendMultipleOpNopPass>(3);
138   RunAndCheck(text.c_str(), (text + "OpNop\nOpNop\nOpNop\n").c_str());
139 }
140 
141 // A pass that appends an OpTypeVoid instruction that uses a given id.
142 class AppendTypeVoidInstPass : public Pass {
143  public:
AppendTypeVoidInstPass(uint32_t result_id)144   explicit AppendTypeVoidInstPass(uint32_t result_id) : result_id_(result_id) {}
145 
name() const146   const char* name() const override { return "AppendTypeVoidInstPass"; }
Process()147   Status Process() override {
148     auto inst = MakeUnique<Instruction>(context(), SpvOpTypeVoid, 0, result_id_,
149                                         std::vector<Operand>{});
150     context()->AddType(std::move(inst));
151     return Status::SuccessWithChange;
152   }
153 
154  private:
155   uint32_t result_id_;
156 };
157 
TEST(PassManager,RecomputeIdBoundAutomatically)158 TEST(PassManager, RecomputeIdBoundAutomatically) {
159   PassManager manager;
160   std::unique_ptr<Module> module(new Module());
161   IRContext context(SPV_ENV_UNIVERSAL_1_2, std::move(module),
162                     manager.consumer());
163   EXPECT_THAT(GetIdBound(*context.module()), Eq(0u));
164 
165   manager.Run(&context);
166   manager.AddPass<AppendOpNopPass>();
167   // With no ID changes, the ID bound does not change.
168   EXPECT_THAT(GetIdBound(*context.module()), Eq(0u));
169 
170   // Now we force an Id of 100 to be used.
171   manager.AddPass(MakeUnique<AppendTypeVoidInstPass>(100));
172   EXPECT_THAT(GetIdBound(*context.module()), Eq(0u));
173   manager.Run(&context);
174   // The Id has been updated automatically, even though the pass
175   // did not update it.
176   EXPECT_THAT(GetIdBound(*context.module()), Eq(101u));
177 
178   // Try one more time!
179   manager.AddPass(MakeUnique<AppendTypeVoidInstPass>(200));
180   manager.Run(&context);
181   EXPECT_THAT(GetIdBound(*context.module()), Eq(201u));
182 
183   // Add another pass, but which uses a lower Id.
184   manager.AddPass(MakeUnique<AppendTypeVoidInstPass>(10));
185   manager.Run(&context);
186   // The Id stays high.
187   EXPECT_THAT(GetIdBound(*context.module()), Eq(201u));
188 }
189 
190 }  // anonymous namespace
191 }  // namespace opt
192 }  // namespace spvtools
193