mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-22 11:40:05 +00:00
d35a78db57
Fixes #4960 * Switches to using enum classes with an underlying type to avoid undefined behaviour
147 lines
5.3 KiB
C++
147 lines
5.3 KiB
C++
// Copyright (c) 2019 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/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;
|
|
} // namespace
|
|
|
|
std::string RemoveSelectionReductionOpportunityFinder::GetName() const {
|
|
return "RemoveSelectionReductionOpportunityFinder";
|
|
}
|
|
|
|
std::vector<std::unique_ptr<ReductionOpportunity>>
|
|
RemoveSelectionReductionOpportunityFinder::GetAvailableOpportunities(
|
|
opt::IRContext* context, uint32_t target_function) const {
|
|
// Get all loop merge and continue blocks so we can check for these later.
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Return all selection headers where the OpSelectionMergeInstruction can be
|
|
// removed.
|
|
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");
|
|
|
|
// The OpSelectionMerge instruction is needed if either of the following are
|
|
// true.
|
|
//
|
|
// 1. The header block has at least two (unique) successors that are not
|
|
// merge or continue blocks of a loop.
|
|
//
|
|
// 2. The predecessors of the merge block are "using" the merge block to avoid
|
|
// divergence. In other words, there exists a predecessor of the merge block
|
|
// that has a successor that is not the merge block of this construct and not
|
|
// a merge or continue block of a loop.
|
|
|
|
// 1.
|
|
{
|
|
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) {
|
|
// Not already seen.
|
|
if (seen_successors.find(successor) == seen_successors.end()) {
|
|
seen_successors.insert(successor);
|
|
// Not a loop continue or merge.
|
|
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;
|
|
}
|
|
}
|
|
|
|
// 2.
|
|
{
|
|
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) {
|
|
// The successor is not the merge block, nor a loop merge or
|
|
// continue.
|
|
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;
|
|
}
|
|
|
|
} // namespace reduce
|
|
} // namespace spvtools
|