| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733 |
- // Copyright (c) 2018 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/opt/loop_fusion.h"
- #include <algorithm>
- #include <vector>
- #include "source/opt/ir_context.h"
- #include "source/opt/loop_dependence.h"
- #include "source/opt/loop_descriptor.h"
- namespace spvtools {
- namespace opt {
- namespace {
- // Append all the loops nested in |loop| to |loops|.
- void CollectChildren(Loop* loop, std::vector<const Loop*>* loops) {
- for (auto child : *loop) {
- loops->push_back(child);
- if (child->NumImmediateChildren() != 0) {
- CollectChildren(child, loops);
- }
- }
- }
- // Return the set of locations accessed by |stores| and |loads|.
- std::set<Instruction*> GetLocationsAccessed(
- const std::map<Instruction*, std::vector<Instruction*>>& stores,
- const std::map<Instruction*, std::vector<Instruction*>>& loads) {
- std::set<Instruction*> locations{};
- for (const auto& kv : stores) {
- locations.insert(std::get<0>(kv));
- }
- for (const auto& kv : loads) {
- locations.insert(std::get<0>(kv));
- }
- return locations;
- }
- // Append all dependences from |sources| to |destinations| to |dependences|.
- void GetDependences(std::vector<DistanceVector>* dependences,
- LoopDependenceAnalysis* analysis,
- const std::vector<Instruction*>& sources,
- const std::vector<Instruction*>& destinations,
- size_t num_entries) {
- for (auto source : sources) {
- for (auto destination : destinations) {
- DistanceVector dist(num_entries);
- if (!analysis->GetDependence(source, destination, &dist)) {
- dependences->push_back(dist);
- }
- }
- }
- }
- // Apped all instructions in |block| to |instructions|.
- void AddInstructionsInBlock(std::vector<Instruction*>* instructions,
- BasicBlock* block) {
- for (auto& inst : *block) {
- instructions->push_back(&inst);
- }
- instructions->push_back(block->GetLabelInst());
- }
- } // namespace
- bool LoopFusion::UsedInContinueOrConditionBlock(Instruction* phi_instruction,
- Loop* loop) {
- auto condition_block = loop->FindConditionBlock()->id();
- auto continue_block = loop->GetContinueBlock()->id();
- auto not_used = context_->get_def_use_mgr()->WhileEachUser(
- phi_instruction,
- [this, condition_block, continue_block](Instruction* instruction) {
- auto block_id = context_->get_instr_block(instruction)->id();
- return block_id != condition_block && block_id != continue_block;
- });
- return !not_used;
- }
- void LoopFusion::RemoveIfNotUsedContinueOrConditionBlock(
- std::vector<Instruction*>* instructions, Loop* loop) {
- instructions->erase(
- std::remove_if(std::begin(*instructions), std::end(*instructions),
- [this, loop](Instruction* instruction) {
- return !UsedInContinueOrConditionBlock(instruction,
- loop);
- }),
- std::end(*instructions));
- }
- bool LoopFusion::AreCompatible() {
- // Check that the loops are in the same function.
- if (loop_0_->GetHeaderBlock()->GetParent() !=
- loop_1_->GetHeaderBlock()->GetParent()) {
- return false;
- }
- // Check that both loops have pre-header blocks.
- if (!loop_0_->GetPreHeaderBlock() || !loop_1_->GetPreHeaderBlock()) {
- return false;
- }
- // Check there are no breaks.
- if (context_->cfg()->preds(loop_0_->GetMergeBlock()->id()).size() != 1 ||
- context_->cfg()->preds(loop_1_->GetMergeBlock()->id()).size() != 1) {
- return false;
- }
- // Check there are no continues.
- if (context_->cfg()->preds(loop_0_->GetContinueBlock()->id()).size() != 1 ||
- context_->cfg()->preds(loop_1_->GetContinueBlock()->id()).size() != 1) {
- return false;
- }
- // |GetInductionVariables| returns all OpPhi in the header. Check that both
- // loops have exactly one that is used in the continue and condition blocks.
- std::vector<Instruction*> inductions_0{}, inductions_1{};
- loop_0_->GetInductionVariables(inductions_0);
- RemoveIfNotUsedContinueOrConditionBlock(&inductions_0, loop_0_);
- if (inductions_0.size() != 1) {
- return false;
- }
- induction_0_ = inductions_0.front();
- loop_1_->GetInductionVariables(inductions_1);
- RemoveIfNotUsedContinueOrConditionBlock(&inductions_1, loop_1_);
- if (inductions_1.size() != 1) {
- return false;
- }
- induction_1_ = inductions_1.front();
- if (!CheckInit()) {
- return false;
- }
- if (!CheckCondition()) {
- return false;
- }
- if (!CheckStep()) {
- return false;
- }
- // Check adjacency, |loop_0_| should come just before |loop_1_|.
- // There is always at least one block between loops, even if it's empty.
- // We'll check at most 2 preceding blocks.
- auto pre_header_1 = loop_1_->GetPreHeaderBlock();
- std::vector<BasicBlock*> block_to_check{};
- block_to_check.push_back(pre_header_1);
- if (loop_0_->GetMergeBlock() != loop_1_->GetPreHeaderBlock()) {
- // Follow CFG for one more block.
- auto preds = context_->cfg()->preds(pre_header_1->id());
- if (preds.size() == 1) {
- auto block = &*containing_function_->FindBlock(preds.front());
- if (block == loop_0_->GetMergeBlock()) {
- block_to_check.push_back(block);
- } else {
- return false;
- }
- } else {
- return false;
- }
- }
- // Check that the separating blocks are either empty or only contains a store
- // to a local variable that is never read (left behind by
- // '--eliminate-local-multi-store'). Also allow OpPhi, since the loop could be
- // in LCSSA form.
- for (auto block : block_to_check) {
- for (auto& inst : *block) {
- if (inst.opcode() == spv::Op::OpStore) {
- // Get the definition of the target to check it's function scope so
- // there are no observable side effects.
- auto variable =
- context_->get_def_use_mgr()->GetDef(inst.GetSingleWordInOperand(0));
- if (variable->opcode() != spv::Op::OpVariable ||
- spv::StorageClass(variable->GetSingleWordInOperand(0)) !=
- spv::StorageClass::Function) {
- return false;
- }
- // Check the target is never loaded.
- auto is_used = false;
- context_->get_def_use_mgr()->ForEachUse(
- inst.GetSingleWordInOperand(0),
- [&is_used](Instruction* use_inst, uint32_t) {
- if (use_inst->opcode() == spv::Op::OpLoad) {
- is_used = true;
- }
- });
- if (is_used) {
- return false;
- }
- } else if (inst.opcode() == spv::Op::OpPhi) {
- if (inst.NumInOperands() != 2) {
- return false;
- }
- } else if (inst.opcode() != spv::Op::OpBranch) {
- return false;
- }
- }
- }
- return true;
- } // namespace opt
- bool LoopFusion::ContainsBarriersOrFunctionCalls(Loop* loop) {
- for (const auto& block : loop->GetBlocks()) {
- for (const auto& inst : *containing_function_->FindBlock(block)) {
- auto opcode = inst.opcode();
- if (opcode == spv::Op::OpFunctionCall ||
- opcode == spv::Op::OpControlBarrier ||
- opcode == spv::Op::OpMemoryBarrier ||
- opcode == spv::Op::OpTypeNamedBarrier ||
- opcode == spv::Op::OpNamedBarrierInitialize ||
- opcode == spv::Op::OpMemoryNamedBarrier) {
- return true;
- }
- }
- }
- return false;
- }
- bool LoopFusion::CheckInit() {
- int64_t loop_0_init;
- if (!loop_0_->GetInductionInitValue(induction_0_, &loop_0_init)) {
- return false;
- }
- int64_t loop_1_init;
- if (!loop_1_->GetInductionInitValue(induction_1_, &loop_1_init)) {
- return false;
- }
- if (loop_0_init != loop_1_init) {
- return false;
- }
- return true;
- }
- bool LoopFusion::CheckCondition() {
- auto condition_0 = loop_0_->GetConditionInst();
- auto condition_1 = loop_1_->GetConditionInst();
- if (!loop_0_->IsSupportedCondition(condition_0->opcode()) ||
- !loop_1_->IsSupportedCondition(condition_1->opcode())) {
- return false;
- }
- if (condition_0->opcode() != condition_1->opcode()) {
- return false;
- }
- for (uint32_t i = 0; i < condition_0->NumInOperandWords(); ++i) {
- auto arg_0 = context_->get_def_use_mgr()->GetDef(
- condition_0->GetSingleWordInOperand(i));
- auto arg_1 = context_->get_def_use_mgr()->GetDef(
- condition_1->GetSingleWordInOperand(i));
- if (arg_0 == induction_0_ && arg_1 == induction_1_) {
- continue;
- }
- if (arg_0 == induction_0_ && arg_1 != induction_1_) {
- return false;
- }
- if (arg_1 == induction_1_ && arg_0 != induction_0_) {
- return false;
- }
- if (arg_0 != arg_1) {
- return false;
- }
- }
- return true;
- }
- bool LoopFusion::CheckStep() {
- auto scalar_analysis = context_->GetScalarEvolutionAnalysis();
- SENode* induction_node_0 = scalar_analysis->SimplifyExpression(
- scalar_analysis->AnalyzeInstruction(induction_0_));
- if (!induction_node_0->AsSERecurrentNode()) {
- return false;
- }
- SENode* induction_step_0 =
- induction_node_0->AsSERecurrentNode()->GetCoefficient();
- if (!induction_step_0->AsSEConstantNode()) {
- return false;
- }
- SENode* induction_node_1 = scalar_analysis->SimplifyExpression(
- scalar_analysis->AnalyzeInstruction(induction_1_));
- if (!induction_node_1->AsSERecurrentNode()) {
- return false;
- }
- SENode* induction_step_1 =
- induction_node_1->AsSERecurrentNode()->GetCoefficient();
- if (!induction_step_1->AsSEConstantNode()) {
- return false;
- }
- if (*induction_step_0 != *induction_step_1) {
- return false;
- }
- return true;
- }
- std::map<Instruction*, std::vector<Instruction*>> LoopFusion::LocationToMemOps(
- const std::vector<Instruction*>& mem_ops) {
- std::map<Instruction*, std::vector<Instruction*>> location_map{};
- for (auto instruction : mem_ops) {
- auto access_location = context_->get_def_use_mgr()->GetDef(
- instruction->GetSingleWordInOperand(0));
- while (access_location->opcode() == spv::Op::OpAccessChain) {
- access_location = context_->get_def_use_mgr()->GetDef(
- access_location->GetSingleWordInOperand(0));
- }
- location_map[access_location].push_back(instruction);
- }
- return location_map;
- }
- std::pair<std::vector<Instruction*>, std::vector<Instruction*>>
- LoopFusion::GetLoadsAndStoresInLoop(Loop* loop) {
- std::vector<Instruction*> loads{};
- std::vector<Instruction*> stores{};
- for (auto block_id : loop->GetBlocks()) {
- if (block_id == loop->GetContinueBlock()->id()) {
- continue;
- }
- for (auto& instruction : *containing_function_->FindBlock(block_id)) {
- if (instruction.opcode() == spv::Op::OpLoad) {
- loads.push_back(&instruction);
- } else if (instruction.opcode() == spv::Op::OpStore) {
- stores.push_back(&instruction);
- }
- }
- }
- return std::make_pair(loads, stores);
- }
- bool LoopFusion::IsUsedInLoop(Instruction* instruction, Loop* loop) {
- auto not_used = context_->get_def_use_mgr()->WhileEachUser(
- instruction, [this, loop](Instruction* user) {
- auto block_id = context_->get_instr_block(user)->id();
- return !loop->IsInsideLoop(block_id);
- });
- return !not_used;
- }
- bool LoopFusion::IsLegal() {
- assert(AreCompatible() && "Fusion can't be legal, loops are not compatible.");
- // Bail out if there are function calls as they could have side-effects that
- // cause dependencies or if there are any barriers.
- if (ContainsBarriersOrFunctionCalls(loop_0_) ||
- ContainsBarriersOrFunctionCalls(loop_1_)) {
- return false;
- }
- std::vector<Instruction*> phi_instructions{};
- loop_0_->GetInductionVariables(phi_instructions);
- // Check no OpPhi in |loop_0_| is used in |loop_1_|.
- for (auto phi_instruction : phi_instructions) {
- if (IsUsedInLoop(phi_instruction, loop_1_)) {
- return false;
- }
- }
- // Check no LCSSA OpPhi in merge block of |loop_0_| is used in |loop_1_|.
- auto phi_used = false;
- loop_0_->GetMergeBlock()->ForEachPhiInst(
- [this, &phi_used](Instruction* phi_instruction) {
- phi_used |= IsUsedInLoop(phi_instruction, loop_1_);
- });
- if (phi_used) {
- return false;
- }
- // Grab loads & stores from both loops.
- auto loads_stores_0 = GetLoadsAndStoresInLoop(loop_0_);
- auto loads_stores_1 = GetLoadsAndStoresInLoop(loop_1_);
- // Build memory location to operation maps.
- auto load_locs_0 = LocationToMemOps(std::get<0>(loads_stores_0));
- auto store_locs_0 = LocationToMemOps(std::get<1>(loads_stores_0));
- auto load_locs_1 = LocationToMemOps(std::get<0>(loads_stores_1));
- auto store_locs_1 = LocationToMemOps(std::get<1>(loads_stores_1));
- // Get the locations accessed in both loops.
- auto locations_0 = GetLocationsAccessed(store_locs_0, load_locs_0);
- auto locations_1 = GetLocationsAccessed(store_locs_1, load_locs_1);
- std::vector<Instruction*> potential_clashes{};
- std::set_intersection(std::begin(locations_0), std::end(locations_0),
- std::begin(locations_1), std::end(locations_1),
- std::back_inserter(potential_clashes));
- // If the loops don't access the same variables, the fusion is legal.
- if (potential_clashes.empty()) {
- return true;
- }
- // Find variables that have at least one store.
- std::vector<Instruction*> potential_clashes_with_stores{};
- for (auto location : potential_clashes) {
- if (store_locs_0.find(location) != std::end(store_locs_0) ||
- store_locs_1.find(location) != std::end(store_locs_1)) {
- potential_clashes_with_stores.push_back(location);
- }
- }
- // If there are only loads to the same variables, the fusion is legal.
- if (potential_clashes_with_stores.empty()) {
- return true;
- }
- // Else if loads and at least one store (across loops) to the same variable
- // there is a potential dependence and we need to check the dependence
- // distance.
- // Find all the loops in this loop nest for the dependency analysis.
- std::vector<const Loop*> loops{};
- // Find the parents.
- for (auto current_loop = loop_0_; current_loop != nullptr;
- current_loop = current_loop->GetParent()) {
- loops.push_back(current_loop);
- }
- auto this_loop_position = loops.size() - 1;
- std::reverse(std::begin(loops), std::end(loops));
- // Find the children.
- CollectChildren(loop_0_, &loops);
- CollectChildren(loop_1_, &loops);
- // Check that any dependes created are legal. That means the fused loops do
- // not have any dependencies with dependence distance greater than 0 that did
- // not exist in the original loops.
- LoopDependenceAnalysis analysis(context_, loops);
- analysis.GetScalarEvolution()->AddLoopsToPretendAreTheSame(
- {loop_0_, loop_1_});
- for (auto location : potential_clashes_with_stores) {
- // Analyse dependences from |loop_0_| to |loop_1_|.
- std::vector<DistanceVector> dependences;
- // Read-After-Write.
- GetDependences(&dependences, &analysis, store_locs_0[location],
- load_locs_1[location], loops.size());
- // Write-After-Read.
- GetDependences(&dependences, &analysis, load_locs_0[location],
- store_locs_1[location], loops.size());
- // Write-After-Write.
- GetDependences(&dependences, &analysis, store_locs_0[location],
- store_locs_1[location], loops.size());
- // Check that the induction variables either don't appear in the subscripts
- // or the dependence distance is negative.
- for (const auto& dependence : dependences) {
- const auto& entry = dependence.GetEntries()[this_loop_position];
- if ((entry.dependence_information ==
- DistanceEntry::DependenceInformation::DISTANCE &&
- entry.distance < 1) ||
- (entry.dependence_information ==
- DistanceEntry::DependenceInformation::IRRELEVANT)) {
- continue;
- } else {
- return false;
- }
- }
- }
- return true;
- }
- void ReplacePhiParentWith(Instruction* inst, uint32_t orig_block,
- uint32_t new_block) {
- if (inst->GetSingleWordInOperand(1) == orig_block) {
- inst->SetInOperand(1, {new_block});
- } else {
- inst->SetInOperand(3, {new_block});
- }
- }
- void LoopFusion::Fuse() {
- assert(AreCompatible() && "Can't fuse, loops aren't compatible");
- assert(IsLegal() && "Can't fuse, illegal");
- // Save the pointers/ids, won't be found in the middle of doing modifications.
- auto header_1 = loop_1_->GetHeaderBlock()->id();
- auto condition_1 = loop_1_->FindConditionBlock()->id();
- auto continue_1 = loop_1_->GetContinueBlock()->id();
- auto continue_0 = loop_0_->GetContinueBlock()->id();
- auto condition_block_of_0 = loop_0_->FindConditionBlock();
- // Find the blocks whose branches need updating.
- auto first_block_of_1 = &*(++containing_function_->FindBlock(condition_1));
- auto last_block_of_1 = &*(--containing_function_->FindBlock(continue_1));
- auto last_block_of_0 = &*(--containing_function_->FindBlock(continue_0));
- // Update the branch for |last_block_of_loop_0| to go to |first_block_of_1|.
- last_block_of_0->ForEachSuccessorLabel(
- [first_block_of_1](uint32_t* succ) { *succ = first_block_of_1->id(); });
- // Update the branch for the |last_block_of_loop_1| to go to the continue
- // block of |loop_0_|.
- last_block_of_1->ForEachSuccessorLabel(
- [this](uint32_t* succ) { *succ = loop_0_->GetContinueBlock()->id(); });
- // Update merge block id in the header of |loop_0_| to the merge block of
- // |loop_1_|.
- loop_0_->GetHeaderBlock()->ForEachInst([this](Instruction* inst) {
- if (inst->opcode() == spv::Op::OpLoopMerge) {
- inst->SetInOperand(0, {loop_1_->GetMergeBlock()->id()});
- }
- });
- // Update condition branch target in |loop_0_| to the merge block of
- // |loop_1_|.
- condition_block_of_0->ForEachInst([this](Instruction* inst) {
- if (inst->opcode() == spv::Op::OpBranchConditional) {
- auto loop_0_merge_block_id = loop_0_->GetMergeBlock()->id();
- if (inst->GetSingleWordInOperand(1) == loop_0_merge_block_id) {
- inst->SetInOperand(1, {loop_1_->GetMergeBlock()->id()});
- } else {
- inst->SetInOperand(2, {loop_1_->GetMergeBlock()->id()});
- }
- }
- });
- // Move OpPhi instructions not corresponding to the induction variable from
- // the header of |loop_1_| to the header of |loop_0_|.
- std::vector<Instruction*> instructions_to_move{};
- for (auto& instruction : *loop_1_->GetHeaderBlock()) {
- if (instruction.opcode() == spv::Op::OpPhi &&
- &instruction != induction_1_) {
- instructions_to_move.push_back(&instruction);
- }
- }
- for (auto& it : instructions_to_move) {
- it->RemoveFromList();
- it->InsertBefore(induction_0_);
- }
- // Update the OpPhi parents to the correct blocks in |loop_0_|.
- loop_0_->GetHeaderBlock()->ForEachPhiInst([this](Instruction* i) {
- ReplacePhiParentWith(i, loop_1_->GetPreHeaderBlock()->id(),
- loop_0_->GetPreHeaderBlock()->id());
- ReplacePhiParentWith(i, loop_1_->GetContinueBlock()->id(),
- loop_0_->GetContinueBlock()->id());
- });
- // Update instruction to block mapping & DefUseManager.
- for (auto& phi_instruction : instructions_to_move) {
- context_->set_instr_block(phi_instruction, loop_0_->GetHeaderBlock());
- context_->get_def_use_mgr()->AnalyzeInstUse(phi_instruction);
- }
- // Replace the uses of the induction variable of |loop_1_| with that the
- // induction variable of |loop_0_|.
- context_->ReplaceAllUsesWith(induction_1_->result_id(),
- induction_0_->result_id());
- // Replace LCSSA OpPhi in merge block of |loop_0_|.
- loop_0_->GetMergeBlock()->ForEachPhiInst([this](Instruction* instruction) {
- context_->ReplaceAllUsesWith(instruction->result_id(),
- instruction->GetSingleWordInOperand(0));
- });
- // Update LCSSA OpPhi in merge block of |loop_1_|.
- loop_1_->GetMergeBlock()->ForEachPhiInst(
- [condition_block_of_0](Instruction* instruction) {
- instruction->SetInOperand(1, {condition_block_of_0->id()});
- });
- // Move the continue block of |loop_0_| after the last block of |loop_1_|.
- containing_function_->MoveBasicBlockToAfter(continue_0, last_block_of_1);
- // Gather all instructions to be killed from |loop_1_| (induction variable
- // initialisation, header, condition and continue blocks).
- std::vector<Instruction*> instr_to_delete{};
- AddInstructionsInBlock(&instr_to_delete, loop_1_->GetPreHeaderBlock());
- AddInstructionsInBlock(&instr_to_delete, loop_1_->GetHeaderBlock());
- AddInstructionsInBlock(&instr_to_delete, loop_1_->FindConditionBlock());
- AddInstructionsInBlock(&instr_to_delete, loop_1_->GetContinueBlock());
- // There was an additional empty block between the loops, kill that too.
- if (loop_0_->GetMergeBlock() != loop_1_->GetPreHeaderBlock()) {
- AddInstructionsInBlock(&instr_to_delete, loop_0_->GetMergeBlock());
- }
- // Update the CFG, so it wouldn't need invalidating.
- auto cfg = context_->cfg();
- cfg->ForgetBlock(loop_1_->GetPreHeaderBlock());
- cfg->ForgetBlock(loop_1_->GetHeaderBlock());
- cfg->ForgetBlock(loop_1_->FindConditionBlock());
- cfg->ForgetBlock(loop_1_->GetContinueBlock());
- if (loop_0_->GetMergeBlock() != loop_1_->GetPreHeaderBlock()) {
- cfg->ForgetBlock(loop_0_->GetMergeBlock());
- }
- cfg->RemoveEdge(last_block_of_0->id(), loop_0_->GetContinueBlock()->id());
- cfg->AddEdge(last_block_of_0->id(), first_block_of_1->id());
- cfg->AddEdge(last_block_of_1->id(), loop_0_->GetContinueBlock()->id());
- cfg->AddEdge(loop_0_->GetContinueBlock()->id(),
- loop_1_->GetHeaderBlock()->id());
- cfg->AddEdge(condition_block_of_0->id(), loop_1_->GetMergeBlock()->id());
- // Update DefUseManager.
- auto def_use_mgr = context_->get_def_use_mgr();
- // Uses of labels that are in updated branches need analysing.
- def_use_mgr->AnalyzeInstUse(last_block_of_0->terminator());
- def_use_mgr->AnalyzeInstUse(last_block_of_1->terminator());
- def_use_mgr->AnalyzeInstUse(loop_0_->GetHeaderBlock()->GetLoopMergeInst());
- def_use_mgr->AnalyzeInstUse(condition_block_of_0->terminator());
- // Update the LoopDescriptor, so it wouldn't need invalidating.
- auto ld = context_->GetLoopDescriptor(containing_function_);
- // Create a copy, so the iterator wouldn't be invalidated.
- std::vector<Loop*> loops_to_add_remove{};
- for (auto child_loop : *loop_1_) {
- loops_to_add_remove.push_back(child_loop);
- }
- for (auto child_loop : loops_to_add_remove) {
- loop_1_->RemoveChildLoop(child_loop);
- loop_0_->AddNestedLoop(child_loop);
- }
- auto loop_1_blocks = loop_1_->GetBlocks();
- for (auto block : loop_1_blocks) {
- loop_1_->RemoveBasicBlock(block);
- if (block != header_1 && block != condition_1 && block != continue_1) {
- loop_0_->AddBasicBlock(block);
- if ((*ld)[block] == loop_1_) {
- ld->SetBasicBlockToLoop(block, loop_0_);
- }
- }
- if ((*ld)[block] == loop_1_) {
- ld->ForgetBasicBlock(block);
- }
- }
- loop_1_->RemoveBasicBlock(loop_1_->GetPreHeaderBlock()->id());
- ld->ForgetBasicBlock(loop_1_->GetPreHeaderBlock()->id());
- if (loop_0_->GetMergeBlock() != loop_1_->GetPreHeaderBlock()) {
- loop_0_->RemoveBasicBlock(loop_0_->GetMergeBlock()->id());
- ld->ForgetBasicBlock(loop_0_->GetMergeBlock()->id());
- }
- loop_0_->SetMergeBlock(loop_1_->GetMergeBlock());
- loop_1_->ClearBlocks();
- ld->RemoveLoop(loop_1_);
- // Kill unnecessary instructions and remove all empty blocks.
- for (auto inst : instr_to_delete) {
- context_->KillInst(inst);
- }
- containing_function_->RemoveEmptyBlocks();
- // Invalidate analyses.
- context_->InvalidateAnalysesExceptFor(
- IRContext::Analysis::kAnalysisInstrToBlockMapping |
- IRContext::Analysis::kAnalysisLoopAnalysis |
- IRContext::Analysis::kAnalysisDefUse | IRContext::Analysis::kAnalysisCFG);
- }
- } // namespace opt
- } // namespace spvtools
|