pass_fixture.h 13 KB

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