| // Copyright (c) 2017 Google Inc. |
| // |
| // 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 "opt/loop_descriptor.h" |
| #include <iostream> |
| #include <type_traits> |
| #include <utility> |
| #include <vector> |
| |
| #include "opt/iterator.h" |
| #include "opt/loop_descriptor.h" |
| #include "opt/make_unique.h" |
| #include "opt/tree_iterator.h" |
| |
| namespace spvtools { |
| namespace ir { |
| |
| Loop::Loop(IRContext* context, opt::DominatorAnalysis* dom_analysis, |
| BasicBlock* header, BasicBlock* continue_target, |
| BasicBlock* merge_target) |
| : loop_header_(header), |
| loop_continue_(continue_target), |
| loop_merge_(merge_target), |
| loop_preheader_(nullptr), |
| parent_(nullptr) { |
| assert(context); |
| assert(dom_analysis); |
| loop_preheader_ = FindLoopPreheader(context, dom_analysis); |
| AddBasicBlockToLoop(header); |
| AddBasicBlockToLoop(continue_target); |
| } |
| |
| BasicBlock* Loop::FindLoopPreheader(IRContext* ir_context, |
| opt::DominatorAnalysis* dom_analysis) { |
| CFG* cfg = ir_context->cfg(); |
| opt::DominatorTree& dom_tree = dom_analysis->GetDomTree(); |
| opt::DominatorTreeNode* header_node = dom_tree.GetTreeNode(loop_header_); |
| |
| // The loop predecessor. |
| BasicBlock* loop_pred = nullptr; |
| |
| auto header_pred = cfg->preds(loop_header_->id()); |
| for (uint32_t p_id : header_pred) { |
| opt::DominatorTreeNode* node = dom_tree.GetTreeNode(p_id); |
| if (node && !dom_tree.Dominates(header_node, node)) { |
| // The predecessor is not part of the loop, so potential loop preheader. |
| if (loop_pred && node->bb_ != loop_pred) { |
| // If we saw 2 distinct predecessors that are outside the loop, we don't |
| // have a loop preheader. |
| return nullptr; |
| } |
| loop_pred = node->bb_; |
| } |
| } |
| // Safe guard against invalid code, SPIR-V spec forbids loop with the entry |
| // node as header. |
| assert(loop_pred && "The header node is the entry block ?"); |
| |
| // So we have a unique basic block that can enter this loop. |
| // If this loop is the unique successor of this block, then it is a loop |
| // preheader. |
| bool is_preheader = true; |
| uint32_t loop_header_id = loop_header_->id(); |
| loop_pred->ForEachSuccessorLabel( |
| [&is_preheader, loop_header_id](const uint32_t id) { |
| if (id != loop_header_id) is_preheader = false; |
| }); |
| if (is_preheader) return loop_pred; |
| return nullptr; |
| } |
| |
| LoopDescriptor::LoopDescriptor(const Function* f) { PopulateList(f); } |
| |
| void LoopDescriptor::PopulateList(const Function* f) { |
| IRContext* context = f->GetParent()->context(); |
| |
| opt::DominatorAnalysis* dom_analysis = |
| context->GetDominatorAnalysis(f, *context->cfg()); |
| |
| loops_.clear(); |
| |
| // Post-order traversal of the dominator tree to find all the OpLoopMerge |
| // instructions. |
| opt::DominatorTree& dom_tree = dom_analysis->GetDomTree(); |
| for (opt::DominatorTreeNode& node : |
| ir::make_range(dom_tree.post_begin(), dom_tree.post_end())) { |
| Instruction* merge_inst = node.bb_->GetLoopMergeInst(); |
| if (merge_inst) { |
| // The id of the merge basic block of this loop. |
| uint32_t merge_bb_id = merge_inst->GetSingleWordOperand(0); |
| |
| // The id of the continue basic block of this loop. |
| uint32_t continue_bb_id = merge_inst->GetSingleWordOperand(1); |
| |
| // The merge target of this loop. |
| BasicBlock* merge_bb = context->cfg()->block(merge_bb_id); |
| |
| // The continue target of this loop. |
| BasicBlock* continue_bb = context->cfg()->block(continue_bb_id); |
| |
| // The basic block containing the merge instruction. |
| BasicBlock* header_bb = context->get_instr_block(merge_inst); |
| |
| // Add the loop to the list of all the loops in the function. |
| loops_.emplace_back(MakeUnique<Loop>(context, dom_analysis, header_bb, |
| continue_bb, merge_bb)); |
| Loop* current_loop = loops_.back().get(); |
| |
| // We have a bottom-up construction, so if this loop has nested-loops, |
| // they are by construction at the tail of the loop list. |
| for (auto itr = loops_.rbegin() + 1; itr != loops_.rend(); ++itr) { |
| Loop* previous_loop = itr->get(); |
| |
| // If the loop already has a parent, then it has been processed. |
| if (previous_loop->HasParent()) continue; |
| |
| // If the current loop does not dominates the previous loop then it is |
| // not nested loop. |
| if (!dom_analysis->Dominates(header_bb, |
| previous_loop->GetHeaderBlock())) |
| continue; |
| // If the current loop merge dominates the previous loop then it is |
| // not nested loop. |
| if (dom_analysis->Dominates(merge_bb, previous_loop->GetHeaderBlock())) |
| continue; |
| |
| current_loop->AddNestedLoop(previous_loop); |
| } |
| opt::DominatorTreeNode* dom_merge_node = dom_tree.GetTreeNode(merge_bb); |
| for (opt::DominatorTreeNode& loop_node : |
| make_range(node.df_begin(), node.df_end())) { |
| // Check if we are in the loop. |
| if (dom_tree.Dominates(dom_merge_node, &loop_node)) continue; |
| current_loop->AddBasicBlockToLoop(loop_node.bb_); |
| basic_block_to_loop_.insert( |
| std::make_pair(loop_node.bb_->id(), current_loop)); |
| } |
| } |
| } |
| for (std::unique_ptr<Loop>& loop : loops_) { |
| if (!loop->HasParent()) dummy_top_loop_.nested_loops_.push_back(loop.get()); |
| } |
| } |
| |
| } // namespace ir |
| } // namespace spvtools |