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