1/** 2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#include "graph.h" 17#include "basicblock.h" 18#include "optimizer/analysis/dominators_tree.h" 19#include "optimizer/analysis/rpo.h" 20#include "optimizer/analysis/linear_order.h" 21#include "optimizer/analysis/loop_analyzer.h" 22 23namespace panda::compiler { 24static void MarkBlocksRec(Marker mrk, BasicBlock *block) 25{ 26 if (block->SetMarker(mrk)) { 27 return; 28 } 29 for (auto succ : block->GetSuccsBlocks()) { 30 MarkBlocksRec(mrk, succ); 31 } 32} 33 34Graph::~Graph() 35{ 36} 37 38void Graph::RemoveUnreachableBlocks() 39{ 40 Marker mrk = NewMarker(); 41 MarkBlocksRec(mrk, GetStartBlock()); 42 // Remove unreachable blocks 43 for (auto &bb : *this) { 44 if (bb == nullptr) { 45 continue; 46 } 47 if (!bb->IsMarked(mrk)) { 48 RemovePredecessors(bb, false); 49 RemoveSuccessors(bb); 50 if (bb->IsTryBegin()) { 51 EraseTryBeginBlock(bb); 52 // Remove try_end mark from paired bb 53 if (!bb->IsEmpty()) { 54 GetTryBeginInst(bb)->GetTryEndBlock()->SetTryEnd(false); 55 } 56 } 57 // Clear DF: 58 for (auto inst : bb->AllInsts()) { 59 inst->RemoveInputs(); 60 if (IsInstThrowable(inst)) { 61 RemoveThrowableInst(inst); 62 } 63 } 64 EraseBlock(bb); 65 } 66 } 67 EraseMarker(mrk); 68} 69 70void Graph::AddConstInStartBlock(ConstantInst *const_inst) 71{ 72 GetStartBlock()->AppendInst(const_inst); 73} 74 75ParameterInst *Graph::AddNewParameter(uint16_t arg_number) 76{ 77 ParameterInst *param = CreateInstParameter(arg_number); 78 GetStartBlock()->AppendInst(param); 79 return param; 80} 81 82void Graph::RemoveConstFromList(ConstantInst *const_inst) 83{ 84 if (const_inst == first_const_inst_) { 85 first_const_inst_ = const_inst->GetNextConst(); 86 const_inst->SetNextConst(nullptr); 87 return; 88 } 89 auto current = first_const_inst_; 90 auto next = current->GetNextConst(); 91 while (next != nullptr && next != const_inst) { 92 current = next; 93 next = next->GetNextConst(); 94 } 95 ASSERT(next != nullptr); 96 ASSERT(next == const_inst); 97 current->SetNextConst(const_inst->GetNextConst()); 98 const_inst->SetNextConst(nullptr); 99} 100 101void InvalidateBlocksOrderAnalyzes(Graph *graph) 102{ 103 graph->InvalidateAnalysis<Rpo>(); 104 graph->InvalidateAnalysis<DominatorsTree>(); 105 graph->InvalidateAnalysis<LinearOrder>(); 106} 107 108void Graph::AddBlock(BasicBlock *block) 109{ 110 block->SetId(vector_bb_.size()); 111 vector_bb_.push_back(block); 112 block->SetGraph(this); 113 InvalidateBlocksOrderAnalyzes(this); 114} 115 116#ifndef NDEBUG 117void Graph::AddBlock(BasicBlock *block, uint32_t id) 118{ 119 if (vector_bb_.size() <= id) { 120 // (id + 1) for adding a block with index 0 121 vector_bb_.resize((id + 1U) << 1U, nullptr); 122 } 123 ASSERT(vector_bb_[id] == nullptr); 124 block->SetId(id); 125 vector_bb_[id] = block; 126 InvalidateBlocksOrderAnalyzes(this); 127} 128#endif 129 130const ArenaVector<BasicBlock *> &Graph::GetBlocksRPO() const 131{ 132 return GetValidAnalysis<Rpo>().GetBlocks(); 133} 134 135const ArenaVector<BasicBlock *> &Graph::GetBlocksLinearOrder() const 136{ 137 return GetValidAnalysis<LinearOrder>().GetBlocks(); 138} 139 140template <class Callback> 141void Graph::VisitAllInstructions(Callback callback) 142{ 143 for (auto bb : GetBlocksRPO()) { 144 for (auto inst : bb->AllInsts()) { 145 callback(inst); 146 } 147 } 148} 149 150BasicBlock *Graph::CreateEmptyBlock(uint32_t guest_pc) 151{ 152 auto block = GetAllocator()->New<BasicBlock>(this, guest_pc); 153 CHECK_NOT_NULL(block); 154 AddBlock(block); 155 return block; 156} 157 158// Create empty block with base block's properties 159BasicBlock *Graph::CreateEmptyBlock(BasicBlock *base_block) 160{ 161 ASSERT(base_block != nullptr); 162 auto block = CreateEmptyBlock(); 163 block->SetGuestPc(base_block->GetGuestPc()); 164 block->SetAllFields(base_block->GetAllFields()); 165 block->SetTryId(base_block->GetTryId()); 166 return block; 167} 168 169#ifndef NDEBUG 170BasicBlock *Graph::CreateEmptyBlock(uint32_t id, uint32_t guest_pc) 171{ 172 auto block = GetAllocator()->New<BasicBlock>(this, guest_pc); 173 CHECK_NOT_NULL(block); 174 AddBlock(block, id); 175 return block; 176} 177#endif 178 179BasicBlock *Graph::CreateStartBlock() 180{ 181 auto block = CreateEmptyBlock(0U); 182 SetStartBlock(block); 183 return block; 184} 185 186BasicBlock *Graph::CreateEndBlock(uint32_t guest_pc) 187{ 188 auto block = CreateEmptyBlock(guest_pc); 189 SetEndBlock(block); 190 return block; 191} 192 193void RemovePredecessorUpdateDF(BasicBlock *block, BasicBlock *rm_pred) 194{ 195 constexpr auto IMM_2 = 2; 196 if (block->GetPredsBlocks().size() == IMM_2) { 197 for (auto phi : block->PhiInstsSafe()) { 198 auto rm_index = phi->CastToPhi()->GetPredBlockIndex(rm_pred); 199 auto remaining_inst = phi->GetInput(1 - rm_index).GetInst(); 200 if (phi != remaining_inst && remaining_inst->GetBasicBlock() != nullptr) { 201 phi->ReplaceUsers(remaining_inst); 202 } 203 block->RemoveInst(phi); 204 } 205 } else if (block->GetPredsBlocks().size() > IMM_2) { 206 for (auto phi : block->PhiInstsSafe()) { 207 auto rm_index = phi->CastToPhi()->GetPredBlockIndex(rm_pred); 208 phi->CastToPhi()->RemoveInput(rm_index); 209 } 210 } else { 211 ASSERT(block->GetPredsBlocks().size() == 1); 212 } 213 block->RemovePred(rm_pred); 214 InvalidateBlocksOrderAnalyzes(block->GetGraph()); 215} 216 217/* 218 * Remove edges between `block` and its successors and 219 * update phi-instructions in successors blocks 220 */ 221void Graph::RemoveSuccessors(BasicBlock *block) 222{ 223 for (auto succ : block->GetSuccsBlocks()) { 224 RemovePredecessorUpdateDF(succ, block); 225 } 226 block->GetSuccsBlocks().clear(); 227} 228 229/* 230 * Remove edges between `block` and its predecessors, 231 * update last instructions in predecessors blocks 232 */ 233void Graph::RemovePredecessors(BasicBlock *block, bool remove_last_inst) 234{ 235 for (auto pred : block->GetPredsBlocks()) { 236 if (remove_last_inst && !pred->IsTryBegin() && !pred->IsTryEnd()) { 237 if (pred->GetSuccsBlocks().size() == 2U) { 238 auto last = pred->GetLastInst(); 239 ASSERT(last->GetOpcode() == Opcode::If || last->GetOpcode() == Opcode::IfImm); 240 pred->RemoveInst(last); 241 } else { 242 ASSERT(pred->GetSuccsBlocks().size() == 1 && pred->GetSuccessor(0) == block); 243 } 244 } 245 if (std::find(pred->GetSuccsBlocks().begin(), pred->GetSuccsBlocks().end(), block) != 246 pred->GetSuccsBlocks().end()) { 247 pred->RemoveSucc(block); 248 } 249 } 250 block->GetPredsBlocks().clear(); 251} 252 253// Helper for the next 2 methods 254static void FinishBlockRemoval(BasicBlock *block) 255{ 256 auto graph = block->GetGraph(); 257 graph->GetAnalysis<DominatorsTree>().SetValid(true); 258 auto dominator = block->GetDominator(); 259 if (dominator != nullptr) { 260 dominator->RemoveDominatedBlock(block); 261 for (auto dom_block : block->GetDominatedBlocks()) { 262 ASSERT(dom_block->GetDominator() == block); 263 dominator->AddDominatedBlock(dom_block); 264 dom_block->SetDominator(dominator); 265 } 266 } 267 block->SetDominator(nullptr); 268 269 block->SetGraph(nullptr); 270 if (graph->GetAnalysis<Rpo>().IsValid()) { 271 graph->GetAnalysis<Rpo>().RemoveBasicBlock(block); 272 } 273} 274 275/** 276 * @param block - a block which is disconnecting from the graph with clearing control-flow and data-flow 277 */ 278void Graph::DisconnectBlock(BasicBlock *block, bool remove_last_inst, bool fix_dom_tree) 279{ 280 ASSERT(IsAnalysisValid<DominatorsTree>() || !fix_dom_tree); 281 RemovePredecessors(block, remove_last_inst); 282 RemoveSuccessors(block); 283 284 if (block->IsTryBegin()) { 285 EraseTryBeginBlock(block); 286 } 287 288 // Remove all instructions from `block` 289 block->Clear(); 290 291 if (block->IsEndBlock()) { 292 SetEndBlock(nullptr); 293 } 294 if (fix_dom_tree) { 295 FinishBlockRemoval(block); 296 } 297 EraseBlock(block); 298 // NB! please do not forget to fix LoopAnalyzer or invalidate it after the end of the pass 299} 300 301void Graph::DisconnectBlockRec(BasicBlock *block, bool remove_last_inst, bool fix_dom_tree) 302{ 303 if (block->GetGraph() == nullptr) { 304 return; 305 } 306 bool loop_flag = false; 307 if (block->IsLoopHeader()) { 308 loop_flag = true; 309 auto loop = block->GetLoop(); 310 for (auto pred : block->GetPredsBlocks()) { 311 loop_flag &= (std::find(loop->GetBackEdges().begin(), loop->GetBackEdges().end(), pred) != 312 loop->GetBackEdges().end()); 313 } 314 } 315 if (block->GetPredsBlocks().empty() || loop_flag) { 316 ArenaVector<BasicBlock *> succs(block->GetSuccsBlocks(), GetLocalAllocator()->Adapter()); 317 DisconnectBlock(block, remove_last_inst, fix_dom_tree); 318 for (auto succ : succs) { 319 DisconnectBlockRec(succ, remove_last_inst, fix_dom_tree); 320 } 321 } 322} 323 324void Graph::EraseBlock(BasicBlock *block) 325{ 326 vector_bb_[block->GetId()] = nullptr; 327 if (GetEndBlock() == block) { 328 SetEndBlock(nullptr); 329 } 330 ASSERT(GetStartBlock() != block); 331 block->SetGraph(nullptr); 332} 333 334void Graph::RestoreBlock(BasicBlock *block) 335{ 336 ASSERT(vector_bb_[block->GetId()] == nullptr); 337 vector_bb_[block->GetId()] = block; 338 block->SetGraph(this); 339 InvalidateBlocksOrderAnalyzes(this); 340} 341 342/** 343 * @param block - same for block without instructions at all 344 */ 345void Graph::RemoveEmptyBlock(BasicBlock *block) 346{ 347 ASSERT(IsAnalysisValid<DominatorsTree>()); 348 ASSERT(block->GetLastInst() == nullptr); 349 ASSERT(block->GetPredsBlocks().empty()); 350 ASSERT(block->GetSuccsBlocks().empty()); 351 352 FinishBlockRemoval(block); 353 EraseBlock(block); 354 // NB! please do not forget to fix LoopAnalyzer or invalidate it after the end of the pass 355} 356 357/** 358 * @param block - same for block without instructions, may have Phi(s) 359 */ 360void Graph::RemoveEmptyBlockWithPhis(BasicBlock *block, bool irr_loop) 361{ 362 ASSERT(IsAnalysisValid<DominatorsTree>()); 363 ASSERT(block->IsEmpty()); 364 365 ASSERT(!block->GetSuccsBlocks().empty()); 366 ASSERT(!block->GetPredsBlocks().empty()); 367 block->RemoveEmptyBlock(irr_loop); 368 369 FinishBlockRemoval(block); 370 EraseBlock(block); 371} 372 373ConstantInst *Graph::FindConstant(DataType::Type type, uint64_t value) 374{ 375 for (auto constant = GetFirstConstInst(); constant != nullptr; constant = constant->GetNextConst()) { 376 if (constant->GetType() != type) { 377 continue; 378 } 379 if (IsBytecodeOptimizer() && IsInt32Bit(type) && (constant->GetInt32Value() == static_cast<uint32_t>(value))) { 380 return constant; 381 } 382 if (constant->IsEqualConst(type, value)) { 383 return constant; 384 } 385 } 386 return nullptr; 387} 388 389ConstantInst *Graph::FindOrAddConstant(ConstantInst *inst) 390{ 391 auto existing_const = FindConstant(inst->GetType(), inst->GetRawValue()); 392 if (existing_const != nullptr) { 393 return existing_const; 394 } 395 AddConstInStartBlock(inst); 396 inst->SetNextConst(first_const_inst_); 397 first_const_inst_ = inst; 398 return inst; 399} 400 401void Graph::ResetParameterInfo() 402{ 403 param_info_ = nullptr; 404 return; 405} 406 407bool Graph::HasLoop() const 408{ 409 ASSERT(GetAnalysis<LoopAnalyzer>().IsValid()); 410 return !GetRootLoop()->GetInnerLoops().empty(); 411} 412 413bool Graph::HasIrreducibleLoop() const 414{ 415 ASSERT(GetAnalysis<LoopAnalyzer>().IsValid()); 416 return FlagIrredicibleLoop::Get(bit_fields_); 417} 418 419bool Graph::HasInfiniteLoop() const 420{ 421 ASSERT(GetAnalysis<LoopAnalyzer>().IsValid()); 422 return FlagInfiniteLoop::Get(bit_fields_); 423} 424 425bool Graph::HasFloatRegs() const 426{ 427 ASSERT(IsRegAllocApplied()); 428 return FlagFloatRegs::Get(bit_fields_); 429} 430 431/* 432 * Mark blocks, which have successor from external loop 433 */ 434void MarkLoopExits(const Graph *graph, Marker marker) 435{ 436 for (auto block : graph->GetBlocksRPO()) { 437 if (block->GetSuccsBlocks().size() == MAX_SUCCS_NUM) { 438 if (block->GetSuccessor(0)->GetLoop() != block->GetSuccessor(1)->GetLoop()) { 439 block->SetMarker(marker); 440 } 441 } else if (block->GetSuccsBlocks().size() > MAX_SUCCS_NUM) { 442 ASSERT(block->IsTryEnd() || block->IsTryBegin()); 443 auto loop = block->GetSuccessor(0)->GetLoop(); 444 for (size_t i = 1; i < block->GetSuccsBlocks().size(); i++) { 445 if (loop != block->GetSuccessor(i)->GetLoop()) { 446 block->SetMarker(marker); 447 } 448 } 449 } 450 } 451} 452 453std::string GetMethodFullName(const Graph *graph, RuntimeInterface::MethodPtr method) 454{ 455 std::stringstream sstream; 456 sstream << graph->GetRuntime()->GetClassNameFromMethod(method) 457 << "::" << graph->GetRuntime()->GetMethodName(method); 458 return sstream.str(); 459} 460 461// NOLINTNEXTLINE(readability-identifier-naming,-warnings-as-errors) 462Graph::ParameterList::Iterator Graph::ParameterList::begin() 463{ 464 auto start_bb = graph_->GetStartBlock(); 465 Iterator it(start_bb->GetFirstInst()); 466 if (*it != nullptr && it->GetOpcode() != Opcode::Parameter) { 467 ++it; 468 } 469 return it; 470} 471 472void Graph::RemoveThrowableInst(const Inst *inst) 473{ 474 ASSERT(IsInstThrowable(inst)); 475 for (auto catch_handler : throwable_insts_.at(inst)) { 476 for (auto catch_inst : catch_handler->AllInsts()) { 477 if (!catch_inst->IsCatchPhi() || catch_inst->CastToCatchPhi()->IsAcc()) { 478 continue; 479 } 480 auto catch_phi = catch_inst->CastToCatchPhi(); 481 const auto &vregs = catch_phi->GetThrowableInsts(); 482 auto it = std::find(vregs->begin(), vregs->end(), inst); 483 if (it != vregs->end()) { 484 int index = std::distance(vregs->begin(), it); 485 catch_phi->RemoveInput(index); 486 } 487 } 488 } 489 throwable_insts_.erase(inst); 490} 491 492void Graph::ReplaceThrowableInst(Inst *old_inst, Inst *new_inst) 493{ 494 auto it = throwable_insts_.emplace(new_inst, GetAllocator()->Adapter()).first; 495 it->second = std::move(throwable_insts_.at(old_inst)); 496 497 for (auto catch_handler : it->second) { 498 for (auto catch_inst : catch_handler->AllInsts()) { 499 if (!catch_inst->IsCatchPhi() || catch_inst->CastToCatchPhi()->IsAcc()) { 500 continue; 501 } 502 auto catch_phi = catch_inst->CastToCatchPhi(); 503 const auto &vregs = catch_phi->GetThrowableInsts(); 504 auto iter = std::find(vregs->begin(), vregs->end(), old_inst); 505 if (iter != vregs->end()) { 506 catch_phi->ReplaceThrowableInst(old_inst, new_inst); 507 } 508 } 509 } 510 throwable_insts_.erase(old_inst); 511} 512 513void Graph::DumpThrowableInsts(std::ostream *out) const 514{ 515 for (auto &[inst, handlers] : throwable_insts_) { 516 (*out) << "Throwable Inst"; 517 inst->Dump(out); 518 (*out) << "Catch handlers:"; 519 auto sep = " "; 520 for (auto bb : handlers) { 521 (*out) << sep << "BB " << bb->GetId(); 522 sep = ", "; 523 } 524 (*out) << std::endl; 525 } 526} 527 528void Graph::InitDefaultLocations() 529{ 530 if (IsDefaultLocationsInit()) { 531 return; 532 } 533 VisitAllInstructions([this](Inst *inst) { 534 if (!inst->IsOperandsDynamic() || inst->IsPhi()) { 535 return; 536 } 537 [[maybe_unused]] LocationsInfo *locations = GetAllocator()->New<LocationsInfo>(GetAllocator(), inst); 538 for (size_t i = 0; i < inst->GetInputsCount(); i++) { 539 if (inst->GetInputType(i) != DataType::NO_TYPE) { 540 locations->SetLocation(i, Location::RequireRegister()); 541 } 542 } 543 }); 544 SetDefaultLocationsInit(); 545} 546 547int64_t Graph::GetBranchCounter(const BasicBlock *block, bool true_succ) 548{ 549 ASSERT(block->GetSuccsBlocks().size() == MAX_SUCCS_NUM); 550 auto last_inst = block->GetLastInst(); 551 RuntimeInterface::MethodPtr method; 552 if (last_inst->GetOpcode() == Opcode::IfImm) { 553 method = last_inst->CastToIfImm()->GetMethod(); 554 } else if (last_inst->GetOpcode() == Opcode::If) { 555 method = last_inst->CastToIf()->GetMethod(); 556 } else { 557 return 0; 558 } 559 560 if (method == nullptr) { 561 // corresponded branch instruction was not present in bytecode, e.g. IfImmInst was created while inlining 562 return 0; 563 } 564 565 return block->IsInverted() == 0; 566} 567 568uint32_t Graph::GetParametersSlotsCount() const 569{ 570 uint32_t max_slot = 0; 571 for (auto param_inst : GetParameters()) { 572 auto location = param_inst->CastToParameter()->GetLocationData().GetSrc(); 573 if (location.IsStackParameter()) { 574 max_slot = location.GetValue() + 1U; 575 } 576 } 577 return max_slot; 578} 579 580void GraphMode::Dump(std::ostream &stm) 581{ 582 const char *sep = ""; 583// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 584#define DUMP_MODE(name) \ 585 if (Is##name()) { \ 586 stm << sep << #name; \ 587 sep = ", "; \ 588 } 589 590 DUMP_MODE(Osr); 591 DUMP_MODE(BytecodeOpt); 592 DUMP_MODE(DynamicMethod); 593 DUMP_MODE(Native); 594 DUMP_MODE(FastPath); 595 DUMP_MODE(Boundary); 596 DUMP_MODE(Interpreter); 597 DUMP_MODE(InterpreterEntry); 598} 599 600} // namespace panda::compiler 601