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