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 "cleanup.h" 17 18#include "compiler_logger.h" 19#include "optimizer/analysis/linear_order.h" 20#include "optimizer/analysis/loop_analyzer.h" 21#include "optimizer/ir/basicblock.h" 22 23namespace panda::compiler { 24 25static bool SkipBasicBlock(BasicBlock *bb) 26{ 27 // TODO (a.popov) Make empty catch-begin and try-end blocks removeable 28 return bb == nullptr || bb->IsStartBlock() || bb->IsEndBlock() || bb->IsCatchBegin() || bb->IsTryEnd(); 29} 30 31/* Cleanup pass works like dead code elimination (DCE) and removes code which does not affect the program results. 32 * It also removes empty basic blocks when it is possible and merges a linear basic block sequence to one bigger 33 * basic block, thus simplifying control flow graph. 34 */ 35bool Cleanup::RunImpl() 36{ 37 GetGraph()->RunPass<DominatorsTree>(); 38 GetGraph()->RunPass<LoopAnalyzer>(); 39 40 // Two vectors to store basic blocks lists 41 auto empty_blocks = &empty1_; 42 auto new_empty_blocks = &empty2_; 43 44 bool modified = PhiChecker(); 45 46 for (auto bb : GetGraph()->GetVectorBlocks()) { 47 if (!SkipBasicBlock(bb) && bb->IsEmpty()) { 48 empty_blocks->insert(bb); 49 } 50 } 51 52 bool first_run = true; 53 do { 54 modified |= RunOnce(empty_blocks, new_empty_blocks, !first_run); 55 first_run = false; 56 // Swap vectors pointers 57 auto temp = empty_blocks; 58 empty_blocks = new_empty_blocks; 59 new_empty_blocks = temp; 60 // Clean the "new" list 61 new_empty_blocks->clear(); 62 } while (!empty_blocks->empty()); 63 64 empty1_.clear(); 65 empty2_.clear(); 66 67 /* Merge linear sectors. 68 * For merging possibility a block must have one successor, and its successor must have one predecessor. 69 * EXAMPLE: 70 * [1] 71 * | 72 * [2] 73 * | 74 * [3] 75 * | 76 * [4] 77 * 78 * turns into this: 79 * [1'] 80 */ 81 for (auto bb : GetGraph()->GetVectorBlocks()) { 82 if (bb == nullptr || bb->IsPseudoControlFlowBlock()) { 83 continue; 84 } 85 86 while (bb->GetSuccsBlocks().size() == 1 && bb->GetSuccessor(0)->GetPredsBlocks().size() == 1 && 87 !bb->GetSuccessor(0)->IsPseudoControlFlowBlock() && bb->IsTry() == bb->GetSuccessor(0)->IsTry()) { 88 ASSERT(!bb->GetSuccessor(0)->HasPhi()); 89 COMPILER_LOG(DEBUG, CLEANUP) << "Merged block " << bb->GetSuccessor(0)->GetId() << " into " << bb->GetId(); 90 bb->JoinSuccessorBlock(); 91 modified = true; 92 } 93 } 94 return modified; 95} 96 97bool Cleanup::RunOnce(ArenaSet<BasicBlock *> *empty_blocks, ArenaSet<BasicBlock *> *new_empty_blocks, bool simple_dce) 98{ 99 bool modified = false; 100 auto marker_holder = MarkerHolder(GetGraph()); 101 auto dead_mrk = marker_holder.GetMarker(); 102 103 // Check all basic blocks in "old" list 104 for (auto bb : *empty_blocks) { 105 // In some tricky cases block may be already deleted in previous iteration 106 if (bb->GetGraph() == nullptr) { 107 continue; 108 } 109 110 auto succ = bb->GetSuccessor(0); 111 // Strange infinite loop with only one empty block, or loop pre-header - lets bail out 112 if (succ == bb || succ->GetLoop()->GetPreHeader() == bb) { 113 continue; 114 } 115 116#ifndef NDEBUG 117 // Now we know that 'bb' is not a loop pre-header, so if both 'bb' and 'succ' have many predecessors 118 // all 'bb' Phi(s) must have user only in successor Phis 119 if (succ->GetPredsBlocks().size() > 1) { 120 for (auto phi : bb->PhiInsts()) { 121 for (auto &user_item : phi->GetUsers()) { 122 auto user = user_item.GetInst(); 123 ASSERT((user->GetBasicBlock() == succ && user->IsPhi()) || user->IsCatchPhi()); 124 } 125 } 126 } 127#endif 128 modified |= ProcessBB(bb, dead_mrk, new_empty_blocks); 129 } 130 131 if (simple_dce) { 132 modified |= SimpleDce(dead_mrk, new_empty_blocks); 133 } else { 134 modified |= Dce(dead_mrk, new_empty_blocks); 135 } 136 dead_.clear(); 137 138 return modified; 139} 140 141// Check around for special triangle case 142bool Cleanup::CheckSpecialTriangle(BasicBlock *bb) 143{ 144 auto succ = bb->GetSuccessor(0); 145 size_t i = 0; 146 for (auto pred : bb->GetPredsBlocks()) { 147 if (pred->GetSuccessor(0) == succ || 148 (pred->GetSuccsBlocks().size() == MAX_SUCCS_NUM && pred->GetSuccessor(1) == succ)) { 149 // Checking all Phis 150 for (auto phi : succ->PhiInsts()) { 151 size_t index_bb = phi->CastToPhi()->GetPredBlockIndex(bb); 152 size_t index_pred = phi->CastToPhi()->GetPredBlockIndex(pred); 153 ASSERT(index_bb != index_pred); 154 155 auto inst_pred = phi->GetInput(index_pred).GetInst(); 156 auto inst_bb = phi->GetInput(index_bb).GetInst(); 157 // If phi input is in 'bb', check input of that phi instead 158 if (inst_bb->GetBasicBlock() == bb) { 159 ASSERT(inst_bb->IsPhi()); 160 inst_bb = inst_bb->CastToPhi()->GetInput(i).GetInst(); 161 } 162 if (inst_bb != inst_pred) { 163 return true; 164 } 165 } 166 // Would fully remove 'straight' pred->succ edge, and second one would stay after 'bb' removal 167 saved_preds_.push_back(pred); 168 } 169 i++; 170 } 171 return false; 172} 173 174void Cleanup::RemoveDeadPhi(BasicBlock *bb, ArenaSet<BasicBlock *> *new_empty_blocks) 175{ 176 for (auto phi : bb->PhiInstsSafe()) { 177 if (!phi->GetUsers().Empty()) { 178 continue; 179 } 180 bb->RemoveInst(phi); 181 COMPILER_LOG(DEBUG, CLEANUP) << "Dead Phi removed " << phi->GetId(); 182 GetGraph()->GetEventWriter().EventCleanup(phi->GetId(), phi->GetPc()); 183 184 for (auto pred : bb->GetPredsBlocks()) { 185 if (pred->IsEmpty() && !SkipBasicBlock(pred)) { 186 COMPILER_LOG(DEBUG, CLEANUP) << "Would re-check empty block " << pred->GetId(); 187 new_empty_blocks->insert(pred); 188 } 189 } 190 } 191} 192 193bool Cleanup::ProcessBB(BasicBlock *bb, Marker dead_mrk, ArenaSet<BasicBlock *> *new_empty_blocks) 194{ 195 auto succ = bb->GetSuccessor(0); 196 if (CheckSpecialTriangle(bb)) { 197 return false; 198 } 199 // Remove dead Phi(s) 200 RemoveDeadPhi(bb, new_empty_blocks); 201 // Process saved predecessors 202 for (auto pred : saved_preds_) { 203 ASSERT(pred->GetSuccsBlocks().size() == MAX_SUCCS_NUM); 204 constexpr auto PREDS_BLOCK_NUM = 2; 205 ASSERT(succ->GetPredsBlocks().size() >= PREDS_BLOCK_NUM); 206 207 auto last = pred->GetLastInst(); 208 if (last->GetOpcode() == Opcode::If || last->GetOpcode() == Opcode::IfImm) { 209 last->SetMarker(dead_mrk); 210 dead_.push_back(last); 211 } else { 212 ASSERT(last->GetOpcode() == Opcode::Try); 213 } 214 pred->RemoveSucc(succ); 215 if (succ->GetPredsBlocks().size() == PREDS_BLOCK_NUM) { 216 for (auto phi : succ->PhiInstsSafe()) { 217 auto rm_index = phi->CastToPhi()->GetPredBlockIndex(pred); 218 auto remaining_inst = phi->GetInputs()[1 - rm_index].GetInst(); 219 phi->ReplaceUsers(remaining_inst); 220 succ->RemoveInst(phi); 221 } 222 } else { // more than 2 predecessors 223 for (auto phi : succ->PhiInstsSafe()) { 224 auto rm_index = phi->CastToPhi()->GetPredBlockIndex(pred); 225 phi->CastToPhi()->RemoveInput(rm_index); 226 } 227 } 228 succ->RemovePred(pred); 229 // Fixing LoopAnalysis or DomTree is no necessary here, because there would be another edge 230 } 231 saved_preds_.clear(); 232 bool bad_loop = bb->GetLoop()->IsIrreducible(); 233 GetGraph()->RemoveEmptyBlockWithPhis(bb, bad_loop); 234 if (bad_loop) { 235 GetGraph()->InvalidateAnalysis<LoopAnalyzer>(); 236 GetGraph()->RunPass<LoopAnalyzer>(); 237 } 238 COMPILER_LOG(DEBUG, CLEANUP) << "Removed empty block: " << bb->GetId(); 239 return true; 240} 241 242// Mark instructions that have the NOT_REMOVABLE property 243// and recursively mark all their inputs 244void Cleanup::MarkLiveRec(Marker live_mrk, Inst *inst) 245{ 246 // No recursion for one-input case, otherwise got stackoverflow on TSAN job 247 bool marked = false; 248 while (inst->GetInputsCount() == 1) { 249 marked = inst->SetMarker(live_mrk); 250 if (marked) { 251 break; 252 } 253 inst = inst->GetInput(0).GetInst(); 254 } 255 if (!marked && !inst->SetMarker(live_mrk)) { 256 for (auto input : inst->GetInputs()) { 257 MarkLiveRec(live_mrk, input.GetInst()); 258 } 259 } 260} 261 262bool Cleanup::Dce(Marker dead_mrk, ArenaSet<BasicBlock *> *new_empty_blocks) 263{ 264 bool modified = false; 265 auto marker_holder = MarkerHolder(GetGraph()); 266 auto live_mrk = marker_holder.GetMarker(); 267 268 // Mark live instructions 269 for (auto bb : GetGraph()->GetBlocksRPO()) { 270 for (auto inst : bb->AllInsts()) { 271 if (inst->IsNotRemovable() && !inst->IsMarked(dead_mrk)) { 272 MarkLiveRec(live_mrk, inst); 273 } 274 } 275 } 276 // Remove non-live instructions 277 for (auto bb : GetGraph()->GetBlocksRPO()) { 278 for (auto inst : bb->AllInstsSafe()) { 279 if (inst->IsMarked(live_mrk)) { 280 continue; 281 } 282 bool is_phi = inst->IsPhi(); 283 bb->RemoveInst(inst); 284 COMPILER_LOG(DEBUG, CLEANUP) << "Dead instruction " << inst->GetId(); 285 GetGraph()->GetEventWriter().EventCleanup(inst->GetId(), inst->GetPc()); 286 modified = true; 287 288 if (is_phi) { 289 for (auto pred : bb->GetPredsBlocks()) { 290 if (pred->IsEmpty() && !SkipBasicBlock(pred)) { 291 COMPILER_LOG(DEBUG, CLEANUP) << "Would re-check empty block " << pred->GetId(); 292 new_empty_blocks->insert(pred); 293 } 294 } 295 } else if (bb->IsEmpty() && !SkipBasicBlock(bb)) { 296 COMPILER_LOG(DEBUG, CLEANUP) << "No more non-Phi instructions in block " << bb->GetId(); 297 new_empty_blocks->insert(bb); 298 } 299 } 300 } 301 return modified; 302} 303 304void Cleanup::SetLiveRec(Inst *inst, Marker mrk, Marker live_mrk) 305{ 306 for (auto input_item : inst->GetInputs()) { 307 auto input = input_item.GetInst(); 308 if (!input->IsMarked(live_mrk) && input->IsMarked(mrk)) { 309 input->ResetMarker(mrk); 310 input->SetMarker(live_mrk); 311 SetLiveRec(input, mrk, live_mrk); 312 } 313 } 314} 315 316void Cleanup::LiveUserSearchRec(Inst *inst, Marker mrk, Marker live_mrk, Marker dead_mrk) 317{ 318 ASSERT(!inst->IsMarked(mrk)); 319 ASSERT(!inst->IsMarked(dead_mrk)); 320 if (inst->IsMarked(live_mrk)) { 321 SetLiveRec(inst, mrk, live_mrk); 322 return; 323 } 324 if (inst->IsNotRemovable()) { 325 inst->SetMarker(live_mrk); 326 SetLiveRec(inst, mrk, live_mrk); 327 return; 328 } 329 inst->SetMarker(mrk); 330 temp_.push_back(inst); 331 bool unknown = false; 332 for (auto &user_item : inst->GetUsers()) { 333 auto user = user_item.GetInst(); 334 if (user->IsMarked(mrk)) { 335 unknown = true; 336 continue; 337 } 338 if (user->IsMarked(dead_mrk)) { 339 continue; 340 } 341 LiveUserSearchRec(user, mrk, live_mrk, dead_mrk); 342 if (user->IsMarked(live_mrk)) { 343 ASSERT(!inst->IsMarked(mrk) && inst->IsMarked(live_mrk)); 344 return; 345 } 346 ASSERT(inst->IsMarked(mrk)); 347 if (user->IsMarked(mrk)) { 348 ASSERT(!user->IsMarked(live_mrk) && !user->IsMarked(dead_mrk)); 349 unknown = true; 350 } else { 351 ASSERT(user->IsMarked(dead_mrk)); 352 } 353 } 354 if (!unknown) { 355 inst->ResetMarker(mrk); 356 inst->SetMarker(dead_mrk); 357 dead_.push_back(inst); 358 } 359} 360 361void Cleanup::Marking(Marker dead_mrk, Marker mrk, Marker live_mrk) 362{ 363 size_t i = 0; 364 while (i < dead_.size()) { 365 auto inst = dead_.at(i); 366 for (auto input_item : inst->GetInputs()) { 367 auto input = input_item.GetInst(); 368 if (input->IsMarked(dead_mrk) || input->IsMarked(mrk)) { 369 continue; 370 } 371 LiveUserSearchRec(input, mrk, live_mrk, dead_mrk); 372 for (auto temp : temp_) { 373 if (temp->IsMarked(mrk)) { 374 ASSERT(!temp->IsMarked(live_mrk) && !temp->IsMarked(dead_mrk)); 375 inst->ResetMarker(mrk); 376 inst->SetMarker(dead_mrk); 377 dead_.push_back(inst); 378 } 379 } 380 temp_.clear(); 381 } 382 i++; 383 } 384} 385 386bool Cleanup::Removal(ArenaSet<BasicBlock *> *new_empty_blocks) 387{ 388 bool modified = false; 389 390 for (auto inst : dead_) { 391 inst->ClearMarkers(); 392 auto bb = inst->GetBasicBlock(); 393 if (bb == nullptr) { 394 continue; 395 } 396 bb->RemoveInst(inst); 397 COMPILER_LOG(DEBUG, CLEANUP) << "Dead instruction " << inst->GetId(); 398 GetGraph()->GetEventWriter().EventCleanup(inst->GetId(), inst->GetPc()); 399 modified = true; 400 401 if (inst->IsPhi()) { 402 for (auto pred : bb->GetPredsBlocks()) { 403 if (pred->IsEmpty() && !SkipBasicBlock(pred)) { 404 COMPILER_LOG(DEBUG, CLEANUP) << "Would re-check empty block " << pred->GetId(); 405 new_empty_blocks->insert(pred); 406 } 407 } 408 } else { 409 if (bb->IsEmpty() && !SkipBasicBlock(bb)) { 410 COMPILER_LOG(DEBUG, CLEANUP) << "No more non-Phi instructions in block " << bb->GetId(); 411 new_empty_blocks->insert(bb); 412 } 413 } 414 } 415 return modified; 416} 417 418bool Cleanup::SimpleDce(Marker dead_mrk, ArenaSet<BasicBlock *> *new_empty_blocks) 419{ 420 auto marker_holder = MarkerHolder(GetGraph()); 421 auto mrk = marker_holder.GetMarker(); 422 auto live_marker_holder = MarkerHolder(GetGraph()); 423 auto live_mrk = live_marker_holder.GetMarker(); 424 425 // Step 1. Marking 426 Marking(dead_mrk, mrk, live_mrk); 427 428 // Step 2. Removal 429 return Removal(new_empty_blocks); 430} 431 432void Cleanup::BuildDominators() 433{ 434 size_t amount = 0; 435 fake_root_ = reinterpret_cast<Inst *>(sizeof(Inst *)); 436 map_.insert({fake_root_, amount}); 437 for (auto bb : GetGraph()->GetBlocksRPO()) { 438 for (auto inst : bb->PhiInsts()) { 439 amount++; 440 map_.insert({inst, amount}); 441 for (auto input : inst->GetInputs()) { 442 auto pred = input.GetInst(); 443 if (!pred->IsPhi() && map_.count(pred) == 0) { 444 amount++; 445 map_.insert({pred, amount}); 446 } 447 } 448 } 449 } 450 Init(amount + 1); 451 SetVertex(0, fake_root_); 452 for (auto bb : GetGraph()->GetBlocksRPO()) { 453 for (auto inst : bb->Insts()) { 454 if (map_.count(inst) > 0 && GetSemi(inst) == DEFAULT_DFS_VAL) { 455 SetParent(inst, fake_root_); 456 DfsNumbering(inst); 457 } 458 } 459 } 460 ASSERT(static_cast<size_t>(dfs_num_) == amount); 461 462 for (size_t i = amount; i > 0; i--) { 463 ComputeImmediateDominators(GetVertex(i)); 464 } 465 466 for (size_t i = 1; i <= amount; i++) { 467 AdjustImmediateDominators(GetVertex(i)); 468 } 469} 470 471/* 472 * Adjust immediate dominators, 473 * Update dominator information for 'inst' 474 */ 475void Cleanup::AdjustImmediateDominators(Inst *inst) 476{ 477 ASSERT(inst != nullptr); 478 479 if (GetIdom(inst) != GetVertex(GetSemi(inst))) { 480 SetIdom(inst, GetIdom(GetIdom(inst))); 481 } 482} 483 484/* 485 * Compute initial values for semidominators, 486 * store instructions with the same semidominator in the same bucket, 487 * compute immediate dominators for instructions in the bucket of 'inst' parent 488 */ 489void Cleanup::ComputeImmediateDominators(Inst *inst) 490{ 491 ASSERT(inst != nullptr); 492 493 if (inst->IsPhi()) { 494 for (auto input : inst->GetInputs()) { 495 auto pred = input.GetInst(); 496 auto eval = Eval(pred); 497 if (GetSemi(eval) < GetSemi(inst)) { 498 SetSemi(inst, GetSemi(eval)); 499 } 500 } 501 } else { 502 auto eval = fake_root_; 503 if (GetSemi(eval) < GetSemi(inst)) { 504 SetSemi(inst, GetSemi(eval)); 505 } 506 } 507 508 auto vertex = GetVertex(GetSemi(inst)); 509 GetBucket(vertex).push_back(inst); 510 auto parent = GetParent(inst); 511 SetAncestor(inst, parent); 512 513 auto &bucket = GetBucket(parent); 514 while (!bucket.empty()) { 515 auto v = *bucket.rbegin(); 516 auto eval = Eval(v); 517 if (GetSemi(eval) < GetSemi(v)) { 518 SetIdom(v, eval); 519 } else { 520 SetIdom(v, parent); 521 } 522 bucket.pop_back(); 523 } 524} 525 526/* 527 * Compress ancestor path to 'inst' to the instruction whose label has the maximal semidominator number 528 */ 529void Cleanup::Compress(Inst *inst) 530{ 531 auto anc = GetAncestor(inst); 532 ASSERT(anc != nullptr); 533 534 if (GetAncestor(anc) != nullptr) { 535 Compress(anc); 536 if (GetSemi(GetLabel(anc)) < GetSemi(GetLabel(inst))) { 537 SetLabel(inst, GetLabel(anc)); 538 } 539 SetAncestor(inst, GetAncestor(anc)); 540 } 541} 542 543/* 544 * Depth-first search with numbering instruction in order they are reaching 545 */ 546void Cleanup::DfsNumbering(Inst *inst) 547{ 548 ASSERT(inst != nullptr || inst != fake_root_); 549 dfs_num_++; 550 ASSERT_PRINT(static_cast<size_t>(dfs_num_) < vertices_.size(), "DFS-number overflow"); 551 552 SetVertex(dfs_num_, inst); 553 SetLabel(inst, inst); 554 SetSemi(inst, dfs_num_); 555 SetAncestor(inst, nullptr); 556 557 for (auto &user : inst->GetUsers()) { 558 auto succ = user.GetInst(); 559 if (succ->IsPhi() && GetSemi(succ) == DEFAULT_DFS_VAL) { 560 SetParent(succ, inst); 561 DfsNumbering(succ); 562 } 563 } 564} 565 566/* 567 * Return 'inst' if it is the root of a tree 568 * Otherwise, after tree compressing 569 * return the instruction in the ancestors chain with the minimal semidominator DFS-number 570 */ 571Inst *Cleanup::Eval(Inst *inst) 572{ 573 ASSERT(inst != nullptr); 574 if (GetAncestor(inst) == nullptr) { 575 return inst; 576 } 577 Compress(inst); 578 return GetLabel(inst); 579} 580 581/* 582 * Initialize data structures to start DFS 583 */ 584void Cleanup::Init(size_t count) 585{ 586 ancestors_.clear(); 587 idoms_.clear(); 588 labels_.clear(); 589 parents_.clear(); 590 vertices_.clear(); 591 semi_.clear(); 592 593 ancestors_.resize(count); 594 idoms_.resize(count); 595 labels_.resize(count); 596 parents_.resize(count); 597 vertices_.resize(count); 598 semi_.resize(count); 599 600 std::fill(vertices_.begin(), vertices_.end(), nullptr); 601 std::fill(semi_.begin(), semi_.end(), DEFAULT_DFS_VAL); 602 603 if (buckets_.size() < count) { 604 buckets_.resize(count, InstVector(GetGraph()->GetLocalAllocator()->Adapter())); 605 } 606 for (auto &bucket : buckets_) { 607 bucket.clear(); 608 } 609 dfs_num_ = DEFAULT_DFS_VAL; 610} 611 612/* 613 * Selecting phi instructions that can be deleted 614 * and replaced with a single instruction for all uses 615 * 616 * Example 617 * ... 618 * 6p.u64 Phi v2(bb4), v2(bb3) 619 * 7.u64 Return v6p 620 * 621 * Removing instruction 6 and replacing use with v2 622 * 623 * ... 624 * 7.u64 Return v2 625 */ 626bool Cleanup::PhiChecker() 627{ 628 BuildDominators(); 629 bool modified = false; 630 for (auto bb : GetGraph()->GetBlocksRPO()) { 631 for (auto phi : bb->PhiInstsSafe()) { 632 auto change = GetIdom(phi); 633 if (change == fake_root_) { 634 continue; 635 } 636 while (GetIdom(change) != fake_root_) { 637 change = GetIdom(change); 638 } 639 auto basic_block = phi->GetBasicBlock(); 640 phi->ReplaceUsers(change); 641 basic_block->RemoveInst(phi); 642 modified = true; 643 } 644 } 645 map_.clear(); 646 return modified; 647} 648 649void Cleanup::InvalidateAnalyses() 650{ 651 GetGraph()->InvalidateAnalysis<LinearOrder>(); 652} 653} // namespace panda::compiler 654