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