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 constructors 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(), 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
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