| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029 |
- // 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 "source/opt/loop_descriptor.h"
- #include <algorithm>
- #include <iostream>
- #include <limits>
- #include <stack>
- #include <type_traits>
- #include <utility>
- #include <vector>
- #include "source/opt/cfg.h"
- #include "source/opt/constants.h"
- #include "source/opt/dominator_tree.h"
- #include "source/opt/ir_builder.h"
- #include "source/opt/ir_context.h"
- #include "source/opt/iterator.h"
- #include "source/opt/tree_iterator.h"
- #include "source/util/make_unique.h"
- namespace spvtools {
- namespace opt {
- // Takes in a phi instruction |induction| and the loop |header| and returns the
- // step operation of the loop.
- Instruction* Loop::GetInductionStepOperation(
- const Instruction* induction) const {
- // Induction must be a phi instruction.
- assert(induction->opcode() == SpvOpPhi);
- Instruction* step = nullptr;
- analysis::DefUseManager* def_use_manager = context_->get_def_use_mgr();
- // Traverse the incoming operands of the phi instruction.
- for (uint32_t operand_id = 1; operand_id < induction->NumInOperands();
- operand_id += 2) {
- // Incoming edge.
- BasicBlock* incoming_block =
- context_->cfg()->block(induction->GetSingleWordInOperand(operand_id));
- // Check if the block is dominated by header, and thus coming from within
- // the loop.
- if (IsInsideLoop(incoming_block)) {
- step = def_use_manager->GetDef(
- induction->GetSingleWordInOperand(operand_id - 1));
- break;
- }
- }
- if (!step || !IsSupportedStepOp(step->opcode())) {
- return nullptr;
- }
- // The induction variable which binds the loop must only be modified once.
- uint32_t lhs = step->GetSingleWordInOperand(0);
- uint32_t rhs = step->GetSingleWordInOperand(1);
- // One of the left hand side or right hand side of the step instruction must
- // be the induction phi and the other must be an OpConstant.
- if (lhs != induction->result_id() && rhs != induction->result_id()) {
- return nullptr;
- }
- if (def_use_manager->GetDef(lhs)->opcode() != SpvOp::SpvOpConstant &&
- def_use_manager->GetDef(rhs)->opcode() != SpvOp::SpvOpConstant) {
- return nullptr;
- }
- return step;
- }
- // Returns true if the |step| operation is an induction variable step operation
- // which is currently handled.
- bool Loop::IsSupportedStepOp(SpvOp step) const {
- switch (step) {
- case SpvOp::SpvOpISub:
- case SpvOp::SpvOpIAdd:
- return true;
- default:
- return false;
- }
- }
- bool Loop::IsSupportedCondition(SpvOp condition) const {
- switch (condition) {
- // <
- case SpvOp::SpvOpULessThan:
- case SpvOp::SpvOpSLessThan:
- // >
- case SpvOp::SpvOpUGreaterThan:
- case SpvOp::SpvOpSGreaterThan:
- // >=
- case SpvOp::SpvOpSGreaterThanEqual:
- case SpvOp::SpvOpUGreaterThanEqual:
- // <=
- case SpvOp::SpvOpSLessThanEqual:
- case SpvOp::SpvOpULessThanEqual:
- return true;
- default:
- return false;
- }
- }
- int64_t Loop::GetResidualConditionValue(SpvOp condition, int64_t initial_value,
- int64_t step_value,
- size_t number_of_iterations,
- size_t factor) {
- int64_t remainder =
- initial_value + (number_of_iterations % factor) * step_value;
- // We subtract or add one as the above formula calculates the remainder if the
- // loop where just less than or greater than. Adding or subtracting one should
- // give a functionally equivalent value.
- switch (condition) {
- case SpvOp::SpvOpSGreaterThanEqual:
- case SpvOp::SpvOpUGreaterThanEqual: {
- remainder -= 1;
- break;
- }
- case SpvOp::SpvOpSLessThanEqual:
- case SpvOp::SpvOpULessThanEqual: {
- remainder += 1;
- break;
- }
- default:
- break;
- }
- return remainder;
- }
- Instruction* Loop::GetConditionInst() const {
- BasicBlock* condition_block = FindConditionBlock();
- if (!condition_block) {
- return nullptr;
- }
- Instruction* branch_conditional = &*condition_block->tail();
- if (!branch_conditional ||
- branch_conditional->opcode() != SpvOpBranchConditional) {
- return nullptr;
- }
- Instruction* condition_inst = context_->get_def_use_mgr()->GetDef(
- branch_conditional->GetSingleWordInOperand(0));
- if (IsSupportedCondition(condition_inst->opcode())) {
- return condition_inst;
- }
- return nullptr;
- }
- // Extract the initial value from the |induction| OpPhi instruction and store it
- // in |value|. If the function couldn't find the initial value of |induction|
- // return false.
- bool Loop::GetInductionInitValue(const Instruction* induction,
- int64_t* value) const {
- Instruction* constant_instruction = nullptr;
- analysis::DefUseManager* def_use_manager = context_->get_def_use_mgr();
- for (uint32_t operand_id = 0; operand_id < induction->NumInOperands();
- operand_id += 2) {
- BasicBlock* bb = context_->cfg()->block(
- induction->GetSingleWordInOperand(operand_id + 1));
- if (!IsInsideLoop(bb)) {
- constant_instruction = def_use_manager->GetDef(
- induction->GetSingleWordInOperand(operand_id));
- }
- }
- if (!constant_instruction) return false;
- const analysis::Constant* constant =
- context_->get_constant_mgr()->FindDeclaredConstant(
- constant_instruction->result_id());
- if (!constant) return false;
- if (value) {
- const analysis::Integer* type = constant->type()->AsInteger();
- if (!type) {
- return false;
- }
- *value = type->IsSigned() ? constant->GetSignExtendedValue()
- : constant->GetZeroExtendedValue();
- }
- return true;
- }
- Loop::Loop(IRContext* context, DominatorAnalysis* dom_analysis,
- BasicBlock* header, BasicBlock* continue_target,
- BasicBlock* merge_target)
- : context_(context),
- loop_header_(header),
- loop_continue_(continue_target),
- loop_merge_(merge_target),
- loop_preheader_(nullptr),
- parent_(nullptr),
- loop_is_marked_for_removal_(false) {
- assert(context);
- assert(dom_analysis);
- loop_preheader_ = FindLoopPreheader(dom_analysis);
- loop_latch_ = FindLatchBlock();
- }
- BasicBlock* Loop::FindLoopPreheader(DominatorAnalysis* dom_analysis) {
- CFG* cfg = context_->cfg();
- DominatorTree& dom_tree = dom_analysis->GetDomTree();
- 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) {
- 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();
- const auto* const_loop_pred = loop_pred;
- const_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;
- }
- bool Loop::IsInsideLoop(Instruction* inst) const {
- const BasicBlock* parent_block = context_->get_instr_block(inst);
- if (!parent_block) return false;
- return IsInsideLoop(parent_block);
- }
- bool Loop::IsBasicBlockInLoopSlow(const BasicBlock* bb) {
- assert(bb->GetParent() && "The basic block does not belong to a function");
- DominatorAnalysis* dom_analysis =
- context_->GetDominatorAnalysis(bb->GetParent());
- if (dom_analysis->IsReachable(bb) &&
- !dom_analysis->Dominates(GetHeaderBlock(), bb))
- return false;
- return true;
- }
- BasicBlock* Loop::GetOrCreatePreHeaderBlock() {
- if (loop_preheader_) return loop_preheader_;
- CFG* cfg = context_->cfg();
- loop_header_ = cfg->SplitLoopHeader(loop_header_);
- return loop_preheader_;
- }
- void Loop::SetContinueBlock(BasicBlock* continue_block) {
- assert(IsInsideLoop(continue_block));
- loop_continue_ = continue_block;
- }
- void Loop::SetLatchBlock(BasicBlock* latch) {
- #ifndef NDEBUG
- assert(latch->GetParent() && "The basic block does not belong to a function");
- const auto* const_latch = latch;
- const_latch->ForEachSuccessorLabel([this](uint32_t id) {
- assert((!IsInsideLoop(id) || id == GetHeaderBlock()->id()) &&
- "A predecessor of the continue block does not belong to the loop");
- });
- #endif // NDEBUG
- assert(IsInsideLoop(latch) && "The continue block is not in the loop");
- SetLatchBlockImpl(latch);
- }
- void Loop::SetMergeBlock(BasicBlock* merge) {
- #ifndef NDEBUG
- assert(merge->GetParent() && "The basic block does not belong to a function");
- #endif // NDEBUG
- assert(!IsInsideLoop(merge) && "The merge block is in the loop");
- SetMergeBlockImpl(merge);
- if (GetHeaderBlock()->GetLoopMergeInst()) {
- UpdateLoopMergeInst();
- }
- }
- void Loop::SetPreHeaderBlock(BasicBlock* preheader) {
- if (preheader) {
- assert(!IsInsideLoop(preheader) && "The preheader block is in the loop");
- assert(preheader->tail()->opcode() == SpvOpBranch &&
- "The preheader block does not unconditionally branch to the header "
- "block");
- assert(preheader->tail()->GetSingleWordOperand(0) ==
- GetHeaderBlock()->id() &&
- "The preheader block does not unconditionally branch to the header "
- "block");
- }
- loop_preheader_ = preheader;
- }
- BasicBlock* Loop::FindLatchBlock() {
- CFG* cfg = context_->cfg();
- DominatorAnalysis* dominator_analysis =
- context_->GetDominatorAnalysis(loop_header_->GetParent());
- // Look at the predecessors of the loop header to find a predecessor block
- // which is dominated by the loop continue target. There should only be one
- // block which meets this criteria and this is the latch block, as per the
- // SPIR-V spec.
- for (uint32_t block_id : cfg->preds(loop_header_->id())) {
- if (dominator_analysis->Dominates(loop_continue_->id(), block_id)) {
- return cfg->block(block_id);
- }
- }
- assert(
- false &&
- "Every loop should have a latch block dominated by the continue target");
- return nullptr;
- }
- void Loop::GetExitBlocks(std::unordered_set<uint32_t>* exit_blocks) const {
- CFG* cfg = context_->cfg();
- exit_blocks->clear();
- for (uint32_t bb_id : GetBlocks()) {
- const BasicBlock* bb = cfg->block(bb_id);
- bb->ForEachSuccessorLabel([exit_blocks, this](uint32_t succ) {
- if (!IsInsideLoop(succ)) {
- exit_blocks->insert(succ);
- }
- });
- }
- }
- void Loop::GetMergingBlocks(
- std::unordered_set<uint32_t>* merging_blocks) const {
- assert(GetMergeBlock() && "This loop is not structured");
- CFG* cfg = context_->cfg();
- merging_blocks->clear();
- std::stack<const BasicBlock*> to_visit;
- to_visit.push(GetMergeBlock());
- while (!to_visit.empty()) {
- const BasicBlock* bb = to_visit.top();
- to_visit.pop();
- merging_blocks->insert(bb->id());
- for (uint32_t pred_id : cfg->preds(bb->id())) {
- if (!IsInsideLoop(pred_id) && !merging_blocks->count(pred_id)) {
- to_visit.push(cfg->block(pred_id));
- }
- }
- }
- }
- namespace {
- static inline bool IsBasicBlockSafeToClone(IRContext* context, BasicBlock* bb) {
- for (Instruction& inst : *bb) {
- if (!inst.IsBranch() && !context->IsCombinatorInstruction(&inst))
- return false;
- }
- return true;
- }
- } // namespace
- bool Loop::IsSafeToClone() const {
- CFG& cfg = *context_->cfg();
- for (uint32_t bb_id : GetBlocks()) {
- BasicBlock* bb = cfg.block(bb_id);
- assert(bb);
- if (!IsBasicBlockSafeToClone(context_, bb)) return false;
- }
- // Look at the merge construct.
- if (GetHeaderBlock()->GetLoopMergeInst()) {
- std::unordered_set<uint32_t> blocks;
- GetMergingBlocks(&blocks);
- blocks.erase(GetMergeBlock()->id());
- for (uint32_t bb_id : blocks) {
- BasicBlock* bb = cfg.block(bb_id);
- assert(bb);
- if (!IsBasicBlockSafeToClone(context_, bb)) return false;
- }
- }
- return true;
- }
- bool Loop::IsLCSSA() const {
- CFG* cfg = context_->cfg();
- analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr();
- std::unordered_set<uint32_t> exit_blocks;
- GetExitBlocks(&exit_blocks);
- // Declare ir_context so we can capture context_ in the below lambda
- IRContext* ir_context = context_;
- for (uint32_t bb_id : GetBlocks()) {
- for (Instruction& insn : *cfg->block(bb_id)) {
- // All uses must be either:
- // - In the loop;
- // - In an exit block and in a phi instruction.
- if (!def_use_mgr->WhileEachUser(
- &insn,
- [&exit_blocks, ir_context, this](Instruction* use) -> bool {
- BasicBlock* parent = ir_context->get_instr_block(use);
- assert(parent && "Invalid analysis");
- if (IsInsideLoop(parent)) return true;
- if (use->opcode() != SpvOpPhi) return false;
- return exit_blocks.count(parent->id());
- }))
- return false;
- }
- }
- return true;
- }
- bool Loop::ShouldHoistInstruction(IRContext* context, Instruction* inst) {
- return AreAllOperandsOutsideLoop(context, inst) &&
- inst->IsOpcodeCodeMotionSafe();
- }
- bool Loop::AreAllOperandsOutsideLoop(IRContext* context, Instruction* inst) {
- analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
- bool all_outside_loop = true;
- const std::function<void(uint32_t*)> operand_outside_loop =
- [this, &def_use_mgr, &all_outside_loop](uint32_t* id) {
- if (this->IsInsideLoop(def_use_mgr->GetDef(*id))) {
- all_outside_loop = false;
- return;
- }
- };
- inst->ForEachInId(operand_outside_loop);
- return all_outside_loop;
- }
- void Loop::ComputeLoopStructuredOrder(
- std::vector<BasicBlock*>* ordered_loop_blocks, bool include_pre_header,
- bool include_merge) const {
- CFG& cfg = *context_->cfg();
- // Reserve the memory: all blocks in the loop + extra if needed.
- ordered_loop_blocks->reserve(GetBlocks().size() + include_pre_header +
- include_merge);
- if (include_pre_header && GetPreHeaderBlock())
- ordered_loop_blocks->push_back(loop_preheader_);
- bool is_shader =
- context_->get_feature_mgr()->HasCapability(SpvCapabilityShader);
- if (!is_shader) {
- cfg.ForEachBlockInReversePostOrder(
- loop_header_, [ordered_loop_blocks, this](BasicBlock* bb) {
- if (IsInsideLoop(bb)) ordered_loop_blocks->push_back(bb);
- });
- } else {
- // If this is a shader, it is possible that there are unreachable merge and
- // continue blocks that must be copied to retain the structured order.
- // The structured order will include these.
- std::list<BasicBlock*> order;
- cfg.ComputeStructuredOrder(loop_header_->GetParent(), loop_header_, &order);
- for (BasicBlock* bb : order) {
- if (bb == GetMergeBlock()) {
- break;
- }
- ordered_loop_blocks->push_back(bb);
- }
- }
- if (include_merge && GetMergeBlock())
- ordered_loop_blocks->push_back(loop_merge_);
- }
- LoopDescriptor::LoopDescriptor(IRContext* context, const Function* f)
- : loops_(), placeholder_top_loop_(nullptr) {
- PopulateList(context, f);
- }
- LoopDescriptor::~LoopDescriptor() { ClearLoops(); }
- void LoopDescriptor::PopulateList(IRContext* context, const Function* f) {
- DominatorAnalysis* dom_analysis = context->GetDominatorAnalysis(f);
- ClearLoops();
- // Post-order traversal of the dominator tree to find all the OpLoopMerge
- // instructions.
- DominatorTree& dom_tree = dom_analysis->GetDomTree();
- for (DominatorTreeNode& node :
- make_range(dom_tree.post_begin(), dom_tree.post_end())) {
- Instruction* merge_inst = node.bb_->GetLoopMergeInst();
- if (merge_inst) {
- bool all_backedge_unreachable = true;
- for (uint32_t pid : context->cfg()->preds(node.bb_->id())) {
- if (dom_analysis->IsReachable(pid) &&
- dom_analysis->Dominates(node.bb_->id(), pid)) {
- all_backedge_unreachable = false;
- break;
- }
- }
- if (all_backedge_unreachable)
- continue; // ignore this one, we actually never branch back.
- // 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.
- Loop* current_loop =
- new Loop(context, dom_analysis, header_bb, continue_bb, merge_bb);
- loops_.push_back(current_loop);
- // 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;
- // 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);
- }
- DominatorTreeNode* dom_merge_node = dom_tree.GetTreeNode(merge_bb);
- for (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->AddBasicBlock(loop_node.bb_);
- basic_block_to_loop_.insert(
- std::make_pair(loop_node.bb_->id(), current_loop));
- }
- }
- }
- for (Loop* loop : loops_) {
- if (!loop->HasParent()) placeholder_top_loop_.nested_loops_.push_back(loop);
- }
- }
- std::vector<Loop*> LoopDescriptor::GetLoopsInBinaryLayoutOrder() {
- std::vector<uint32_t> ids{};
- for (size_t i = 0; i < NumLoops(); ++i) {
- ids.push_back(GetLoopByIndex(i).GetHeaderBlock()->id());
- }
- std::vector<Loop*> loops{};
- if (!ids.empty()) {
- auto function = GetLoopByIndex(0).GetHeaderBlock()->GetParent();
- for (const auto& block : *function) {
- auto block_id = block.id();
- auto element = std::find(std::begin(ids), std::end(ids), block_id);
- if (element != std::end(ids)) {
- loops.push_back(&GetLoopByIndex(element - std::begin(ids)));
- }
- }
- }
- return loops;
- }
- BasicBlock* Loop::FindConditionBlock() const {
- if (!loop_merge_) {
- return nullptr;
- }
- BasicBlock* condition_block = nullptr;
- uint32_t in_loop_pred = 0;
- for (uint32_t p : context_->cfg()->preds(loop_merge_->id())) {
- if (IsInsideLoop(p)) {
- if (in_loop_pred) {
- // 2 in-loop predecessors.
- return nullptr;
- }
- in_loop_pred = p;
- }
- }
- if (!in_loop_pred) {
- // Merge block is unreachable.
- return nullptr;
- }
- BasicBlock* bb = context_->cfg()->block(in_loop_pred);
- if (!bb) return nullptr;
- const Instruction& branch = *bb->ctail();
- // Make sure the branch is a conditional branch.
- if (branch.opcode() != SpvOpBranchConditional) return nullptr;
- // Make sure one of the two possible branches is to the merge block.
- if (branch.GetSingleWordInOperand(1) == loop_merge_->id() ||
- branch.GetSingleWordInOperand(2) == loop_merge_->id()) {
- condition_block = bb;
- }
- return condition_block;
- }
- bool Loop::FindNumberOfIterations(const Instruction* induction,
- const Instruction* branch_inst,
- size_t* iterations_out,
- int64_t* step_value_out,
- int64_t* init_value_out) const {
- // From the branch instruction find the branch condition.
- analysis::DefUseManager* def_use_manager = context_->get_def_use_mgr();
- // Condition instruction from the OpConditionalBranch.
- Instruction* condition =
- def_use_manager->GetDef(branch_inst->GetSingleWordOperand(0));
- assert(IsSupportedCondition(condition->opcode()));
- // Get the constant manager from the ir context.
- analysis::ConstantManager* const_manager = context_->get_constant_mgr();
- // Find the constant value used by the condition variable. Exit out if it
- // isn't a constant int.
- const analysis::Constant* upper_bound =
- const_manager->FindDeclaredConstant(condition->GetSingleWordOperand(3));
- if (!upper_bound) return false;
- // Must be integer because of the opcode on the condition.
- const analysis::Integer* type = upper_bound->type()->AsInteger();
- if (!type || type->width() > 64) {
- return false;
- }
- int64_t condition_value = type->IsSigned()
- ? upper_bound->GetSignExtendedValue()
- : upper_bound->GetZeroExtendedValue();
- // Find the instruction which is stepping through the loop.
- //
- // GetInductionStepOperation returns nullptr if |step_inst| is OpConstantNull.
- Instruction* step_inst = GetInductionStepOperation(induction);
- if (!step_inst) return false;
- // Find the constant value used by the condition variable.
- const analysis::Constant* step_constant =
- const_manager->FindDeclaredConstant(step_inst->GetSingleWordOperand(3));
- if (!step_constant) return false;
- // Must be integer because of the opcode on the condition.
- int64_t step_value = 0;
- const analysis::Integer* step_type =
- step_constant->AsIntConstant()->type()->AsInteger();
- if (step_type->IsSigned()) {
- step_value = step_constant->AsIntConstant()->GetS32BitValue();
- } else {
- step_value = step_constant->AsIntConstant()->GetU32BitValue();
- }
- // If this is a subtraction step we should negate the step value.
- if (step_inst->opcode() == SpvOp::SpvOpISub) {
- step_value = -step_value;
- }
- // Find the initial value of the loop and make sure it is a constant integer.
- int64_t init_value = 0;
- if (!GetInductionInitValue(induction, &init_value)) return false;
- // If iterations is non null then store the value in that.
- int64_t num_itrs = GetIterations(condition->opcode(), condition_value,
- init_value, step_value);
- // If the loop body will not be reached return false.
- if (num_itrs <= 0) {
- return false;
- }
- if (iterations_out) {
- assert(static_cast<size_t>(num_itrs) <= std::numeric_limits<size_t>::max());
- *iterations_out = static_cast<size_t>(num_itrs);
- }
- if (step_value_out) {
- *step_value_out = step_value;
- }
- if (init_value_out) {
- *init_value_out = init_value;
- }
- return true;
- }
- // We retrieve the number of iterations using the following formula, diff /
- // |step_value| where diff is calculated differently according to the
- // |condition| and uses the |condition_value| and |init_value|. If diff /
- // |step_value| is NOT cleanly divisible then we add one to the sum.
- int64_t Loop::GetIterations(SpvOp condition, int64_t condition_value,
- int64_t init_value, int64_t step_value) const {
- if (step_value == 0) {
- return 0;
- }
- int64_t diff = 0;
- switch (condition) {
- case SpvOp::SpvOpSLessThan:
- case SpvOp::SpvOpULessThan: {
- // If the condition is not met to begin with the loop will never iterate.
- if (!(init_value < condition_value)) return 0;
- diff = condition_value - init_value;
- // If the operation is a less then operation then the diff and step must
- // have the same sign otherwise the induction will never cross the
- // condition (either never true or always true).
- if ((diff < 0 && step_value > 0) || (diff > 0 && step_value < 0)) {
- return 0;
- }
- break;
- }
- case SpvOp::SpvOpSGreaterThan:
- case SpvOp::SpvOpUGreaterThan: {
- // If the condition is not met to begin with the loop will never iterate.
- if (!(init_value > condition_value)) return 0;
- diff = init_value - condition_value;
- // If the operation is a greater than operation then the diff and step
- // must have opposite signs. Otherwise the condition will always be true
- // or will never be true.
- if ((diff < 0 && step_value < 0) || (diff > 0 && step_value > 0)) {
- return 0;
- }
- break;
- }
- case SpvOp::SpvOpSGreaterThanEqual:
- case SpvOp::SpvOpUGreaterThanEqual: {
- // If the condition is not met to begin with the loop will never iterate.
- if (!(init_value >= condition_value)) return 0;
- // We subtract one to make it the same as SpvOpGreaterThan as it is
- // functionally equivalent.
- diff = init_value - (condition_value - 1);
- // If the operation is a greater than operation then the diff and step
- // must have opposite signs. Otherwise the condition will always be true
- // or will never be true.
- if ((diff > 0 && step_value > 0) || (diff < 0 && step_value < 0)) {
- return 0;
- }
- break;
- }
- case SpvOp::SpvOpSLessThanEqual:
- case SpvOp::SpvOpULessThanEqual: {
- // If the condition is not met to begin with the loop will never iterate.
- if (!(init_value <= condition_value)) return 0;
- // We add one to make it the same as SpvOpLessThan as it is functionally
- // equivalent.
- diff = (condition_value + 1) - init_value;
- // If the operation is a less than operation then the diff and step must
- // have the same sign otherwise the induction will never cross the
- // condition (either never true or always true).
- if ((diff < 0 && step_value > 0) || (diff > 0 && step_value < 0)) {
- return 0;
- }
- break;
- }
- default:
- assert(false &&
- "Could not retrieve number of iterations from the loop condition. "
- "Condition is not supported.");
- }
- // Take the abs of - step values.
- step_value = llabs(step_value);
- diff = llabs(diff);
- int64_t result = diff / step_value;
- if (diff % step_value != 0) {
- result += 1;
- }
- return result;
- }
- // Returns the list of induction variables within the loop.
- void Loop::GetInductionVariables(
- std::vector<Instruction*>& induction_variables) const {
- for (Instruction& inst : *loop_header_) {
- if (inst.opcode() == SpvOp::SpvOpPhi) {
- induction_variables.push_back(&inst);
- }
- }
- }
- Instruction* Loop::FindConditionVariable(
- const BasicBlock* condition_block) const {
- // Find the branch instruction.
- const Instruction& branch_inst = *condition_block->ctail();
- Instruction* induction = nullptr;
- // Verify that the branch instruction is a conditional branch.
- if (branch_inst.opcode() == SpvOp::SpvOpBranchConditional) {
- // From the branch instruction find the branch condition.
- analysis::DefUseManager* def_use_manager = context_->get_def_use_mgr();
- // Find the instruction representing the condition used in the conditional
- // branch.
- Instruction* condition =
- def_use_manager->GetDef(branch_inst.GetSingleWordOperand(0));
- // Ensure that the condition is a less than operation.
- if (condition && IsSupportedCondition(condition->opcode())) {
- // The left hand side operand of the operation.
- Instruction* variable_inst =
- def_use_manager->GetDef(condition->GetSingleWordOperand(2));
- // Make sure the variable instruction used is a phi.
- if (!variable_inst || variable_inst->opcode() != SpvOpPhi) return nullptr;
- // Make sure the phi instruction only has two incoming blocks. Each
- // incoming block will be represented by two in operands in the phi
- // instruction, the value and the block which that value came from. We
- // assume the cannocalised phi will have two incoming values, one from the
- // preheader and one from the continue block.
- size_t max_supported_operands = 4;
- if (variable_inst->NumInOperands() == max_supported_operands) {
- // The operand index of the first incoming block label.
- uint32_t operand_label_1 = 1;
- // The operand index of the second incoming block label.
- uint32_t operand_label_2 = 3;
- // Make sure one of them is the preheader.
- if (!IsInsideLoop(
- variable_inst->GetSingleWordInOperand(operand_label_1)) &&
- !IsInsideLoop(
- variable_inst->GetSingleWordInOperand(operand_label_2))) {
- return nullptr;
- }
- // And make sure that the other is the latch block.
- if (variable_inst->GetSingleWordInOperand(operand_label_1) !=
- loop_latch_->id() &&
- variable_inst->GetSingleWordInOperand(operand_label_2) !=
- loop_latch_->id()) {
- return nullptr;
- }
- } else {
- return nullptr;
- }
- if (!FindNumberOfIterations(variable_inst, &branch_inst, nullptr))
- return nullptr;
- induction = variable_inst;
- }
- }
- return induction;
- }
- bool LoopDescriptor::CreatePreHeaderBlocksIfMissing() {
- auto modified = false;
- for (auto& loop : *this) {
- if (!loop.GetPreHeaderBlock()) {
- modified = true;
- // TODO(1841): Handle failure to create pre-header.
- loop.GetOrCreatePreHeaderBlock();
- }
- }
- return modified;
- }
- // Add and remove loops which have been marked for addition and removal to
- // maintain the state of the loop descriptor class.
- void LoopDescriptor::PostModificationCleanup() {
- LoopContainerType loops_to_remove_;
- for (Loop* loop : loops_) {
- if (loop->IsMarkedForRemoval()) {
- loops_to_remove_.push_back(loop);
- if (loop->HasParent()) {
- loop->GetParent()->RemoveChildLoop(loop);
- }
- }
- }
- for (Loop* loop : loops_to_remove_) {
- loops_.erase(std::find(loops_.begin(), loops_.end(), loop));
- delete loop;
- }
- for (auto& pair : loops_to_add_) {
- Loop* parent = pair.first;
- std::unique_ptr<Loop> loop = std::move(pair.second);
- if (parent) {
- loop->SetParent(nullptr);
- parent->AddNestedLoop(loop.get());
- for (uint32_t block_id : loop->GetBlocks()) {
- parent->AddBasicBlock(block_id);
- }
- }
- loops_.emplace_back(loop.release());
- }
- loops_to_add_.clear();
- }
- void LoopDescriptor::ClearLoops() {
- for (Loop* loop : loops_) {
- delete loop;
- }
- loops_.clear();
- }
- // Adds a new loop nest to the descriptor set.
- Loop* LoopDescriptor::AddLoopNest(std::unique_ptr<Loop> new_loop) {
- Loop* loop = new_loop.release();
- if (!loop->HasParent()) placeholder_top_loop_.nested_loops_.push_back(loop);
- // Iterate from inner to outer most loop, adding basic block to loop mapping
- // as we go.
- for (Loop& current_loop :
- make_range(iterator::begin(loop), iterator::end(nullptr))) {
- loops_.push_back(¤t_loop);
- for (uint32_t bb_id : current_loop.GetBlocks())
- basic_block_to_loop_.insert(std::make_pair(bb_id, ¤t_loop));
- }
- return loop;
- }
- void LoopDescriptor::RemoveLoop(Loop* loop) {
- Loop* parent = loop->GetParent() ? loop->GetParent() : &placeholder_top_loop_;
- parent->nested_loops_.erase(std::find(parent->nested_loops_.begin(),
- parent->nested_loops_.end(), loop));
- std::for_each(
- loop->nested_loops_.begin(), loop->nested_loops_.end(),
- [loop](Loop* sub_loop) { sub_loop->SetParent(loop->GetParent()); });
- parent->nested_loops_.insert(parent->nested_loops_.end(),
- loop->nested_loops_.begin(),
- loop->nested_loops_.end());
- for (uint32_t bb_id : loop->GetBlocks()) {
- Loop* l = FindLoopForBasicBlock(bb_id);
- if (l == loop) {
- SetBasicBlockToLoop(bb_id, l->GetParent());
- } else {
- ForgetBasicBlock(bb_id);
- }
- }
- LoopContainerType::iterator it =
- std::find(loops_.begin(), loops_.end(), loop);
- assert(it != loops_.end());
- delete loop;
- loops_.erase(it);
- }
- } // namespace opt
- } // namespace spvtools
|