• 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 LIBSPIRV_TEST_OPT_PASS_FIXTURE_H_
16 #define LIBSPIRV_TEST_OPT_PASS_FIXTURE_H_
17 
18 #include <iostream>
19 #include <string>
20 #include <tuple>
21 #include <vector>
22 
23 #include <gtest/gtest.h>
24 
25 #include "opt/build_module.h"
26 #include "opt/make_unique.h"
27 #include "opt/pass_manager.h"
28 #include "opt/passes.h"
29 #include "spirv-tools/libspirv.hpp"
30 
31 namespace spvtools {
32 
33 // Template class for testing passes. It contains some handy utility methods for
34 // running passes and checking results.
35 //
36 // To write value-Parameterized tests:
37 //   using ValueParamTest = PassTest<::testing::TestWithParam<std::string>>;
38 // To use as normal fixture:
39 //   using FixtureTest = PassTest<::testing::Test>;
40 template <typename TestT>
41 class PassTest : public TestT {
42  public:
PassTest()43   PassTest()
44       : consumer_(nullptr),
45         tools_(SPV_ENV_UNIVERSAL_1_1),
46         manager_(new opt::PassManager()),
47         assemble_options_(SpirvTools::kDefaultAssembleOption),
48         disassemble_options_(SpirvTools::kDefaultDisassembleOption) {}
49 
50   // Runs the given |pass| on the binary assembled from the |original|.
51   // Returns a tuple of the optimized binary and the boolean value returned
52   // from pass Process() function.
OptimizeToBinary(opt::Pass * pass,const std::string & original,bool skip_nop)53   std::tuple<std::vector<uint32_t>, opt::Pass::Status> OptimizeToBinary(
54       opt::Pass* pass, const std::string& original, bool skip_nop) {
55     std::unique_ptr<ir::Module> module = BuildModule(
56         SPV_ENV_UNIVERSAL_1_1, consumer_, original, assemble_options_);
57     EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
58                                << original << std::endl;
59     if (!module) {
60       return std::make_tuple(std::vector<uint32_t>(),
61           opt::Pass::Status::Failure);
62     }
63 
64     const auto status = pass->Process(module.get());
65 
66     std::vector<uint32_t> binary;
67     module->ToBinary(&binary, skip_nop);
68     return std::make_tuple(binary, status);
69   }
70 
71   // Runs a single pass of class |PassT| on the binary assembled from the
72   // |assembly|. Returns a tuple of the optimized binary and the boolean value
73   // from the pass Process() function.
74   template <typename PassT, typename... Args>
SinglePassRunToBinary(const std::string & assembly,bool skip_nop,Args &&...args)75   std::tuple<std::vector<uint32_t>, opt::Pass::Status> SinglePassRunToBinary(
76       const std::string& assembly, bool skip_nop, Args&&... args) {
77     auto pass = MakeUnique<PassT>(std::forward<Args>(args)...);
78     pass->SetMessageConsumer(consumer_);
79     return OptimizeToBinary(pass.get(), assembly, skip_nop);
80   }
81 
82   // Runs a single pass of class |PassT| on the binary assembled from the
83   // |assembly|, disassembles the optimized binary. Returns a tuple of
84   // disassembly string and the boolean value from the pass Process() function.
85   template <typename PassT, typename... Args>
SinglePassRunAndDisassemble(const std::string & assembly,bool skip_nop,Args &&...args)86   std::tuple<std::string, opt::Pass::Status> SinglePassRunAndDisassemble(
87       const std::string& assembly, bool skip_nop, Args&&... args) {
88     std::vector<uint32_t> optimized_bin;
89     auto status = opt::Pass::Status::SuccessWithoutChange;
90     std::tie(optimized_bin, status) = SinglePassRunToBinary<PassT>(
91         assembly, skip_nop, std::forward<Args>(args)...);
92     std::string optimized_asm;
93     EXPECT_TRUE(tools_.Disassemble(optimized_bin, &optimized_asm,
94                                    disassemble_options_))
95         << "Disassembling failed for shader:\n"
96         << assembly << std::endl;
97     return std::make_tuple(optimized_asm, status);
98   }
99 
100   // Runs a single pass of class |PassT| on the binary assembled from the
101   // |original| assembly, and checks whether the optimized binary can be
102   // disassembled to the |expected| assembly. Optionally will also validate
103   // the optimized binary. This does *not* involve pass manager. Callers
104   // are suggested to use SCOPED_TRACE() for better messages.
105   template <typename PassT, typename... Args>
SinglePassRunAndCheck(const std::string & original,const std::string & expected,bool skip_nop,bool do_validation,Args &&...args)106   void SinglePassRunAndCheck(const std::string& original,
107                              const std::string& expected, bool skip_nop,
108                              bool do_validation, Args&&... args) {
109     std::vector<uint32_t> optimized_bin;
110     auto status = opt::Pass::Status::SuccessWithoutChange;
111     std::tie(optimized_bin, status) = SinglePassRunToBinary<PassT>(
112         original, skip_nop, std::forward<Args>(args)...);
113     // Check whether the pass returns the correct modification indication.
114     EXPECT_NE(opt::Pass::Status::Failure, status);
115     EXPECT_EQ(original == expected,
116               status == opt::Pass::Status::SuccessWithoutChange);
117     if (do_validation) {
118       spv_target_env target_env = SPV_ENV_UNIVERSAL_1_1;
119       spv_context context = spvContextCreate(target_env);
120       spv_diagnostic diagnostic = nullptr;
121       spv_const_binary_t binary = {optimized_bin.data(),
122           optimized_bin.size()};
123       spv_result_t error = spvValidate(context, &binary, &diagnostic);
124       EXPECT_EQ(error, 0);
125       if (error != 0)
126         spvDiagnosticPrint(diagnostic);
127       spvDiagnosticDestroy(diagnostic);
128       spvContextDestroy(context);
129     }
130     std::string optimized_asm;
131     EXPECT_TRUE(tools_.Disassemble(optimized_bin, &optimized_asm,
132                                    disassemble_options_))
133         << "Disassembling failed for shader:\n"
134         << original << std::endl;
135     EXPECT_EQ(expected, optimized_asm);
136   }
137 
138   // Runs a single pass of class |PassT| on the binary assembled from the
139   // |original| assembly, and checks whether the optimized binary can be
140   // disassembled to the |expected| assembly. This does *not* involve pass
141   // manager. Callers are suggested to use SCOPED_TRACE() for better messages.
142   template <typename PassT, typename... Args>
SinglePassRunAndCheck(const std::string & original,const std::string & expected,bool skip_nop,Args &&...args)143   void SinglePassRunAndCheck(const std::string& original,
144                              const std::string& expected, bool skip_nop,
145                              Args&&... args) {
146     SinglePassRunAndCheck<PassT>(original, expected, skip_nop, false,
147       std::forward<Args>(args)...);
148   }
149 
150   // Adds a pass to be run.
151   template <typename PassT, typename... Args>
AddPass(Args &&...args)152   void AddPass(Args&&... args) {
153     manager_->AddPass<PassT>(std::forward<Args>(args)...);
154   }
155 
156   // Renews the pass manager, including clearing all previously added passes.
RenewPassManger()157   void RenewPassManger() {
158     manager_.reset(new opt::PassManager());
159     manager_->SetMessageConsumer(consumer_);
160   }
161 
162   // Runs the passes added thus far using a pass manager on the binary assembled
163   // from the |original| assembly, and checks whether the optimized binary can
164   // be disassembled to the |expected| assembly. Callers are suggested to use
165   // SCOPED_TRACE() for better messages.
RunAndCheck(const std::string & original,const std::string & expected)166   void RunAndCheck(const std::string& original, const std::string& expected) {
167     assert(manager_->NumPasses());
168 
169     std::unique_ptr<ir::Module> module = BuildModule(
170         SPV_ENV_UNIVERSAL_1_1, nullptr, original, assemble_options_);
171     ASSERT_NE(nullptr, module);
172 
173     manager_->Run(module.get());
174 
175     std::vector<uint32_t> binary;
176     module->ToBinary(&binary, /* skip_nop = */ false);
177 
178     std::string optimized;
179     EXPECT_TRUE(tools_.Disassemble(binary, &optimized,
180                                    disassemble_options_));
181     EXPECT_EQ(expected, optimized);
182   }
183 
SetAssembleOptions(uint32_t assemble_options)184   void SetAssembleOptions(uint32_t assemble_options) {
185     assemble_options_ = assemble_options;
186   }
187 
SetDisassembleOptions(uint32_t disassemble_options)188   void SetDisassembleOptions(uint32_t disassemble_options) {
189     disassemble_options_ = disassemble_options;
190   }
191 
192  private:
193   MessageConsumer consumer_;  // Message consumer.
194   SpirvTools tools_;  // An instance for calling SPIRV-Tools functionalities.
195   std::unique_ptr<opt::PassManager> manager_;  // The pass manager.
196   uint32_t assemble_options_;
197   uint32_t disassemble_options_;
198 };
199 
200 }  // namespace spvtools
201 
202 #endif  // LIBSPIRV_TEST_OPT_PASS_FIXTURE_H_
203