blob: e1bb0c714fac8669bf5310ba44d21c0015f6fac7 [file] [log] [blame]
// 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