reducer.cpp 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  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_opname_instruction_reduction_opportunity_finder.h"
  25. #include "source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h"
  26. #include "source/reduce/remove_selection_reduction_opportunity_finder.h"
  27. #include "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h"
  28. #include "source/reduce/simple_conditional_branch_to_branch_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. struct Reducer::Impl {
  34. explicit Impl(spv_target_env env) : target_env(env) {}
  35. bool ReachedStepLimit(uint32_t current_step,
  36. spv_const_reducer_options options);
  37. const spv_target_env target_env; // Target environment.
  38. MessageConsumer consumer; // Message consumer.
  39. InterestingnessFunction interestingness_function;
  40. std::vector<std::unique_ptr<ReductionPass>> passes;
  41. };
  42. Reducer::Reducer(spv_target_env env) : impl_(MakeUnique<Impl>(env)) {}
  43. Reducer::~Reducer() = default;
  44. void Reducer::SetMessageConsumer(MessageConsumer c) {
  45. for (auto& pass : impl_->passes) {
  46. pass->SetMessageConsumer(c);
  47. }
  48. impl_->consumer = std::move(c);
  49. }
  50. void Reducer::SetInterestingnessFunction(
  51. Reducer::InterestingnessFunction interestingness_function) {
  52. impl_->interestingness_function = std::move(interestingness_function);
  53. }
  54. Reducer::ReductionResultStatus Reducer::Run(
  55. std::vector<uint32_t>&& binary_in, std::vector<uint32_t>* binary_out,
  56. spv_const_reducer_options options,
  57. spv_validator_options validator_options) const {
  58. std::vector<uint32_t> current_binary(std::move(binary_in));
  59. spvtools::SpirvTools tools(impl_->target_env);
  60. assert(tools.IsValid() && "Failed to create SPIRV-Tools interface");
  61. // Keeps track of how many reduction attempts have been tried. Reduction
  62. // bails out if this reaches a given limit.
  63. uint32_t reductions_applied = 0;
  64. // Initial state should be valid.
  65. if (!tools.Validate(&current_binary[0], current_binary.size(),
  66. validator_options)) {
  67. impl_->consumer(SPV_MSG_INFO, nullptr, {},
  68. "Initial binary is invalid; stopping.");
  69. return Reducer::ReductionResultStatus::kInitialStateInvalid;
  70. }
  71. // Initial state should be interesting.
  72. if (!impl_->interestingness_function(current_binary, reductions_applied)) {
  73. impl_->consumer(SPV_MSG_INFO, nullptr, {},
  74. "Initial state was not interesting; stopping.");
  75. return Reducer::ReductionResultStatus::kInitialStateNotInteresting;
  76. }
  77. // Determines whether, on completing one round of reduction passes, it is
  78. // worthwhile trying a further round.
  79. bool another_round_worthwhile = true;
  80. // Apply round after round of reduction passes until we hit the reduction
  81. // step limit, or deem that another round is not going to be worthwhile.
  82. while (!impl_->ReachedStepLimit(reductions_applied, options) &&
  83. another_round_worthwhile) {
  84. // At the start of a round of reduction passes, assume another round will
  85. // not be worthwhile unless we find evidence to the contrary.
  86. another_round_worthwhile = false;
  87. // Iterate through the available passes
  88. for (auto& pass : impl_->passes) {
  89. // If this pass hasn't reached its minimum granularity then it's
  90. // worth eventually doing another round of reductions, in order to
  91. // try this pass at a finer granularity.
  92. another_round_worthwhile |= !pass->ReachedMinimumGranularity();
  93. // Keep applying this pass at its current granularity until it stops
  94. // working or we hit the reduction step limit.
  95. impl_->consumer(SPV_MSG_INFO, nullptr, {},
  96. ("Trying pass " + pass->GetName() + ".").c_str());
  97. do {
  98. auto maybe_result = pass->TryApplyReduction(current_binary);
  99. if (maybe_result.empty()) {
  100. // For this round, the pass has no more opportunities (chunks) to
  101. // apply, so move on to the next pass.
  102. impl_->consumer(
  103. SPV_MSG_INFO, nullptr, {},
  104. ("Pass " + pass->GetName() + " did not make a reduction step.")
  105. .c_str());
  106. break;
  107. }
  108. bool interesting = false;
  109. std::stringstream stringstream;
  110. reductions_applied++;
  111. stringstream << "Pass " << pass->GetName() << " made reduction step "
  112. << reductions_applied << ".";
  113. impl_->consumer(SPV_MSG_INFO, nullptr, {},
  114. (stringstream.str().c_str()));
  115. if (!tools.Validate(&maybe_result[0], maybe_result.size(),
  116. validator_options)) {
  117. // The reduction step went wrong and an invalid binary was produced.
  118. // By design, this shouldn't happen; this is a safeguard to stop an
  119. // invalid binary from being regarded as interesting.
  120. impl_->consumer(SPV_MSG_INFO, nullptr, {},
  121. "Reduction step produced an invalid binary.");
  122. if (options->fail_on_validation_error) {
  123. return Reducer::ReductionResultStatus::kStateInvalid;
  124. }
  125. } else if (impl_->interestingness_function(maybe_result,
  126. reductions_applied)) {
  127. // Success! The binary produced by this reduction step is
  128. // interesting, so make it the binary of interest henceforth, and
  129. // note that it's worth doing another round of reduction passes.
  130. impl_->consumer(SPV_MSG_INFO, nullptr, {},
  131. "Reduction step succeeded.");
  132. current_binary = std::move(maybe_result);
  133. interesting = true;
  134. another_round_worthwhile = true;
  135. }
  136. // We must call this before the next call to TryApplyReduction.
  137. pass->NotifyInteresting(interesting);
  138. // Bail out if the reduction step limit has been reached.
  139. } while (!impl_->ReachedStepLimit(reductions_applied, options));
  140. }
  141. }
  142. *binary_out = std::move(current_binary);
  143. // Report whether reduction completed, or bailed out early due to reaching
  144. // the step limit.
  145. if (impl_->ReachedStepLimit(reductions_applied, options)) {
  146. impl_->consumer(SPV_MSG_INFO, nullptr, {},
  147. "Reached reduction step limit; stopping.");
  148. return Reducer::ReductionResultStatus::kReachedStepLimit;
  149. }
  150. impl_->consumer(SPV_MSG_INFO, nullptr, {}, "No more to reduce; stopping.");
  151. return Reducer::ReductionResultStatus::kComplete;
  152. }
  153. void Reducer::AddDefaultReductionPasses() {
  154. AddReductionPass(spvtools::MakeUnique<
  155. RemoveOpNameInstructionReductionOpportunityFinder>());
  156. AddReductionPass(spvtools::MakeUnique<
  157. RemoveRelaxedPrecisionDecorationOpportunityFinder>());
  158. AddReductionPass(
  159. spvtools::MakeUnique<OperandToUndefReductionOpportunityFinder>());
  160. AddReductionPass(
  161. spvtools::MakeUnique<OperandToConstReductionOpportunityFinder>());
  162. AddReductionPass(
  163. spvtools::MakeUnique<OperandToDominatingIdReductionOpportunityFinder>());
  164. AddReductionPass(spvtools::MakeUnique<
  165. RemoveUnreferencedInstructionReductionOpportunityFinder>());
  166. AddReductionPass(spvtools::MakeUnique<
  167. StructuredLoopToSelectionReductionOpportunityFinder>());
  168. AddReductionPass(
  169. spvtools::MakeUnique<MergeBlocksReductionOpportunityFinder>());
  170. AddReductionPass(
  171. spvtools::MakeUnique<RemoveFunctionReductionOpportunityFinder>());
  172. AddReductionPass(
  173. spvtools::MakeUnique<RemoveBlockReductionOpportunityFinder>());
  174. AddReductionPass(
  175. spvtools::MakeUnique<RemoveSelectionReductionOpportunityFinder>());
  176. AddReductionPass(
  177. spvtools::MakeUnique<
  178. ConditionalBranchToSimpleConditionalBranchOpportunityFinder>());
  179. AddReductionPass(
  180. spvtools::MakeUnique<SimpleConditionalBranchToBranchOpportunityFinder>());
  181. }
  182. void Reducer::AddReductionPass(
  183. std::unique_ptr<ReductionOpportunityFinder>&& finder) {
  184. impl_->passes.push_back(spvtools::MakeUnique<ReductionPass>(
  185. impl_->target_env, std::move(finder)));
  186. }
  187. bool Reducer::Impl::ReachedStepLimit(uint32_t current_step,
  188. spv_const_reducer_options options) {
  189. return current_step >= options->step_limit;
  190. }
  191. } // namespace reduce
  192. } // namespace spvtools