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 = MakeUnique<Instruction>(*(--context()->debug1_end()));
111     context()->AddDebug1Inst(std::move(inst));
112     return Status::SuccessWithChange;
113   }
114 };
115 
116 using PassManagerTest = PassTest<::testing::Test>;
117 
TEST_F(PassManagerTest,Run)118 TEST_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.
141 class AppendTypeVoidInstPass : public Pass {
142  public:
AppendTypeVoidInstPass(uint32_t result_id)143   explicit AppendTypeVoidInstPass(uint32_t result_id) : result_id_(result_id) {}
144 
name() const145   const char* name() const override { return "AppendTypeVoidInstPass"; }
Process()146   Status Process() override {
147     auto inst = MakeUnique<Instruction>(context(), SpvOpTypeVoid, 0, result_id_,
148                                         std::vector<Operand>{});
149     context()->AddType(std::move(inst));
150     return Status::SuccessWithChange;
151   }
152 
153  private:
154   uint32_t result_id_;
155 };
156 
TEST(PassManager,RecomputeIdBoundAutomatically)157 TEST(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