/** * Copyright (c) 2021-2022 Huawei Device Co., Ltd. * 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 "basicblock.h" #include "graph.h" #include "inst.h" #include "optimizer/analysis/loop_analyzer.h" #include "optimizer/analysis/dominators_tree.h" namespace panda::compiler { class Inst; BasicBlock::BasicBlock(Graph *graph, uint32_t guest_pc) : graph_(graph), preds_(graph_->GetAllocator()->Adapter()), succs_(graph_->GetAllocator()->Adapter()), dom_blocks_(graph_->GetAllocator()->Adapter()), guest_pc_(guest_pc) { } bool BasicBlock::IsStartBlock() const { return (graph_->GetStartBlock() == this); } bool BasicBlock::IsEndBlock() const { return (graph_->GetEndBlock() == this); } bool BasicBlock::IsPseudoControlFlowBlock() const { return IsStartBlock() || IsEndBlock() || IsTryBegin() || IsTryEnd(); } bool BasicBlock::IsLoopHeader() const { ASSERT(GetLoop() != nullptr); return (GetLoop()->GetHeader() == this); } BasicBlock *BasicBlock::SplitBlockAfterInstruction(Inst *inst, bool make_edge) { ASSERT(inst != nullptr); ASSERT(inst->GetBasicBlock() == this); ASSERT(!IsStartBlock() && !IsEndBlock()); auto next_inst = inst->GetNext(); auto new_bb = GetGraph()->CreateEmptyBlock((next_inst != nullptr) ? next_inst->GetPc() : INVALID_PC); new_bb->SetAllFields(this->GetAllFields()); GetLoop()->AppendBlock(new_bb); for (; next_inst != nullptr; next_inst = next_inst->GetNext()) { new_bb->AppendInst(next_inst); } inst->SetNext(nullptr); last_inst_ = inst; if (inst->IsPhi()) { first_inst_ = nullptr; } for (auto succ : GetSuccsBlocks()) { succ->ReplacePred(this, new_bb); } GetSuccsBlocks().clear(); ASSERT(GetSuccsBlocks().empty()); if (make_edge) { AddSucc(new_bb); } return new_bb; } void BasicBlock::AddSucc(BasicBlock *succ, bool can_add_empty_block) { auto it = std::find(succs_.begin(), succs_.end(), succ); ASSERT_PRINT(it == succs_.end() || can_add_empty_block, "Uncovered case where empty block needed to fix CFG"); if (it != succs_.end() && can_add_empty_block) { // If edge already exists we create empty block on it auto empty_bb = GetGraph()->CreateEmptyBlock(GetGuestPc()); ReplaceSucc(succ, empty_bb); succ->ReplacePred(this, empty_bb); } succs_.push_back(succ); succ->GetPredsBlocks().push_back(this); } void BasicBlock::ReplaceSucc(const BasicBlock *prev_succ, BasicBlock *new_succ, bool can_add_empty_block) { auto it = std::find(succs_.begin(), succs_.end(), new_succ); ASSERT_PRINT(it == succs_.end() || can_add_empty_block, "Uncovered case where empty block needed to fix CFG"); if (it != succs_.end() && can_add_empty_block) { // If edge already exists we create empty block on it auto empty_bb = GetGraph()->CreateEmptyBlock(GetGuestPc()); ReplaceSucc(new_succ, empty_bb); new_succ->ReplacePred(this, empty_bb); } succs_[GetSuccBlockIndex(prev_succ)] = new_succ; new_succ->preds_.push_back(this); } BasicBlock *BasicBlock::InsertNewBlockToSuccEdge(BasicBlock *succ) { auto block = GetGraph()->CreateEmptyBlock(succ->GetGuestPc()); this->ReplaceSucc(succ, block); succ->ReplacePred(this, block); return block; } BasicBlock *BasicBlock::InsertEmptyBlockBefore() { auto block = GetGraph()->CreateEmptyBlock(this->GetGuestPc()); for (auto pred : preds_) { pred->ReplaceSucc(this, block); this->RemovePred(pred); } block->AddSucc(this); return block; } void BasicBlock::InsertBlockBeforeSucc(BasicBlock *block, BasicBlock *succ) { this->ReplaceSucc(succ, block); succ->ReplacePred(this, block); } static void RemovePhiProcessing(BasicBlock *bb, BasicBlock *succ) { size_t num_preds = bb->GetPredsBlocks().size(); for (auto phi : succ->PhiInsts()) { auto index = phi->CastToPhi()->GetPredBlockIndex(bb); auto inst = phi->GetInput(index).GetInst(); if (inst->GetBasicBlock() == bb) { // When INST is from empty basic block ... ASSERT(inst->IsPhi()); // ... we have to copy it's inputs into corresponding inputs of PHI auto pred_bb = bb->GetPredBlockByIndex(0); phi->SetInput(index, inst->CastToPhi()->GetPhiInput(pred_bb)); for (size_t i = 1; i < num_preds; i++) { pred_bb = bb->GetPredBlockByIndex(i); phi->AppendInput(inst->CastToPhi()->GetPhiInput(pred_bb)); } } else { // otherwise, just copy inputs for new arrived predecessors for (size_t i = 1; i < num_preds; i++) { phi->AppendInput(inst); } } } // And now we should remove Phis from the empty block for (auto phi : bb->PhiInstsSafe()) { bb->RemoveInst(phi); } } // Remove empty block with one successor, may have more than one predecessors and Phi(s) void BasicBlock::RemoveEmptyBlock(bool irr_loop) { ASSERT(GetFirstInst() == nullptr); ASSERT(!GetPredsBlocks().empty()); ASSERT(GetSuccsBlocks().size() == 1); auto succ = succs_[0]; // Save old amount of predecessors in successor block size_t succ_preds_num = succ->GetPredsBlocks().size(); size_t num_preds = preds_.size(); // If empty block had more than one predecessors if (num_preds > 1) { if (succ_preds_num > 1) { // We have to process Phi instructions in successor block in a special way RemovePhiProcessing(this, succ); } else { // successor didn't have other predecessors, we are moving Phi(s) into successor ASSERT(!succ->HasPhi()); for (auto phi : PhiInstsSafe()) { succ->AppendPhi(phi); } first_phi_ = nullptr; last_inst_ = nullptr; } } // Set successor for all predecessor blocks, at the same time in successor we replace one predecessor // and add others to the end (if there were many of them in empty block) auto pred = preds_[0]; pred->succs_[pred->GetSuccBlockIndex(this)] = succ; succ->preds_[succ->GetPredBlockIndex(this)] = pred; for (size_t i = 1; i < num_preds; ++i) { pred = preds_[i]; pred->succs_[pred->GetSuccBlockIndex(this)] = succ; succ->preds_.push_back(pred); } ASSERT(GetLastInst() == nullptr); ASSERT(GetLoop()->IsIrreducible() == irr_loop); // N.B. info about Irreducible loop can not be fixed on the fly if (!irr_loop) { RemoveFixLoopInfo(); } // Finally clean lists preds_.clear(); succs_.clear(); } static void FixLoopInfoHelper(BasicBlock *bb) { ASSERT(!bb->GetPredsBlocks().empty()); auto loop = bb->GetLoop(); auto first_pred = bb->GetPredBlockByIndex(0); // Do not dup back-edge if (loop->HasBackEdge(first_pred)) { loop->RemoveBackEdge(bb); } else { loop->ReplaceBackEdge(bb, first_pred); } // If empty block has more than 1 predecessor, append others to the loop back-edges' list for (size_t i = 1; i < bb->GetPredsBlocks().size(); ++i) { auto pred = bb->GetPredBlockByIndex(i); if (!loop->HasBackEdge(pred)) { loop->AppendBackEdge(pred); } } } void BasicBlock::RemoveFixLoopInfo() { auto loop = GetLoop(); ASSERT(loop != nullptr); ASSERT(!loop->IsIrreducible()); while (!loop->IsRoot()) { if (loop->HasBackEdge(this)) { FixLoopInfoHelper(this); } loop = loop->GetOuterLoop(); } if (this == GetLoop()->GetHeader()) { GetLoop()->MoveHeaderToSucc(); } GetLoop()->RemoveBlock(this); } /** * Join single successor into single predecessor. * Block must have one successor, and its successor must have one predecessor (this block). * EXAMPLE: * [1] * | * [2] * * turns into this: * [1'] */ void BasicBlock::JoinSuccessorBlock() { ASSERT(!IsStartBlock()); ASSERT(GetSuccsBlocks().size() == 1); auto succ = GetSuccessor(0); ASSERT(!succ->IsEndBlock()); ASSERT(succ->GetPredsBlocks().size() == 1); ASSERT(succ->GetPredBlockByIndex(0) == this); // moving instructions from successor ASSERT(!succ->HasPhi()); for (auto succ_inst : succ->Insts()) { AppendInst(succ_inst); } // moving successor blocks from the successor GetSuccsBlocks().clear(); for (auto succ_succ : succ->GetSuccsBlocks()) { succ_succ->ReplacePred(succ, this); } // fixing loop information // invariant: succ has one predecessor, so it cannot be a loop header auto loop = succ->GetLoop(); ASSERT(loop != nullptr); // Irreducible loop can not be fixed on the fly if (loop->IsIrreducible()) { GetGraph()->InvalidateAnalysis(); } else { // edge can have 2 successors, so it can be back-edge in 2 loops: own loop and outer loop if (loop->HasBackEdge(succ)) { loop->ReplaceBackEdge(succ, this); } if (auto outer_loop = loop->GetOuterLoop()) { if (outer_loop->HasBackEdge(succ)) { outer_loop->ReplaceBackEdge(succ, this); } } for (auto inner_loop : loop->GetInnerLoops()) { if (inner_loop->GetPreHeader() == succ) { inner_loop->SetPreHeader(this); } } loop->RemoveBlock(succ); } succ->first_inst_ = nullptr; succ->last_inst_ = nullptr; succ->GetPredsBlocks().clear(); succ->GetSuccsBlocks().clear(); this->bit_fields_ |= succ->bit_fields_; // TODO (a.popov) replace by assert if (succ->try_id_ != INVALID_ID) { this->try_id_ = succ->try_id_; } GetGraph()->RemoveEmptyBlock(succ); } void BasicBlock::SelectsFixLoopInfo(BasicBlock *select_bb, BasicBlock *other) { // invariant: 'this' block has one predecessor, so it cannot be a loop header auto loop = GetLoop(); ASSERT(loop != nullptr); // Irreducible loop can not be fixed on the fly if (loop->IsIrreducible()) { GetGraph()->InvalidateAnalysis(); } else { if (loop->HasBackEdge(this)) { loop->RemoveBackEdge(this); } for (auto inner_loop : loop->GetInnerLoops()) { if (inner_loop->GetPreHeader() == this) { inner_loop->SetPreHeader(select_bb); break; } } loop->RemoveBlock(this); } // Remove outgoing 'this->other' edge RemoveSucc(other); other->RemovePred(this); // Disconnect GetGraph()->RemoveEmptyBlock(this); } void BasicBlock::AppendPhi(Inst *inst) { ASSERT_PRINT(inst->IsPhi(), "Instruction must be phi"); inst->SetBasicBlock(this); if (first_phi_ == nullptr) { inst->SetNext(first_inst_); if (first_inst_ != nullptr) { first_inst_->SetPrev(inst); } first_phi_ = inst; if (last_inst_ == nullptr) { last_inst_ = inst; } } else { if (first_inst_ != nullptr) { Inst *prev = first_inst_->GetPrev(); ASSERT_PRINT(prev && prev->IsPhi(), "There is no phi in the block"); inst->SetPrev(prev); prev->SetNext(inst); inst->SetNext(first_inst_); first_inst_->SetPrev(inst); } else { ASSERT_PRINT(last_inst_ && last_inst_->IsPhi(), "If first_phi is defined and first_inst is undefined, last_inst must be phi"); last_inst_->SetNext(inst); inst->SetPrev(last_inst_); last_inst_ = inst; } } } template void BasicBlock::AddInst(Inst *inst) { ASSERT_PRINT(!inst->IsPhi(), "Instruction mustn't be phi"); inst->SetBasicBlock(this); if (first_inst_ == nullptr) { inst->SetPrev(last_inst_); if (last_inst_ != nullptr) { ASSERT(last_inst_->IsPhi()); last_inst_->SetNext(inst); } first_inst_ = inst; last_inst_ = inst; } else { // NOLINTNEXTLINE(readability-braces-around-statements) if constexpr (to_end) { ASSERT_PRINT(last_inst_, "Last instruction is undefined"); inst->SetPrev(last_inst_); last_inst_->SetNext(inst); last_inst_ = inst; // NOLINTNEXTLINE(readability-misleading-indentation) } else { auto first_prev = first_inst_->GetPrev(); if (first_prev != nullptr) { first_prev->SetNext(inst); } inst->SetPrev(first_prev); inst->SetNext(first_inst_); first_inst_->SetPrev(inst); first_inst_ = inst; } } } void BasicBlock::AppendRangeInst(Inst *range_first, Inst *range_last) { #ifndef NDEBUG ASSERT(range_first && range_last && range_first->IsDominate(range_last)); ASSERT(range_first->GetPrev() == nullptr); ASSERT(range_last->GetNext() == nullptr); auto inst_db = range_first; while (inst_db != range_last) { ASSERT_PRINT(!inst_db->IsPhi(), "Instruction mustn't be phi"); ASSERT_PRINT(inst_db->GetBasicBlock() == this, "Inst::SetBasicBlock() should be called beforehand"); inst_db = inst_db->GetNext(); } ASSERT_PRINT(!inst_db->IsPhi(), "Instruction mustn't be phi"); ASSERT_PRINT(inst_db->GetBasicBlock() == this, "Inst::SetBasicBlock() should be called beforehand"); #endif if (first_inst_ == nullptr) { range_first->SetPrev(last_inst_); if (last_inst_ != nullptr) { ASSERT(last_inst_->IsPhi()); last_inst_->SetNext(range_first); } first_inst_ = range_first; last_inst_ = range_last; } else { ASSERT_PRINT(last_inst_, "Last instruction is undefined"); range_first->SetPrev(last_inst_); last_inst_->SetNext(range_first); last_inst_ = range_last; } } void BasicBlock::InsertAfter(Inst *inst, Inst *after) { ASSERT(inst && after); ASSERT(inst->IsPhi() == after->IsPhi()); ASSERT(after->GetBasicBlock() == this); ASSERT(inst->GetBasicBlock() == nullptr); inst->SetBasicBlock(this); Inst *next = after->GetNext(); inst->SetPrev(after); inst->SetNext(next); after->SetNext(inst); if (next != nullptr) { next->SetPrev(inst); } else { ASSERT(after == last_inst_); last_inst_ = inst; } } void BasicBlock::InsertBefore(Inst *inst, Inst *before) { ASSERT(inst && before); ASSERT(inst->IsPhi() == before->IsPhi()); ASSERT(before->GetBasicBlock() == this); ASSERT(inst->GetBasicBlock() == nullptr); inst->SetBasicBlock(this); Inst *prev = before->GetPrev(); inst->SetPrev(prev); inst->SetNext(before); before->SetPrev(inst); if (prev != nullptr) { prev->SetNext(inst); } if (before == first_phi_) { first_phi_ = inst; } if (before == first_inst_) { first_inst_ = inst; } } void BasicBlock::InsertRangeBefore(Inst *range_first, Inst *range_last, Inst *before) { #ifndef NDEBUG ASSERT(range_first && range_last && range_first->IsDominate(range_last)); ASSERT(before && !before->IsPhi()); ASSERT(range_first->GetPrev() == nullptr); ASSERT(range_last->GetNext() == nullptr); ASSERT(before->GetBasicBlock() == this); auto inst_db = range_first; while (inst_db != range_last) { ASSERT_PRINT(!inst_db->IsPhi(), "Instruction mustn't be phi"); ASSERT_PRINT(inst_db->GetBasicBlock() == this, "Inst::SetBasicBlock() should be called beforehand"); inst_db = inst_db->GetNext(); } ASSERT_PRINT(!inst_db->IsPhi(), "Instruction mustn't be phi"); ASSERT_PRINT(inst_db->GetBasicBlock() == this, "Inst::SetBasicBlock() should be called beforehand"); #endif Inst *prev = before->GetPrev(); range_first->SetPrev(prev); range_last->SetNext(before); before->SetPrev(range_last); if (prev != nullptr) { prev->SetNext(range_first); } if (before == first_inst_) { first_inst_ = range_first; } } void BasicBlock::ReplaceInst(Inst *old_inst, Inst *new_inst) { ASSERT(old_inst && new_inst); ASSERT(old_inst->IsPhi() == new_inst->IsPhi()); ASSERT(old_inst->GetBasicBlock() == this); ASSERT(new_inst->GetBasicBlock() == nullptr); new_inst->SetBasicBlock(this); Inst *prev = old_inst->GetPrev(); Inst *next = old_inst->GetNext(); old_inst->SetBasicBlock(nullptr); if (prev != nullptr) { prev->SetNext(new_inst); } if (next != nullptr) { next->SetPrev(new_inst); } new_inst->SetPrev(prev); new_inst->SetNext(next); if (first_phi_ == old_inst) { first_phi_ = new_inst; } if (first_inst_ == old_inst) { first_inst_ = new_inst; } if (last_inst_ == old_inst) { last_inst_ = new_inst; } if (graph_->IsInstThrowable(old_inst)) { graph_->ReplaceThrowableInst(old_inst, new_inst); } } void BasicBlock::EraseInst(Inst *inst, [[maybe_unused]] bool will_be_moved) { ASSERT(will_be_moved || !GetGraph()->IsInstThrowable(inst)); Inst *prev = inst->GetPrev(); Inst *next = inst->GetNext(); ASSERT(inst->GetBasicBlock() == this); inst->SetBasicBlock(nullptr); if (prev != nullptr) { prev->SetNext(next); } if (next != nullptr) { next->SetPrev(prev); } inst->SetPrev(nullptr); inst->SetNext(nullptr); if (inst == first_phi_) { first_phi_ = (next != nullptr && next->IsPhi()) ? next : nullptr; } if (inst == first_inst_) { first_inst_ = next; } if (inst == last_inst_) { last_inst_ = prev; } } void BasicBlock::RemoveInst(Inst *inst) { inst->RemoveUsers(); ASSERT(!inst->HasUsers()); inst->RemoveInputs(); if (inst->GetOpcode() == Opcode::Constant) { graph_->RemoveConstFromList(static_cast(inst)); } if (graph_->IsInstThrowable(inst)) { graph_->RemoveThrowableInst(inst); } EraseInst(inst); } void BasicBlock::Clear() { for (auto inst : AllInstsSafeReverse()) { RemoveInst(inst); } } /* * Check if this block is dominate other */ bool BasicBlock::IsDominate(const BasicBlock *other) const { if (other == this) { return true; } ASSERT(GetGraph()->IsAnalysisValid()); BasicBlock *dom_block = other->GetDominator(); while (dom_block != nullptr) { if (dom_block == this) { return true; } // Otherwise we are in infinite loop!? ASSERT(dom_block != dom_block->GetDominator()); dom_block = dom_block->GetDominator(); } return false; } BasicBlock *BasicBlock::CreateImmediateDominator() { ASSERT(GetGraph()->IsAnalysisValid()); auto dominator = GetGraph()->CreateEmptyBlock(); GetGraph()->GetAnalysis().SetValid(true); if (GetDominator() != nullptr) { GetDominator()->RemoveDominatedBlock(this); GetDominator()->AddDominatedBlock(dominator); dominator->SetDominator(GetDominator()); } dominator->AddDominatedBlock(this); SetDominator(dominator); return dominator; } BasicBlock *BasicBlock::GetDominator() const { ASSERT(GetGraph()->IsAnalysisValid()); return dominator_; } const ArenaVector &BasicBlock::GetDominatedBlocks() const { ASSERT(GetGraph()->IsAnalysisValid()); return dom_blocks_; } PhiInstIter BasicBlock::PhiInsts() const { return PhiInstIter(*this); } InstIter BasicBlock::Insts() const { return InstIter(*this); } AllInstIter BasicBlock::AllInsts() const { return AllInstIter(*this); } InstReverseIter BasicBlock::InstsReverse() const { return InstReverseIter(*this); } PhiInstSafeIter BasicBlock::PhiInstsSafe() const { return PhiInstSafeIter(*this); } InstSafeIter BasicBlock::InstsSafe() const { return InstSafeIter(*this); } AllInstSafeIter BasicBlock::AllInstsSafe() const { return AllInstSafeIter(*this); } PhiInstSafeReverseIter BasicBlock::PhiInstsSafeReverse() const { return PhiInstSafeReverseIter(*this); } InstSafeReverseIter BasicBlock::InstsSafeReverse() const { return InstSafeReverseIter(*this); } AllInstSafeReverseIter BasicBlock::AllInstsSafeReverse() const { return AllInstSafeReverseIter(*this); } template void BasicBlock::AddInst(Inst *inst); template void BasicBlock::AddInst(Inst *inst); void BasicBlock::InsertBlockBefore(BasicBlock *block) { for (auto pred : GetPredsBlocks()) { pred->ReplaceSucc(this, block); } GetPredsBlocks().clear(); block->AddSucc(this); } BasicBlock *BasicBlock::Clone(Graph *target_graph) const { BasicBlock *clone = nullptr; #ifndef NDEBUG if (GetGraph() == target_graph) { clone = target_graph->CreateEmptyBlock(); } else { clone = target_graph->CreateEmptyBlock(GetId(), GetGuestPc()); } #else clone = target_graph->CreateEmptyBlock(); #endif clone->SetAllFields(this->GetAllFields()); clone->try_id_ = GetTryId(); if (this->IsStartBlock()) { target_graph->SetStartBlock(clone); } else if (this->IsEndBlock()) { target_graph->SetEndBlock(clone); } return clone; } Inst *BasicBlock::GetFistThrowableInst() const { if (!IsTry()) { return nullptr; } for (auto inst : AllInsts()) { if (GetGraph()->IsInstThrowable(inst)) { return inst; } } return nullptr; } void BasicBlock::InvalidateLoopIfIrreducible() { auto loop = GetLoop(); ASSERT(loop != nullptr); if (IsLoopHeader() && loop->IsIrreducible()) { GetGraph()->InvalidateAnalysis(); } } bool BlocksPathDfsSearch(Marker marker, BasicBlock *block, const BasicBlock *target_block, const BasicBlock *exclude_block) { ASSERT(marker != UNDEF_MARKER); if (block == target_block) { return true; } block->SetMarker(marker); for (auto succ_block : block->GetSuccsBlocks()) { if (!succ_block->IsMarked(marker) && succ_block != exclude_block) { if (BlocksPathDfsSearch(marker, succ_block, target_block, exclude_block)) { return true; } } } return false; } } // namespace panda::compiler