#include "source/reduce/remove_selection_reduction_opportunity_finder.h"
#include "source/reduce/remove_selection_reduction_opportunity.h"
namespace spvtools {
namespace reduce {
namespace {
const uint32_t kMergeNodeIndex = 0;
const uint32_t kContinueNodeIndex = 1;
}
std::string RemoveSelectionReductionOpportunityFinder::GetName() const {
return "RemoveSelectionReductionOpportunityFinder";
}
std::vector<std::unique_ptr<ReductionOpportunity>>
RemoveSelectionReductionOpportunityFinder::GetAvailableOpportunities(
opt::IRContext* context, uint32_t target_function) const {
std::unordered_set<uint32_t> merge_and_continue_blocks_from_loops;
for (auto* function : GetTargetFunctions(context, target_function)) {
for (auto& block : *function) {
if (auto merge_instruction = block.GetMergeInst()) {
if (merge_instruction->opcode() == spv::Op::OpLoopMerge) {
uint32_t merge_block_id =
merge_instruction->GetSingleWordOperand(kMergeNodeIndex);
uint32_t continue_block_id =
merge_instruction->GetSingleWordOperand(kContinueNodeIndex);
merge_and_continue_blocks_from_loops.insert(merge_block_id);
merge_and_continue_blocks_from_loops.insert(continue_block_id);
}
}
}
}
std::vector<std::unique_ptr<ReductionOpportunity>> result;
for (auto& function : *context->module()) {
for (auto& block : function) {
if (auto merge_instruction = block.GetMergeInst()) {
if (merge_instruction->opcode() == spv::Op::OpSelectionMerge) {
if (CanOpSelectionMergeBeRemoved(
context, block, merge_instruction,
merge_and_continue_blocks_from_loops)) {
result.push_back(
MakeUnique<RemoveSelectionReductionOpportunity>(&block));
}
}
}
}
}
return result;
}
bool RemoveSelectionReductionOpportunityFinder::CanOpSelectionMergeBeRemoved(
opt::IRContext* context, const opt::BasicBlock& header_block,
opt::Instruction* merge_instruction,
std::unordered_set<uint32_t> merge_and_continue_blocks_from_loops) {
assert(header_block.GetMergeInst() == merge_instruction &&
"CanOpSelectionMergeBeRemoved(...): header block and merge "
"instruction mismatch");
{
uint32_t divergent_successor_count = 0;
std::unordered_set<uint32_t> seen_successors;
header_block.ForEachSuccessorLabel(
[&seen_successors, &merge_and_continue_blocks_from_loops,
&divergent_successor_count](uint32_t successor) {
if (seen_successors.find(successor) == seen_successors.end()) {
seen_successors.insert(successor);
if (merge_and_continue_blocks_from_loops.find(successor) ==
merge_and_continue_blocks_from_loops.end()) {
++divergent_successor_count;
}
}
});
if (divergent_successor_count > 1) {
return false;
}
}
{
uint32_t merge_block_id =
merge_instruction->GetSingleWordOperand(kMergeNodeIndex);
for (uint32_t predecessor_block_id :
context->cfg()->preds(merge_block_id)) {
const opt::BasicBlock* predecessor_block =
context->cfg()->block(predecessor_block_id);
assert(predecessor_block);
bool found_divergent_successor = false;
predecessor_block->ForEachSuccessorLabel(
[&found_divergent_successor, merge_block_id,
&merge_and_continue_blocks_from_loops](uint32_t successor_id) {
if (successor_id != merge_block_id &&
merge_and_continue_blocks_from_loops.find(successor_id) ==
merge_and_continue_blocks_from_loops.end()) {
found_divergent_successor = true;
}
});
if (found_divergent_successor) {
return false;
}
}
}
return true;
}
}
}