• 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 <cassert>
16 #include <sstream>
17 
18 #include "source/spirv_reducer_options.h"
19 
20 #include "reducer.h"
21 #include "reduction_pass.h"
22 
23 namespace spvtools {
24 namespace reduce {
25 
26 struct Reducer::Impl {
Implspvtools::reduce::Reducer::Impl27   explicit Impl(spv_target_env env) : target_env(env) {}
28 
29   bool ReachedStepLimit(uint32_t current_step,
30                         spv_const_reducer_options options);
31 
32   const spv_target_env target_env;  // Target environment.
33   MessageConsumer consumer;         // Message consumer.
34   InterestingnessFunction interestingness_function;
35   std::vector<std::unique_ptr<ReductionPass>> passes;
36 };
37 
Reducer(spv_target_env env)38 Reducer::Reducer(spv_target_env env) : impl_(MakeUnique<Impl>(env)) {}
39 
40 Reducer::~Reducer() = default;
41 
SetMessageConsumer(MessageConsumer c)42 void Reducer::SetMessageConsumer(MessageConsumer c) {
43   for (auto& pass : impl_->passes) {
44     pass->SetMessageConsumer(c);
45   }
46   impl_->consumer = std::move(c);
47 }
48 
SetInterestingnessFunction(Reducer::InterestingnessFunction interestingness_function)49 void Reducer::SetInterestingnessFunction(
50     Reducer::InterestingnessFunction interestingness_function) {
51   impl_->interestingness_function = std::move(interestingness_function);
52 }
53 
Run(std::vector<uint32_t> && binary_in,std::vector<uint32_t> * binary_out,spv_const_reducer_options options) const54 Reducer::ReductionResultStatus Reducer::Run(
55     std::vector<uint32_t>&& binary_in, std::vector<uint32_t>* binary_out,
56     spv_const_reducer_options options) const {
57   std::vector<uint32_t> current_binary = binary_in;
58 
59   // Keeps track of how many reduction attempts have been tried.  Reduction
60   // bails out if this reaches a given limit.
61   uint32_t reductions_applied = 0;
62 
63   // Initial state should be interesting.
64   if (!impl_->interestingness_function(current_binary, reductions_applied)) {
65     impl_->consumer(SPV_MSG_INFO, nullptr, {},
66                     "Initial state was not interesting; stopping.");
67     return Reducer::ReductionResultStatus::kInitialStateNotInteresting;
68   }
69 
70   // Determines whether, on completing one round of reduction passes, it is
71   // worthwhile trying a further round.
72   bool another_round_worthwhile = true;
73 
74   // Apply round after round of reduction passes until we hit the reduction
75   // step limit, or deem that another round is not going to be worthwhile.
76   while (!impl_->ReachedStepLimit(reductions_applied, options) &&
77          another_round_worthwhile) {
78     // At the start of a round of reduction passes, assume another round will
79     // not be worthwhile unless we find evidence to the contrary.
80     another_round_worthwhile = false;
81 
82     // Iterate through the available passes
83     for (auto& pass : impl_->passes) {
84       // If this pass hasn't reached its minimum granularity then it's
85       // worth eventually doing another round of reductions, in order to
86       // try this pass at a finer granularity.
87       another_round_worthwhile |= !pass->ReachedMinimumGranularity();
88 
89       // Keep applying this pass at its current granularity until it stops
90       // working or we hit the reduction step limit.
91       impl_->consumer(SPV_MSG_INFO, nullptr, {},
92                       ("Trying pass " + pass->GetName() + ".").c_str());
93       do {
94         auto maybe_result = pass->TryApplyReduction(current_binary);
95         if (maybe_result.empty()) {
96           // This pass did not have any impact, so move on to the next pass.
97           impl_->consumer(
98               SPV_MSG_INFO, nullptr, {},
99               ("Pass " + pass->GetName() + " did not make a reduction step.")
100                   .c_str());
101           break;
102         }
103         std::stringstream stringstream;
104         reductions_applied++;
105         stringstream << "Pass " << pass->GetName() << " made reduction step "
106                      << reductions_applied << ".";
107         impl_->consumer(SPV_MSG_INFO, nullptr, {},
108                         (stringstream.str().c_str()));
109         if (!spvtools::SpirvTools(impl_->target_env).Validate(maybe_result)) {
110           // The reduction step went wrong and an invalid binary was produced.
111           // By design, this shouldn't happen; this is a safeguard to stop an
112           // invalid binary from being regarded as interesting.
113           impl_->consumer(SPV_MSG_INFO, nullptr, {},
114                           "Reduction step produced an invalid binary.");
115         } else if (impl_->interestingness_function(maybe_result,
116                                                    reductions_applied)) {
117           // Success!  The binary produced by this reduction step is
118           // interesting, so make it the binary of interest henceforth, and
119           // note that it's worth doing another round of reduction passes.
120           impl_->consumer(SPV_MSG_INFO, nullptr, {},
121                           "Reduction step succeeded.");
122           current_binary = std::move(maybe_result);
123           another_round_worthwhile = true;
124         }
125         // Bail out if the reduction step limit has been reached.
126       } while (!impl_->ReachedStepLimit(reductions_applied, options));
127     }
128   }
129 
130   *binary_out = std::move(current_binary);
131 
132   // Report whether reduction completed, or bailed out early due to reaching
133   // the step limit.
134   if (impl_->ReachedStepLimit(reductions_applied, options)) {
135     impl_->consumer(SPV_MSG_INFO, nullptr, {},
136                     "Reached reduction step limit; stopping.");
137     return Reducer::ReductionResultStatus::kReachedStepLimit;
138   }
139   impl_->consumer(SPV_MSG_INFO, nullptr, {}, "No more to reduce; stopping.");
140   return Reducer::ReductionResultStatus::kComplete;
141 }
142 
AddReductionPass(std::unique_ptr<ReductionPass> && reduction_pass)143 void Reducer::AddReductionPass(
144     std::unique_ptr<ReductionPass>&& reduction_pass) {
145   impl_->passes.push_back(std::move(reduction_pass));
146 }
147 
ReachedStepLimit(uint32_t current_step,spv_const_reducer_options options)148 bool Reducer::Impl::ReachedStepLimit(uint32_t current_step,
149                                      spv_const_reducer_options options) {
150   return current_step >= options->step_limit;
151 }
152 
153 }  // namespace reduce
154 }  // namespace spvtools
155