reducer.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. // Copyright (c) 2018 Google LLC
  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. #include "source/reduce/reducer.h"
  15. #include <cassert>
  16. #include <sstream>
  17. #include "source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.h"
  18. #include "source/reduce/merge_blocks_reduction_opportunity_finder.h"
  19. #include "source/reduce/operand_to_const_reduction_opportunity_finder.h"
  20. #include "source/reduce/operand_to_dominating_id_reduction_opportunity_finder.h"
  21. #include "source/reduce/operand_to_undef_reduction_opportunity_finder.h"
  22. #include "source/reduce/remove_block_reduction_opportunity_finder.h"
  23. #include "source/reduce/remove_function_reduction_opportunity_finder.h"
  24. #include "source/reduce/remove_selection_reduction_opportunity_finder.h"
  25. #include "source/reduce/remove_unused_instruction_reduction_opportunity_finder.h"
  26. #include "source/reduce/remove_unused_struct_member_reduction_opportunity_finder.h"
  27. #include "source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h"
  28. #include "source/reduce/structured_construct_to_block_reduction_opportunity_finder.h"
  29. #include "source/reduce/structured_loop_to_selection_reduction_opportunity_finder.h"
  30. #include "source/spirv_reducer_options.h"
  31. namespace spvtools {
  32. namespace reduce {
  33. Reducer::Reducer(spv_target_env target_env) : target_env_(target_env) {}
  34. Reducer::~Reducer() = default;
  35. void Reducer::SetMessageConsumer(MessageConsumer c) {
  36. for (auto& pass : passes_) {
  37. pass->SetMessageConsumer(c);
  38. }
  39. for (auto& pass : cleanup_passes_) {
  40. pass->SetMessageConsumer(c);
  41. }
  42. consumer_ = std::move(c);
  43. }
  44. void Reducer::SetInterestingnessFunction(
  45. Reducer::InterestingnessFunction interestingness_function) {
  46. interestingness_function_ = std::move(interestingness_function);
  47. }
  48. Reducer::ReductionResultStatus Reducer::Run(
  49. const std::vector<uint32_t>& binary_in, std::vector<uint32_t>* binary_out,
  50. spv_const_reducer_options options,
  51. spv_validator_options validator_options) {
  52. std::vector<uint32_t> current_binary(binary_in);
  53. spvtools::SpirvTools tools(target_env_);
  54. assert(tools.IsValid() && "Failed to create SPIRV-Tools interface");
  55. // Keeps track of how many reduction attempts have been tried. Reduction
  56. // bails out if this reaches a given limit.
  57. uint32_t reductions_applied = 0;
  58. // Initial state should be valid.
  59. if (!tools.Validate(&current_binary[0], current_binary.size(),
  60. validator_options)) {
  61. consumer_(SPV_MSG_INFO, nullptr, {},
  62. "Initial binary is invalid; stopping.");
  63. return Reducer::ReductionResultStatus::kInitialStateInvalid;
  64. }
  65. // Initial state should be interesting.
  66. if (!interestingness_function_(current_binary, reductions_applied)) {
  67. consumer_(SPV_MSG_INFO, nullptr, {},
  68. "Initial state was not interesting; stopping.");
  69. return Reducer::ReductionResultStatus::kInitialStateNotInteresting;
  70. }
  71. Reducer::ReductionResultStatus result =
  72. RunPasses(&passes_, options, validator_options, tools, &current_binary,
  73. &reductions_applied);
  74. if (result == Reducer::ReductionResultStatus::kComplete) {
  75. // Cleanup passes.
  76. result = RunPasses(&cleanup_passes_, options, validator_options, tools,
  77. &current_binary, &reductions_applied);
  78. }
  79. if (result == Reducer::ReductionResultStatus::kComplete) {
  80. consumer_(SPV_MSG_INFO, nullptr, {}, "No more to reduce; stopping.");
  81. }
  82. // Even if the reduction has failed by this point (e.g. due to producing an
  83. // invalid binary), we still update the output binary for better debugging.
  84. *binary_out = std::move(current_binary);
  85. return result;
  86. }
  87. void Reducer::AddDefaultReductionPasses() {
  88. AddReductionPass(
  89. spvtools::MakeUnique<RemoveUnusedInstructionReductionOpportunityFinder>(
  90. false));
  91. AddReductionPass(
  92. spvtools::MakeUnique<OperandToUndefReductionOpportunityFinder>());
  93. AddReductionPass(
  94. spvtools::MakeUnique<OperandToConstReductionOpportunityFinder>());
  95. AddReductionPass(
  96. spvtools::MakeUnique<OperandToDominatingIdReductionOpportunityFinder>());
  97. AddReductionPass(spvtools::MakeUnique<
  98. StructuredConstructToBlockReductionOpportunityFinder>());
  99. AddReductionPass(spvtools::MakeUnique<
  100. StructuredLoopToSelectionReductionOpportunityFinder>());
  101. AddReductionPass(
  102. spvtools::MakeUnique<MergeBlocksReductionOpportunityFinder>());
  103. AddReductionPass(
  104. spvtools::MakeUnique<RemoveFunctionReductionOpportunityFinder>());
  105. AddReductionPass(
  106. spvtools::MakeUnique<RemoveBlockReductionOpportunityFinder>());
  107. AddReductionPass(
  108. spvtools::MakeUnique<RemoveSelectionReductionOpportunityFinder>());
  109. AddReductionPass(
  110. spvtools::MakeUnique<
  111. ConditionalBranchToSimpleConditionalBranchOpportunityFinder>());
  112. AddReductionPass(
  113. spvtools::MakeUnique<SimpleConditionalBranchToBranchOpportunityFinder>());
  114. AddReductionPass(spvtools::MakeUnique<
  115. RemoveUnusedStructMemberReductionOpportunityFinder>());
  116. // Cleanup passes.
  117. AddCleanupReductionPass(
  118. spvtools::MakeUnique<RemoveUnusedInstructionReductionOpportunityFinder>(
  119. true));
  120. }
  121. void Reducer::AddReductionPass(
  122. std::unique_ptr<ReductionOpportunityFinder> finder) {
  123. passes_.push_back(
  124. spvtools::MakeUnique<ReductionPass>(target_env_, std::move(finder)));
  125. }
  126. void Reducer::AddCleanupReductionPass(
  127. std::unique_ptr<ReductionOpportunityFinder> finder) {
  128. cleanup_passes_.push_back(
  129. spvtools::MakeUnique<ReductionPass>(target_env_, std::move(finder)));
  130. }
  131. bool Reducer::ReachedStepLimit(uint32_t current_step,
  132. spv_const_reducer_options options) {
  133. return current_step >= options->step_limit;
  134. }
  135. Reducer::ReductionResultStatus Reducer::RunPasses(
  136. std::vector<std::unique_ptr<ReductionPass>>* passes,
  137. spv_const_reducer_options options, spv_validator_options validator_options,
  138. const SpirvTools& tools, std::vector<uint32_t>* current_binary,
  139. uint32_t* const reductions_applied) {
  140. // Determines whether, on completing one round of reduction passes, it is
  141. // worthwhile trying a further round.
  142. bool another_round_worthwhile = true;
  143. // Apply round after round of reduction passes until we hit the reduction
  144. // step limit, or deem that another round is not going to be worthwhile.
  145. while (!ReachedStepLimit(*reductions_applied, options) &&
  146. another_round_worthwhile) {
  147. // At the start of a round of reduction passes, assume another round will
  148. // not be worthwhile unless we find evidence to the contrary.
  149. another_round_worthwhile = false;
  150. // Iterate through the available passes.
  151. for (auto& pass : *passes) {
  152. // If this pass hasn't reached its minimum granularity then it's
  153. // worth eventually doing another round of reductions, in order to
  154. // try this pass at a finer granularity.
  155. another_round_worthwhile |= !pass->ReachedMinimumGranularity();
  156. // Keep applying this pass at its current granularity until it stops
  157. // working or we hit the reduction step limit.
  158. consumer_(SPV_MSG_INFO, nullptr, {},
  159. ("Trying pass " + pass->GetName() + ".").c_str());
  160. do {
  161. auto maybe_result =
  162. pass->TryApplyReduction(*current_binary, options->target_function);
  163. if (maybe_result.empty()) {
  164. // For this round, the pass has no more opportunities (chunks) to
  165. // apply, so move on to the next pass.
  166. consumer_(
  167. SPV_MSG_INFO, nullptr, {},
  168. ("Pass " + pass->GetName() + " did not make a reduction step.")
  169. .c_str());
  170. break;
  171. }
  172. bool interesting = false;
  173. std::stringstream stringstream;
  174. (*reductions_applied)++;
  175. stringstream << "Pass " << pass->GetName() << " made reduction step "
  176. << *reductions_applied << ".";
  177. consumer_(SPV_MSG_INFO, nullptr, {}, (stringstream.str().c_str()));
  178. if (!tools.Validate(&maybe_result[0], maybe_result.size(),
  179. validator_options)) {
  180. // The reduction step went wrong and an invalid binary was produced.
  181. // By design, this shouldn't happen; this is a safeguard to stop an
  182. // invalid binary from being regarded as interesting.
  183. consumer_(SPV_MSG_INFO, nullptr, {},
  184. "Reduction step produced an invalid binary.");
  185. if (options->fail_on_validation_error) {
  186. // In this mode, we fail, so we update the current binary so it is
  187. // output for debugging.
  188. *current_binary = std::move(maybe_result);
  189. return Reducer::ReductionResultStatus::kStateInvalid;
  190. }
  191. } else if (interestingness_function_(maybe_result,
  192. *reductions_applied)) {
  193. // Success! The binary produced by this reduction step is
  194. // interesting, so make it the binary of interest henceforth, and
  195. // note that it's worth doing another round of reduction passes.
  196. consumer_(SPV_MSG_INFO, nullptr, {}, "Reduction step succeeded.");
  197. *current_binary = std::move(maybe_result);
  198. interesting = true;
  199. another_round_worthwhile = true;
  200. }
  201. // We must call this before the next call to TryApplyReduction.
  202. pass->NotifyInteresting(interesting);
  203. // Bail out if the reduction step limit has been reached.
  204. } while (!ReachedStepLimit(*reductions_applied, options));
  205. }
  206. }
  207. // Report whether reduction completed, or bailed out early due to reaching
  208. // the step limit.
  209. if (ReachedStepLimit(*reductions_applied, options)) {
  210. consumer_(SPV_MSG_INFO, nullptr, {},
  211. "Reached reduction step limit; stopping.");
  212. return Reducer::ReductionResultStatus::kReachedStepLimit;
  213. }
  214. // The passes completed successfully, although we may still run more passes.
  215. return Reducer::ReductionResultStatus::kComplete;
  216. }
  217. } // namespace reduce
  218. } // namespace spvtools