| // 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/merge_blocks_reduction_opportunity.h" |
| |
| #include "source/opt/block_merge_util.h" |
| #include "source/opt/ir_context.h" |
| |
| namespace spvtools { |
| namespace reduce { |
| |
| MergeBlocksReductionOpportunity::MergeBlocksReductionOpportunity( |
| opt::IRContext* context, opt::Function* function, opt::BasicBlock* block) { |
| // Precondition: the terminator has to be OpBranch. |
| assert(block->terminator()->opcode() == spv::Op::OpBranch); |
| context_ = context; |
| function_ = function; |
| // Get the successor block associated with the OpBranch. |
| successor_block_ = |
| context->cfg()->block(block->terminator()->GetSingleWordInOperand(0)); |
| } |
| |
| bool MergeBlocksReductionOpportunity::PreconditionHolds() { |
| // Merge block opportunities can disable each other. |
| // Example: Given blocks: A->B->C. |
| // A is a loop header; B and C are blocks in the loop; C ends with OpReturn. |
| // There are two opportunities: B and C can be merged with their predecessors. |
| // Merge C. B now ends with OpReturn. We now just have: A->B. |
| // Merge B is now disabled, as this would lead to A, a loop header, ending |
| // with an OpReturn, which is invalid. |
| |
| const auto predecessors = context_->cfg()->preds(successor_block_->id()); |
| assert(1 == predecessors.size() && |
| "For a successor to be merged into its predecessor, exactly one " |
| "predecessor must be present."); |
| const uint32_t predecessor_id = predecessors[0]; |
| opt::BasicBlock* predecessor_block = |
| context_->get_instr_block(predecessor_id); |
| return opt::blockmergeutil::CanMergeWithSuccessor(context_, |
| predecessor_block); |
| } |
| |
| void MergeBlocksReductionOpportunity::Apply() { |
| // While the original block that targeted the successor may not exist anymore |
| // (it might have been merged with another block), some block must exist that |
| // targets the successor. Find it. |
| |
| const auto predecessors = context_->cfg()->preds(successor_block_->id()); |
| assert(1 == predecessors.size() && |
| "For a successor to be merged into its predecessor, exactly one " |
| "predecessor must be present."); |
| const uint32_t predecessor_id = predecessors[0]; |
| |
| // We need an iterator pointing to the predecessor, hence the loop. |
| for (auto bi = function_->begin(); bi != function_->end(); ++bi) { |
| if (bi->id() == predecessor_id) { |
| opt::blockmergeutil::MergeWithSuccessor(context_, function_, bi); |
| // Block merging changes the control flow graph, so invalidate it. |
| context_->InvalidateAnalysesExceptFor( |
| opt::IRContext::Analysis::kAnalysisNone); |
| return; |
| } |
| } |
| |
| assert(false && |
| "Unreachable: we should have found a block with the desired id."); |
| } |
| |
| } // namespace reduce |
| } // namespace spvtools |