#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");
uint32_t reductions_applied = 0;
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;
}
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) {
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.");
}
*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>());
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) {
bool another_round_worthwhile = true;
while (!ReachedStepLimit(*reductions_applied, options) &&
another_round_worthwhile) {
another_round_worthwhile = false;
for (auto& pass : *passes) {
another_round_worthwhile |= !pass->ReachedMinimumGranularity();
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()) {
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)) {
consumer_(SPV_MSG_INFO, nullptr, {},
"Reduction step produced an invalid binary.");
if (options->fail_on_validation_error) {
*current_binary = std::move(maybe_result);
return Reducer::ReductionResultStatus::kStateInvalid;
}
} else if (interestingness_function_(maybe_result,
*reductions_applied)) {
consumer_(SPV_MSG_INFO, nullptr, {}, "Reduction step succeeded.");
*current_binary = std::move(maybe_result);
interesting = true;
another_round_worthwhile = true;
}
pass->NotifyInteresting(interesting);
} while (!ReachedStepLimit(*reductions_applied, options));
}
}
if (ReachedStepLimit(*reductions_applied, options)) {
consumer_(SPV_MSG_INFO, nullptr, {},
"Reached reduction step limit; stopping.");
return Reducer::ReductionResultStatus::kReachedStepLimit;
}
return Reducer::ReductionResultStatus::kComplete;
}
}
}