| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- // Copyright (c) 2018 Google LLC
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- #include "source/reduce/reducer.h"
- #include <cassert>
- #include <sstream>
- #include "source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.h"
- #include "source/reduce/merge_blocks_reduction_opportunity_finder.h"
- #include "source/reduce/operand_to_const_reduction_opportunity_finder.h"
- #include "source/reduce/operand_to_dominating_id_reduction_opportunity_finder.h"
- #include "source/reduce/operand_to_undef_reduction_opportunity_finder.h"
- #include "source/reduce/remove_block_reduction_opportunity_finder.h"
- #include "source/reduce/remove_function_reduction_opportunity_finder.h"
- #include "source/reduce/remove_selection_reduction_opportunity_finder.h"
- #include "source/reduce/remove_unused_instruction_reduction_opportunity_finder.h"
- #include "source/reduce/remove_unused_struct_member_reduction_opportunity_finder.h"
- #include "source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h"
- #include "source/reduce/structured_construct_to_block_reduction_opportunity_finder.h"
- #include "source/reduce/structured_loop_to_selection_reduction_opportunity_finder.h"
- #include "source/spirv_reducer_options.h"
- namespace spvtools {
- namespace reduce {
- Reducer::Reducer(spv_target_env target_env) : target_env_(target_env) {}
- Reducer::~Reducer() = default;
- void Reducer::SetMessageConsumer(MessageConsumer c) {
- for (auto& pass : passes_) {
- pass->SetMessageConsumer(c);
- }
- for (auto& pass : cleanup_passes_) {
- pass->SetMessageConsumer(c);
- }
- consumer_ = std::move(c);
- }
- void Reducer::SetInterestingnessFunction(
- Reducer::InterestingnessFunction interestingness_function) {
- interestingness_function_ = std::move(interestingness_function);
- }
- Reducer::ReductionResultStatus Reducer::Run(
- const std::vector<uint32_t>& binary_in, std::vector<uint32_t>* binary_out,
- spv_const_reducer_options options,
- spv_validator_options validator_options) {
- std::vector<uint32_t> current_binary(binary_in);
- spvtools::SpirvTools tools(target_env_);
- assert(tools.IsValid() && "Failed to create SPIRV-Tools interface");
- // Keeps track of how many reduction attempts have been tried. Reduction
- // bails out if this reaches a given limit.
- uint32_t reductions_applied = 0;
- // Initial state should be valid.
- if (!tools.Validate(¤t_binary[0], current_binary.size(),
- validator_options)) {
- consumer_(SPV_MSG_INFO, nullptr, {},
- "Initial binary is invalid; stopping.");
- return Reducer::ReductionResultStatus::kInitialStateInvalid;
- }
- // Initial state should be interesting.
- if (!interestingness_function_(current_binary, reductions_applied)) {
- consumer_(SPV_MSG_INFO, nullptr, {},
- "Initial state was not interesting; stopping.");
- return Reducer::ReductionResultStatus::kInitialStateNotInteresting;
- }
- Reducer::ReductionResultStatus result =
- RunPasses(&passes_, options, validator_options, tools, ¤t_binary,
- &reductions_applied);
- if (result == Reducer::ReductionResultStatus::kComplete) {
- // Cleanup passes.
- result = RunPasses(&cleanup_passes_, options, validator_options, tools,
- ¤t_binary, &reductions_applied);
- }
- if (result == Reducer::ReductionResultStatus::kComplete) {
- consumer_(SPV_MSG_INFO, nullptr, {}, "No more to reduce; stopping.");
- }
- // Even if the reduction has failed by this point (e.g. due to producing an
- // invalid binary), we still update the output binary for better debugging.
- *binary_out = std::move(current_binary);
- return result;
- }
- void Reducer::AddDefaultReductionPasses() {
- AddReductionPass(
- spvtools::MakeUnique<RemoveUnusedInstructionReductionOpportunityFinder>(
- false));
- AddReductionPass(
- spvtools::MakeUnique<OperandToUndefReductionOpportunityFinder>());
- AddReductionPass(
- spvtools::MakeUnique<OperandToConstReductionOpportunityFinder>());
- AddReductionPass(
- spvtools::MakeUnique<OperandToDominatingIdReductionOpportunityFinder>());
- AddReductionPass(spvtools::MakeUnique<
- StructuredConstructToBlockReductionOpportunityFinder>());
- AddReductionPass(spvtools::MakeUnique<
- StructuredLoopToSelectionReductionOpportunityFinder>());
- AddReductionPass(
- spvtools::MakeUnique<MergeBlocksReductionOpportunityFinder>());
- AddReductionPass(
- spvtools::MakeUnique<RemoveFunctionReductionOpportunityFinder>());
- AddReductionPass(
- spvtools::MakeUnique<RemoveBlockReductionOpportunityFinder>());
- AddReductionPass(
- spvtools::MakeUnique<RemoveSelectionReductionOpportunityFinder>());
- AddReductionPass(
- spvtools::MakeUnique<
- ConditionalBranchToSimpleConditionalBranchOpportunityFinder>());
- AddReductionPass(
- spvtools::MakeUnique<SimpleConditionalBranchToBranchOpportunityFinder>());
- AddReductionPass(spvtools::MakeUnique<
- RemoveUnusedStructMemberReductionOpportunityFinder>());
- // Cleanup passes.
- AddCleanupReductionPass(
- spvtools::MakeUnique<RemoveUnusedInstructionReductionOpportunityFinder>(
- true));
- }
- void Reducer::AddReductionPass(
- std::unique_ptr<ReductionOpportunityFinder> finder) {
- passes_.push_back(
- spvtools::MakeUnique<ReductionPass>(target_env_, std::move(finder)));
- }
- void Reducer::AddCleanupReductionPass(
- std::unique_ptr<ReductionOpportunityFinder> finder) {
- cleanup_passes_.push_back(
- spvtools::MakeUnique<ReductionPass>(target_env_, std::move(finder)));
- }
- bool Reducer::ReachedStepLimit(uint32_t current_step,
- spv_const_reducer_options options) {
- return current_step >= options->step_limit;
- }
- Reducer::ReductionResultStatus Reducer::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) {
- // Determines whether, on completing one round of reduction passes, it is
- // worthwhile trying a further round.
- bool another_round_worthwhile = true;
- // Apply round after round of reduction passes until we hit the reduction
- // step limit, or deem that another round is not going to be worthwhile.
- while (!ReachedStepLimit(*reductions_applied, options) &&
- another_round_worthwhile) {
- // At the start of a round of reduction passes, assume another round will
- // not be worthwhile unless we find evidence to the contrary.
- another_round_worthwhile = false;
- // Iterate through the available passes.
- for (auto& pass : *passes) {
- // If this pass hasn't reached its minimum granularity then it's
- // worth eventually doing another round of reductions, in order to
- // try this pass at a finer granularity.
- another_round_worthwhile |= !pass->ReachedMinimumGranularity();
- // Keep applying this pass at its current granularity until it stops
- // working or we hit the reduction step limit.
- consumer_(SPV_MSG_INFO, nullptr, {},
- ("Trying pass " + pass->GetName() + ".").c_str());
- do {
- auto maybe_result =
- pass->TryApplyReduction(*current_binary, options->target_function);
- if (maybe_result.empty()) {
- // For this round, the pass has no more opportunities (chunks) to
- // apply, so move on to the next pass.
- consumer_(
- SPV_MSG_INFO, nullptr, {},
- ("Pass " + pass->GetName() + " did not make a reduction step.")
- .c_str());
- break;
- }
- bool interesting = false;
- std::stringstream stringstream;
- (*reductions_applied)++;
- stringstream << "Pass " << pass->GetName() << " made reduction step "
- << *reductions_applied << ".";
- consumer_(SPV_MSG_INFO, nullptr, {}, (stringstream.str().c_str()));
- if (!tools.Validate(&maybe_result[0], maybe_result.size(),
- validator_options)) {
- // The reduction step went wrong and an invalid binary was produced.
- // By design, this shouldn't happen; this is a safeguard to stop an
- // invalid binary from being regarded as interesting.
- consumer_(SPV_MSG_INFO, nullptr, {},
- "Reduction step produced an invalid binary.");
- if (options->fail_on_validation_error) {
- // In this mode, we fail, so we update the current binary so it is
- // output for debugging.
- *current_binary = std::move(maybe_result);
- return Reducer::ReductionResultStatus::kStateInvalid;
- }
- } else if (interestingness_function_(maybe_result,
- *reductions_applied)) {
- // Success! The binary produced by this reduction step is
- // interesting, so make it the binary of interest henceforth, and
- // note that it's worth doing another round of reduction passes.
- consumer_(SPV_MSG_INFO, nullptr, {}, "Reduction step succeeded.");
- *current_binary = std::move(maybe_result);
- interesting = true;
- another_round_worthwhile = true;
- }
- // We must call this before the next call to TryApplyReduction.
- pass->NotifyInteresting(interesting);
- // Bail out if the reduction step limit has been reached.
- } while (!ReachedStepLimit(*reductions_applied, options));
- }
- }
- // Report whether reduction completed, or bailed out early due to reaching
- // the step limit.
- if (ReachedStepLimit(*reductions_applied, options)) {
- consumer_(SPV_MSG_INFO, nullptr, {},
- "Reached reduction step limit; stopping.");
- return Reducer::ReductionResultStatus::kReachedStepLimit;
- }
- // The passes completed successfully, although we may still run more passes.
- return Reducer::ReductionResultStatus::kComplete;
- }
- } // namespace reduce
- } // namespace spvtools
|