• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #ifndef TEST_OPT_PASS_FIXTURE_H_
16 #define TEST_OPT_PASS_FIXTURE_H_
17 
18 #include <iostream>
19 #include <memory>
20 #include <string>
21 #include <tuple>
22 #include <utility>
23 #include <vector>
24 
25 #include "effcee/effcee.h"
26 #include "gtest/gtest.h"
27 #include "source/opt/build_module.h"
28 #include "source/opt/pass_manager.h"
29 #include "source/opt/passes.h"
30 #include "source/spirv_optimizer_options.h"
31 #include "source/spirv_validator_options.h"
32 #include "source/util/make_unique.h"
33 #include "spirv-tools/libspirv.hpp"
34 
35 namespace spvtools {
36 namespace opt {
37 
38 // Template class for testing passes. It contains some handy utility methods for
39 // running passes and checking results.
40 //
41 // To write value-Parameterized tests:
42 //   using ValueParamTest = PassTest<::testing::TestWithParam<std::string>>;
43 // To use as normal fixture:
44 //   using FixtureTest = PassTest<::testing::Test>;
45 template <typename TestT>
46 class PassTest : public TestT {
47  public:
PassTest()48   PassTest()
49       : consumer_(
50             [](spv_message_level_t, const char*, const spv_position_t&,
51                const char* message) { std::cerr << message << std::endl; }),
52         context_(nullptr),
53         manager_(new PassManager()),
54         assemble_options_(SpirvTools::kDefaultAssembleOption),
55         disassemble_options_(SpirvTools::kDefaultDisassembleOption),
56         env_(SPV_ENV_UNIVERSAL_1_3) {}
57 
58   // Runs the given |pass| on the binary assembled from the |original|.
59   // Returns a tuple of the optimized binary and the boolean value returned
60   // from pass Process() function.
OptimizeToBinary(Pass * pass,const std::string & original,bool skip_nop)61   std::tuple<std::vector<uint32_t>, Pass::Status> OptimizeToBinary(
62       Pass* pass, const std::string& original, bool skip_nop) {
63     context_ = BuildModule(env_, consumer_, original, assemble_options_);
64     EXPECT_NE(nullptr, context()) << "Assembling failed for shader:\n"
65                                   << original << std::endl;
66     if (!context()) {
67       return std::make_tuple(std::vector<uint32_t>(), Pass::Status::Failure);
68     }
69 
70     context()->set_preserve_bindings(OptimizerOptions()->preserve_bindings_);
71     context()->set_preserve_spec_constants(
72         OptimizerOptions()->preserve_spec_constants_);
73 
74     const auto status = pass->Run(context());
75 
76     std::vector<uint32_t> binary;
77     if (status != Pass::Status::Failure) {
78       context()->module()->ToBinary(&binary, skip_nop);
79     }
80     return std::make_tuple(binary, status);
81   }
82 
83   // Runs a single pass of class |PassT| on the binary assembled from the
84   // |assembly|. Returns a tuple of the optimized binary and the boolean value
85   // from the pass Process() function.
86   template <typename PassT, typename... Args>
SinglePassRunToBinary(const std::string & assembly,bool skip_nop,Args &&...args)87   std::tuple<std::vector<uint32_t>, Pass::Status> SinglePassRunToBinary(
88       const std::string& assembly, bool skip_nop, Args&&... args) {
89     auto pass = MakeUnique<PassT>(std::forward<Args>(args)...);
90     pass->SetMessageConsumer(consumer_);
91     return OptimizeToBinary(pass.get(), assembly, skip_nop);
92   }
93 
94   // Runs a single pass of class |PassT| on the binary assembled from the
95   // |assembly|, disassembles the optimized binary. Returns a tuple of
96   // disassembly string and the boolean value from the pass Process() function.
97   template <typename PassT, typename... Args>
SinglePassRunAndDisassemble(const std::string & assembly,bool skip_nop,bool do_validation,Args &&...args)98   std::tuple<std::string, Pass::Status> SinglePassRunAndDisassemble(
99       const std::string& assembly, bool skip_nop, bool do_validation,
100       Args&&... args) {
101     std::vector<uint32_t> optimized_bin;
102     auto status = Pass::Status::SuccessWithoutChange;
103     std::tie(optimized_bin, status) = SinglePassRunToBinary<PassT>(
104         assembly, skip_nop, std::forward<Args>(args)...);
105     if (do_validation) {
106       spv_context spvContext = spvContextCreate(env_);
107       spv_diagnostic diagnostic = nullptr;
108       spv_const_binary_t binary = {optimized_bin.data(), optimized_bin.size()};
109       spv_result_t error = spvValidateWithOptions(
110           spvContext, ValidatorOptions(), &binary, &diagnostic);
111       EXPECT_EQ(error, 0);
112       if (error != 0) spvDiagnosticPrint(diagnostic);
113       spvDiagnosticDestroy(diagnostic);
114       spvContextDestroy(spvContext);
115     }
116     std::string optimized_asm;
117     SpirvTools tools(env_);
118     EXPECT_TRUE(
119         tools.Disassemble(optimized_bin, &optimized_asm, disassemble_options_))
120         << "Disassembling failed for shader:\n"
121         << assembly << std::endl;
122     return std::make_tuple(optimized_asm, status);
123   }
124 
125   // Runs a single pass of class |PassT| on the binary assembled from the
126   // |original| assembly, and checks whether the optimized binary can be
127   // disassembled to the |expected| assembly. Optionally will also validate
128   // the optimized binary. This does *not* involve pass manager. Callers
129   // are suggested to use SCOPED_TRACE() for better messages.
130   template <typename PassT, typename... Args>
SinglePassRunAndCheck(const std::string & original,const std::string & expected,bool skip_nop,bool do_validation,Args &&...args)131   void SinglePassRunAndCheck(const std::string& original,
132                              const std::string& expected, bool skip_nop,
133                              bool do_validation, Args&&... args) {
134     std::vector<uint32_t> optimized_bin;
135     auto status = Pass::Status::SuccessWithoutChange;
136     std::tie(optimized_bin, status) = SinglePassRunToBinary<PassT>(
137         original, skip_nop, std::forward<Args>(args)...);
138     // Check whether the pass returns the correct modification indication.
139     EXPECT_NE(Pass::Status::Failure, status);
140     EXPECT_EQ(original == expected,
141               status == Pass::Status::SuccessWithoutChange);
142     if (do_validation) {
143       spv_context spvContext = spvContextCreate(env_);
144       spv_diagnostic diagnostic = nullptr;
145       spv_const_binary_t binary = {optimized_bin.data(), optimized_bin.size()};
146       spv_result_t error = spvValidateWithOptions(
147           spvContext, ValidatorOptions(), &binary, &diagnostic);
148       EXPECT_EQ(error, 0);
149       if (error != 0) spvDiagnosticPrint(diagnostic);
150       spvDiagnosticDestroy(diagnostic);
151       spvContextDestroy(spvContext);
152     }
153     std::string optimized_asm;
154     SpirvTools tools(env_);
155     EXPECT_TRUE(
156         tools.Disassemble(optimized_bin, &optimized_asm, disassemble_options_))
157         << "Disassembling failed for shader:\n"
158         << original << std::endl;
159     EXPECT_EQ(expected, optimized_asm);
160   }
161 
162   // Runs a single pass of class |PassT| on the binary assembled from the
163   // |original| assembly, and checks whether the optimized binary can be
164   // disassembled to the |expected| assembly. This does *not* involve pass
165   // manager. Callers are suggested to use SCOPED_TRACE() for better messages.
166   template <typename PassT, typename... Args>
SinglePassRunAndCheck(const std::string & original,const std::string & expected,bool skip_nop,Args &&...args)167   void SinglePassRunAndCheck(const std::string& original,
168                              const std::string& expected, bool skip_nop,
169                              Args&&... args) {
170     SinglePassRunAndCheck<PassT>(original, expected, skip_nop, false,
171                                  std::forward<Args>(args)...);
172   }
173 
174   // Runs a single pass of class |PassT| on the binary assembled from the
175   // |original| assembly, then runs an Effcee matcher over the disassembled
176   // result, using checks parsed from |original|.  Always skips OpNop.
177   // This does *not* involve pass manager.  Callers are suggested to use
178   // SCOPED_TRACE() for better messages.
179   // Returns a tuple of disassembly string and the boolean value from the pass
180   // Process() function.
181   template <typename PassT, typename... Args>
SinglePassRunAndMatch(const std::string & original,bool do_validation,Args &&...args)182   std::tuple<std::string, Pass::Status> SinglePassRunAndMatch(
183       const std::string& original, bool do_validation, Args&&... args) {
184     const bool skip_nop = true;
185     auto pass_result = SinglePassRunAndDisassemble<PassT>(
186         original, skip_nop, do_validation, std::forward<Args>(args)...);
187     auto disassembly = std::get<0>(pass_result);
188     auto match_result = effcee::Match(disassembly, original);
189     EXPECT_EQ(effcee::Result::Status::Ok, match_result.status())
190         << match_result.message() << "\nChecking result:\n"
191         << disassembly;
192     return pass_result;
193   }
194 
195   // Runs a single pass of class |PassT| on the binary assembled from the
196   // |original| assembly. Check for failure and expect an Effcee matcher
197   // to pass when run on the diagnostic messages. This does *not* involve
198   // pass manager.  Callers are suggested to use SCOPED_TRACE() for better
199   // messages.
200   template <typename PassT, typename... Args>
SinglePassRunAndFail(const std::string & original,Args &&...args)201   void SinglePassRunAndFail(const std::string& original, Args&&... args) {
202     context_ = BuildModule(env_, consumer_, original, assemble_options_);
203     EXPECT_NE(nullptr, context()) << "Assembling failed for shader:\n"
204                                   << original << std::endl;
205     std::ostringstream errs;
206     auto error_consumer = [&errs](spv_message_level_t, const char*,
207                                   const spv_position_t&, const char* message) {
208       errs << message << std::endl;
209     };
210     auto pass = MakeUnique<PassT>(std::forward<Args>(args)...);
211     pass->SetMessageConsumer(error_consumer);
212     const auto status = pass->Run(context());
213     EXPECT_EQ(Pass::Status::Failure, status);
214     auto match_result = effcee::Match(errs.str(), original);
215     EXPECT_EQ(effcee::Result::Status::Ok, match_result.status())
216         << match_result.message() << "\nChecking messages:\n"
217         << errs.str();
218   }
219 
220   // Adds a pass to be run.
221   template <typename PassT, typename... Args>
AddPass(Args &&...args)222   void AddPass(Args&&... args) {
223     manager_->AddPass<PassT>(std::forward<Args>(args)...);
224   }
225 
226   // Renews the pass manager, including clearing all previously added passes.
RenewPassManger()227   void RenewPassManger() {
228     manager_ = MakeUnique<PassManager>();
229     manager_->SetMessageConsumer(consumer_);
230   }
231 
232   // Runs the passes added thus far using a pass manager on the binary assembled
233   // from the |original| assembly, and checks whether the optimized binary can
234   // be disassembled to the |expected| assembly. Callers are suggested to use
235   // SCOPED_TRACE() for better messages.
RunAndCheck(const std::string & original,const std::string & expected)236   void RunAndCheck(const std::string& original, const std::string& expected) {
237     assert(manager_->NumPasses());
238 
239     context_ = BuildModule(env_, nullptr, original, assemble_options_);
240     ASSERT_NE(nullptr, context());
241 
242     context()->set_preserve_bindings(OptimizerOptions()->preserve_bindings_);
243     context()->set_preserve_spec_constants(
244         OptimizerOptions()->preserve_spec_constants_);
245 
246     auto status = manager_->Run(context());
247     EXPECT_NE(status, Pass::Status::Failure);
248 
249     if (status != Pass::Status::Failure) {
250       std::vector<uint32_t> binary;
251       context()->module()->ToBinary(&binary, /* skip_nop = */ false);
252 
253       std::string optimized;
254       SpirvTools tools(env_);
255       EXPECT_TRUE(tools.Disassemble(binary, &optimized, disassemble_options_));
256       EXPECT_EQ(expected, optimized);
257     }
258   }
259 
SetAssembleOptions(uint32_t assemble_options)260   void SetAssembleOptions(uint32_t assemble_options) {
261     assemble_options_ = assemble_options;
262   }
263 
SetDisassembleOptions(uint32_t disassemble_options)264   void SetDisassembleOptions(uint32_t disassemble_options) {
265     disassemble_options_ = disassemble_options;
266   }
267 
consumer()268   MessageConsumer consumer() { return consumer_; }
context()269   IRContext* context() { return context_.get(); }
270 
SetMessageConsumer(MessageConsumer msg_consumer)271   void SetMessageConsumer(MessageConsumer msg_consumer) {
272     consumer_ = msg_consumer;
273   }
274 
OptimizerOptions()275   spv_optimizer_options OptimizerOptions() { return &optimizer_options_; }
276 
ValidatorOptions()277   spv_validator_options ValidatorOptions() { return &validator_options_; }
278 
SetTargetEnv(spv_target_env env)279   void SetTargetEnv(spv_target_env env) { env_ = env; }
280 
281  private:
282   MessageConsumer consumer_;              // Message consumer.
283   std::unique_ptr<IRContext> context_;    // IR context
284   std::unique_ptr<PassManager> manager_;  // The pass manager.
285   uint32_t assemble_options_;
286   uint32_t disassemble_options_;
287   spv_optimizer_options_t optimizer_options_;
288   spv_validator_options_t validator_options_;
289   spv_target_env env_;
290 };
291 
292 }  // namespace opt
293 }  // namespace spvtools
294 
295 #endif  // TEST_OPT_PASS_FIXTURE_H_
296