pass_fixture.h 10 KB

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