1 /*
2 * Copyright (c) 2021-2025 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/alias_analysis.h"
20 #include "optimizer/analysis/bounds_analysis.h"
21 #include "optimizer/analysis/dominators_tree.h"
22 #include "optimizer/analysis/linear_order.h"
23 #include "optimizer/analysis/loop_analyzer.h"
24 #include "optimizer/ir/basicblock.h"
25
26 namespace ark::compiler {
27
CanBeMerged(BasicBlock * bb)28 bool Cleanup::CanBeMerged(BasicBlock *bb)
29 {
30 // TryCatchResolving with profile data needs separate CatchBegin with CatchPhis blocks
31 if (!GetGraph()->IsBytecodeOptimizer() &&
32 (!GetGraph()->IsAotMode() || GetGraph()->GetAotData()->HasProfileData()) && bb->IsCatchBegin()) {
33 return false;
34 }
35 return bb->GetSuccsBlocks().size() == 1 && bb->GetSuccessor(0)->GetPredsBlocks().size() == 1 &&
36 !bb->GetSuccessor(0)->IsPseudoControlFlowBlock() && bb->IsTry() == bb->GetSuccessor(0)->IsTry();
37 }
38
39 /* Cleanup pass works like dead code elimination (DCE) and removes code which does not affect the program results.
40 * It also removes empty basic blocks when it is possible and merges a linear basic block sequence to one bigger
41 * basic block, thus simplifying control flow graph.
42 */
43
RunImpl()44 bool Cleanup::RunImpl()
45 {
46 GetGraph()->RunPass<DominatorsTree>();
47 GetGraph()->RunPass<LoopAnalyzer>();
48 // Two vectors to store basic blocks lists
49 auto emptyBlocks = &empty1_;
50 auto newEmptyBlocks = &empty2_;
51
52 bool modified = (lightMode_ ? PhiCheckerLight() : PhiChecker());
53
54 for (auto bb : GetGraph()->GetVectorBlocks()) {
55 if (!SkipBasicBlock(bb) && bb->IsEmpty()) {
56 emptyBlocks->insert(bb);
57 }
58 }
59
60 bool firstRun = true;
61 do {
62 modified |= RunOnce(emptyBlocks, newEmptyBlocks, !firstRun);
63 firstRun = false;
64 // Swap vectors pointers
65 auto temp = emptyBlocks;
66 emptyBlocks = newEmptyBlocks;
67 newEmptyBlocks = temp;
68 // Clean the "new" list
69 newEmptyBlocks->clear();
70 } while (!emptyBlocks->empty());
71
72 empty1_.clear();
73 empty2_.clear();
74
75 /* Merge linear sectors.
76 * For merging possibility a block must have one successor, and its successor must have one predecessor.
77 * EXAMPLE:
78 * [1]
79 * |
80 * [2]
81 * |
82 * [3]
83 * |
84 * [4]
85 *
86 * turns into this:
87 * [1']
88 */
89 for (auto bb : GetGraph()->GetVectorBlocks()) {
90 if (bb == nullptr || bb->IsPseudoControlFlowBlock()) {
91 continue;
92 }
93
94 while (CanBeMerged(bb)) {
95 ASSERT(!bb->GetSuccessor(0)->HasPhi());
96 COMPILER_LOG(DEBUG, CLEANUP) << "Merged block " << bb->GetSuccessor(0)->GetId() << " into " << bb->GetId();
97 if (GetGraph()->EraseThrowBlock(bb->GetSuccessor(0))) {
98 [[maybe_unused]] auto res = GetGraph()->AppendThrowBlock(bb);
99 ASSERT(res);
100 }
101 bb->JoinSuccessorBlock();
102 modified = true;
103 }
104 }
105 return modified;
106 }
107
108 #ifndef NDEBUG
CheckBBPhisUsers(BasicBlock * succ,BasicBlock * bb)109 void Cleanup::CheckBBPhisUsers(BasicBlock *succ, BasicBlock *bb)
110 {
111 if (succ->GetPredsBlocks().size() > 1) {
112 for (auto phi : bb->PhiInsts()) {
113 for (auto &userItem : phi->GetUsers()) {
114 auto user = userItem.GetInst();
115 ASSERT((user->GetBasicBlock() == succ && user->IsPhi()) || user->IsCatchPhi());
116 }
117 }
118 }
119 }
120 #endif
121
RunOnce(ArenaSet<BasicBlock * > * emptyBlocks,ArenaSet<BasicBlock * > * newEmptyBlocks,bool simpleDce)122 bool Cleanup::RunOnce(ArenaSet<BasicBlock *> *emptyBlocks, ArenaSet<BasicBlock *> *newEmptyBlocks, bool simpleDce)
123 {
124 bool modified = false;
125 auto markerHolder = MarkerHolder(GetGraph());
126 auto deadMrk = markerHolder.GetMarker();
127 // Check all basic blocks in "old" list
128 for (auto bb : *emptyBlocks) {
129 // In some tricky cases block may be already deleted in previous iteration
130 if (bb->GetGraph() == nullptr) {
131 continue;
132 }
133
134 // Strange infinite loop with only one empty block, or loop pre-header - lets bail out
135 auto succ = bb->GetSuccessor(0);
136 if (succ == bb || succ->GetLoop()->GetPreHeader() == bb) {
137 continue;
138 }
139
140 #ifndef NDEBUG
141 // Now we know that 'bb' is not a loop pre-header, so if both 'bb' and 'succ' have many predecessors
142 // all 'bb' Phi(s) must have user only in successor Phis
143 CheckBBPhisUsers(succ, bb);
144 #endif
145 modified |= ProcessBB(bb, deadMrk, newEmptyBlocks);
146 }
147
148 if (simpleDce) {
149 modified |= SimpleDce(deadMrk, newEmptyBlocks);
150 } else if (lightMode_) {
151 modified |= Dce<true>(deadMrk, newEmptyBlocks);
152 } else {
153 modified |= Dce<false>(deadMrk, newEmptyBlocks);
154 }
155 dead_.clear();
156
157 return modified;
158 }
159
SkipBasicBlock(BasicBlock * bb)160 bool Cleanup::SkipBasicBlock(BasicBlock *bb)
161 {
162 // NOTE (a.popov) Make empty catch-begin and try-end blocks removeable
163 return bb == nullptr || bb->IsStartBlock() || bb->IsEndBlock() || bb->IsCatchBegin() || bb->IsTryEnd();
164 }
165
166 // Check around for special triangle case
CheckSpecialTriangle(BasicBlock * bb)167 bool Cleanup::CheckSpecialTriangle(BasicBlock *bb)
168 {
169 auto succ = bb->GetSuccessor(0);
170 size_t i = 0;
171 for (auto pred : bb->GetPredsBlocks()) {
172 if (pred->GetSuccessor(0) != succ &&
173 (pred->GetSuccsBlocks().size() != MAX_SUCCS_NUM || pred->GetSuccessor(1) != succ)) {
174 ++i;
175 continue;
176 }
177 // Checking all Phis
178 for (auto phi : succ->PhiInsts()) {
179 size_t indexBb = phi->CastToPhi()->GetPredBlockIndex(bb);
180 size_t indexPred = phi->CastToPhi()->GetPredBlockIndex(pred);
181 ASSERT(indexBb != indexPred);
182
183 auto instPred = phi->GetInput(indexPred).GetInst();
184 auto instBb = phi->GetInput(indexBb).GetInst();
185 // If phi input is in 'bb', check input of that phi instead
186 if (instBb->GetBasicBlock() == bb) {
187 ASSERT(instBb->IsPhi());
188 instBb = instBb->CastToPhi()->GetInput(i).GetInst();
189 }
190 if (instBb != instPred) {
191 return true;
192 }
193 }
194 // Would fully remove 'straight' pred->succ edge, and second one would stay after 'bb' removal
195 savedPreds_.push_back(pred);
196 i++;
197 }
198 return false;
199 }
200
RemoveDeadPhi(BasicBlock * bb,ArenaSet<BasicBlock * > * newEmptyBlocks)201 void Cleanup::RemoveDeadPhi(BasicBlock *bb, ArenaSet<BasicBlock *> *newEmptyBlocks)
202 {
203 for (auto phi : bb->PhiInstsSafe()) {
204 if (!phi->GetUsers().Empty()) {
205 continue;
206 }
207 bb->RemoveInst(phi);
208 COMPILER_LOG(DEBUG, CLEANUP) << "Dead Phi removed " << phi->GetId();
209 GetGraph()->GetEventWriter().EventCleanup(phi->GetId(), phi->GetPc());
210
211 for (auto pred : bb->GetPredsBlocks()) {
212 if (pred->IsEmpty() && !SkipBasicBlock(pred)) {
213 COMPILER_LOG(DEBUG, CLEANUP) << "Would re-check empty block " << pred->GetId();
214 newEmptyBlocks->insert(pred);
215 }
216 }
217 }
218 }
219
ProcessBB(BasicBlock * bb,Marker deadMrk,ArenaSet<BasicBlock * > * newEmptyBlocks)220 bool Cleanup::ProcessBB(BasicBlock *bb, Marker deadMrk, ArenaSet<BasicBlock *> *newEmptyBlocks)
221 {
222 auto succ = bb->GetSuccessor(0);
223 if (CheckSpecialTriangle(bb)) {
224 return false;
225 }
226 // Remove dead Phi(s)
227 RemoveDeadPhi(bb, newEmptyBlocks);
228 // Process saved predecessors
229 for (auto pred : savedPreds_) {
230 ASSERT(pred->GetSuccsBlocks().size() == MAX_SUCCS_NUM);
231 constexpr auto PREDS_BLOCK_NUM = 2;
232 ASSERT(succ->GetPredsBlocks().size() >= PREDS_BLOCK_NUM);
233
234 auto last = pred->GetLastInst();
235 if (last->GetOpcode() == Opcode::If || last->GetOpcode() == Opcode::IfImm ||
236 last->GetOpcode() == Opcode::AddOverflow || last->GetOpcode() == Opcode::SubOverflow) {
237 last->SetMarker(deadMrk);
238 dead_.push_back(last);
239 } else {
240 ASSERT(last->GetOpcode() == Opcode::Throw || last->GetOpcode() == Opcode::Try);
241 }
242 pred->RemoveSucc(succ);
243 if (succ->GetPredsBlocks().size() == PREDS_BLOCK_NUM) {
244 for (auto phi : succ->PhiInstsSafe()) {
245 auto rmIndex = phi->CastToPhi()->GetPredBlockIndex(pred);
246 auto remainingInst = phi->GetInputs()[1 - rmIndex].GetInst();
247 phi->ReplaceUsers(remainingInst);
248 succ->RemoveInst(phi);
249 }
250 } else { // more than 2 predecessors
251 for (auto phi : succ->PhiInstsSafe()) {
252 auto rmIndex = phi->CastToPhi()->GetPredBlockIndex(pred);
253 phi->CastToPhi()->RemoveInput(rmIndex);
254 }
255 }
256 succ->RemovePred(pred);
257 // Fixing LoopAnalysis or DomTree is no necessary here, because there would be another edge
258 }
259 savedPreds_.clear();
260 bool badLoop = bb->GetLoop()->IsIrreducible() || GetGraph()->IsThrowApplied();
261 if (!GetGraph()->IsThrowApplied()) {
262 GetGraph()->RemoveEmptyBlockWithPhis(bb, badLoop);
263 }
264 if (badLoop) {
265 GetGraph()->InvalidateAnalysis<LoopAnalyzer>();
266 GetGraph()->RunPass<LoopAnalyzer>();
267 }
268 COMPILER_LOG(DEBUG, CLEANUP) << "Removed empty block: " << bb->GetId();
269 return true;
270 }
271
MarkInlinedCaller(Marker liveMrk,Inst * saveState)272 void Cleanup::MarkInlinedCaller(Marker liveMrk, Inst *saveState)
273 {
274 if (!saveState->IsSaveState()) {
275 return;
276 }
277 auto caller = static_cast<SaveStateInst *>(saveState)->GetCallerInst();
278 // if caller->IsInlined() is false, graph is being inlined and caller is not in the graph
279 if (caller != nullptr && !caller->IsMarked(liveMrk) && caller->IsInlined()) {
280 MarkLiveRec<false>(liveMrk, caller);
281 }
282 }
283
IsRemovableCall(Inst * inst)284 bool Cleanup::IsRemovableCall(Inst *inst)
285 {
286 if (inst->IsCall() && static_cast<CallInst *>(inst)->IsInlined() && !static_cast<CallInst *>(inst)->GetIsNative()) {
287 auto saveState = inst->GetSaveState();
288 ASSERT(saveState != nullptr);
289 for (auto &ssUser : saveState->GetUsers()) {
290 if (ssUser.GetInst()->GetOpcode() == Opcode::ReturnInlined &&
291 ssUser.GetInst()->GetFlag(inst_flags::MEM_BARRIER)) {
292 return false;
293 }
294 }
295 return true;
296 }
297 return false;
298 }
299
300 // Mark instructions that have the NOT_REMOVABLE property
301 // and recursively mark all their inputs
302 template <bool LIGHT_MODE>
MarkLiveRec(Marker liveMrk,Inst * inst)303 void Cleanup::MarkLiveRec(Marker liveMrk, Inst *inst)
304 {
305 // No recursion for one-input case, otherwise got stackoverflow on TSAN job
306 bool marked = false;
307 while (inst->GetInputsCount() == 1) {
308 marked = inst->SetMarker(liveMrk);
309 if (marked) {
310 break;
311 }
312 if constexpr (!LIGHT_MODE) {
313 MarkInlinedCaller(liveMrk, inst);
314 }
315 inst = inst->GetInput(0).GetInst();
316 }
317 if (!marked && !inst->SetMarker(liveMrk)) {
318 if constexpr (!LIGHT_MODE) {
319 MarkInlinedCaller(liveMrk, inst);
320 }
321 for (auto input : inst->GetInputs()) {
322 MarkLiveRec<LIGHT_MODE>(liveMrk, input.GetInst());
323 }
324 }
325 }
326
327 template <bool LIGHT_MODE>
MarkOneLiveInst(Marker deadMrk,Marker liveMrk,Inst * inst)328 void Cleanup::MarkOneLiveInst(Marker deadMrk, Marker liveMrk, Inst *inst)
329 {
330 if constexpr (LIGHT_MODE) {
331 if (inst->IsNotRemovable() && !inst->IsMarked(deadMrk)) {
332 MarkLiveRec<true>(liveMrk, inst);
333 }
334 } else {
335 if (inst->GetOpcode() == Opcode::ReturnInlined) {
336 // SaveState input of ReturnInlined will be marked as live through CallInlined if needed
337 inst->SetMarker(liveMrk);
338 } else if (inst->IsNotRemovable() && !inst->IsMarked(deadMrk) && !IsRemovableCall(inst)) {
339 MarkLiveRec<false>(liveMrk, inst);
340 }
341 }
342 }
343
344 template <bool LIGHT_MODE>
MarkLiveInstructions(Marker deadMrk,Marker liveMrk)345 void Cleanup::MarkLiveInstructions(Marker deadMrk, Marker liveMrk)
346 {
347 for (auto bb : GetGraph()->GetBlocksRPO()) {
348 for (auto inst : bb->AllInsts()) {
349 MarkOneLiveInst<LIGHT_MODE>(deadMrk, liveMrk, inst);
350 }
351 }
352 }
353
354 template <bool LIGHT_MODE>
TryToRemoveNonLiveInst(Inst * inst,BasicBlock * bb,ArenaSet<BasicBlock * > * newEmptyBlocks,Marker liveMrk)355 bool Cleanup::TryToRemoveNonLiveInst(Inst *inst, BasicBlock *bb, ArenaSet<BasicBlock *> *newEmptyBlocks, Marker liveMrk)
356 {
357 bool modified = false;
358 if (inst->IsMarked(liveMrk)) {
359 auto saveState = inst->GetSaveState();
360 auto opcode = inst->GetOpcode();
361 ASSERT((LIGHT_MODE || opcode != Opcode::ReturnInlined) || saveState != nullptr);
362 if (LIGHT_MODE || !(opcode == Opcode::ReturnInlined && saveState->GetBasicBlock() == nullptr)) {
363 return modified;
364 }
365 }
366 if (!LIGHT_MODE && inst->IsCall() && static_cast<CallInst *>(inst)->IsInlined()) {
367 auto callerSs = inst->GetSaveState();
368 for (auto &user : callerSs->GetUsers()) {
369 auto userInst = user.GetInst();
370 // we mark all ReturnInlined as live initially to reduce number of IsDominate checks
371 // IsDominate check is needed because several pairs of Call/Return inlined can share SaveState
372 if (userInst->GetOpcode() == Opcode::ReturnInlined && inst->IsDominate(userInst)) {
373 userInst->ResetMarker(liveMrk);
374 }
375 }
376 }
377 bool isPhi = inst->IsPhi();
378 bb->RemoveInst(inst);
379 COMPILER_LOG(DEBUG, CLEANUP) << "Dead instruction " << inst->GetId();
380 GetGraph()->GetEventWriter().EventCleanup(inst->GetId(), inst->GetPc());
381 modified = true;
382
383 if (isPhi) {
384 for (auto pred : bb->GetPredsBlocks()) {
385 if (pred->IsEmpty() && !SkipBasicBlock(pred)) {
386 COMPILER_LOG(DEBUG, CLEANUP) << "Would re-check empty block " << pred->GetId();
387 newEmptyBlocks->insert(pred);
388 }
389 }
390 } else if (bb->IsEmpty() && !SkipBasicBlock(bb)) {
391 COMPILER_LOG(DEBUG, CLEANUP) << "No more non-Phi instructions in block " << bb->GetId();
392 newEmptyBlocks->insert(bb);
393 }
394 return modified;
395 }
396
397 template <bool LIGHT_MODE>
Dce(Marker deadMrk,ArenaSet<BasicBlock * > * newEmptyBlocks)398 bool Cleanup::Dce(Marker deadMrk, ArenaSet<BasicBlock *> *newEmptyBlocks)
399 {
400 bool modified = false;
401 auto markerHolder = MarkerHolder(GetGraph());
402 auto liveMrk = markerHolder.GetMarker();
403 MarkLiveInstructions<LIGHT_MODE>(deadMrk, liveMrk);
404
405 // Remove non-live instructions
406 for (auto bb : GetGraph()->GetBlocksRPO()) {
407 for (auto inst : bb->AllInstsSafe()) {
408 modified |= TryToRemoveNonLiveInst<LIGHT_MODE>(inst, bb, newEmptyBlocks, liveMrk);
409 }
410 }
411 return modified;
412 }
413
SetLiveRec(Inst * inst,Marker mrk,Marker liveMrk)414 void Cleanup::SetLiveRec(Inst *inst, Marker mrk, Marker liveMrk)
415 {
416 for (auto inputItem : inst->GetInputs()) {
417 auto input = inputItem.GetInst();
418 if (!input->IsMarked(liveMrk) && input->IsMarked(mrk)) {
419 input->ResetMarker(mrk);
420 input->SetMarker(liveMrk);
421 SetLiveRec(input, mrk, liveMrk);
422 }
423 }
424 }
425
LiveUserSearchRec(Inst * inst,Marker mrk,Marker liveMrk,Marker deadMrk)426 void Cleanup::LiveUserSearchRec(Inst *inst, Marker mrk, Marker liveMrk, Marker deadMrk)
427 {
428 ASSERT(!inst->IsMarked(mrk));
429 ASSERT(!inst->IsMarked(deadMrk));
430 if (inst->IsMarked(liveMrk)) {
431 SetLiveRec(inst, mrk, liveMrk);
432 return;
433 }
434 if (inst->IsNotRemovable()) {
435 inst->SetMarker(liveMrk);
436 SetLiveRec(inst, mrk, liveMrk);
437 return;
438 }
439 inst->SetMarker(mrk);
440 temp_.push_back(inst);
441 bool unknown = false;
442 for (auto &userItem : inst->GetUsers()) {
443 auto user = userItem.GetInst();
444 if (user->IsMarked(mrk)) {
445 unknown = true;
446 continue;
447 }
448 if (user->IsMarked(deadMrk)) {
449 continue;
450 }
451 LiveUserSearchRec(user, mrk, liveMrk, deadMrk);
452 if (user->IsMarked(liveMrk)) {
453 ASSERT(!inst->IsMarked(mrk) && inst->IsMarked(liveMrk));
454 return;
455 }
456 ASSERT(inst->IsMarked(mrk));
457 if (user->IsMarked(mrk)) {
458 ASSERT(!user->IsMarked(liveMrk) && !user->IsMarked(deadMrk));
459 unknown = true;
460 } else {
461 ASSERT(user->IsMarked(deadMrk));
462 }
463 }
464 if (!unknown) {
465 inst->ResetMarker(mrk);
466 inst->SetMarker(deadMrk);
467 dead_.push_back(inst);
468 }
469 }
470
TryMarkInstIsDead(Inst * inst,Marker deadMrk,Marker mrk,Marker liveMrk)471 void Cleanup::TryMarkInstIsDead(Inst *inst, Marker deadMrk, Marker mrk, [[maybe_unused]] Marker liveMrk)
472 {
473 if (!inst->IsMarked(mrk)) {
474 return;
475 }
476 ASSERT(!inst->IsMarked(liveMrk) && !inst->IsMarked(deadMrk));
477 inst->ResetMarker(mrk);
478 inst->SetMarker(deadMrk);
479 dead_.push_back(inst);
480 }
481
Marking(Marker deadMrk,Marker mrk,Marker liveMrk)482 void Cleanup::Marking(Marker deadMrk, Marker mrk, Marker liveMrk)
483 {
484 size_t i = 0;
485 while (i < dead_.size()) {
486 auto inst = dead_.at(i);
487 for (auto inputItem : inst->GetInputs()) {
488 auto input = inputItem.GetInst();
489 if (input->IsMarked(deadMrk) || input->IsMarked(mrk)) {
490 continue;
491 }
492 LiveUserSearchRec(input, mrk, liveMrk, deadMrk);
493 for (auto temp : temp_) {
494 TryMarkInstIsDead(temp, deadMrk, mrk, liveMrk);
495 }
496 temp_.clear();
497 }
498 i++;
499 }
500 }
501
RemovalPhi(BasicBlock * bb,ArenaSet<BasicBlock * > * newEmptyBlocks)502 void Cleanup::RemovalPhi(BasicBlock *bb, ArenaSet<BasicBlock *> *newEmptyBlocks)
503 {
504 for (auto pred : bb->GetPredsBlocks()) {
505 if (pred->IsEmpty() && !SkipBasicBlock(pred)) {
506 COMPILER_LOG(DEBUG, CLEANUP) << "Would re-check empty block " << pred->GetId();
507 newEmptyBlocks->insert(pred);
508 }
509 }
510 }
511
Removal(ArenaSet<BasicBlock * > * newEmptyBlocks)512 bool Cleanup::Removal(ArenaSet<BasicBlock *> *newEmptyBlocks)
513 {
514 bool modified = false;
515
516 for (auto inst : dead_) {
517 inst->ClearMarkers();
518 auto bb = inst->GetBasicBlock();
519 if (bb == nullptr) {
520 continue;
521 }
522 bb->RemoveInst(inst);
523 COMPILER_LOG(DEBUG, CLEANUP) << "Dead instruction " << inst->GetId();
524 GetGraph()->GetEventWriter().EventCleanup(inst->GetId(), inst->GetPc());
525 modified = true;
526
527 if (inst->IsPhi()) {
528 RemovalPhi(bb, newEmptyBlocks);
529 } else {
530 if (bb->IsEmpty() && !SkipBasicBlock(bb)) {
531 COMPILER_LOG(DEBUG, CLEANUP) << "No more non-Phi instructions in block " << bb->GetId();
532 newEmptyBlocks->insert(bb);
533 }
534 }
535 }
536 return modified;
537 }
538
SimpleDce(Marker deadMrk,ArenaSet<BasicBlock * > * newEmptyBlocks)539 bool Cleanup::SimpleDce(Marker deadMrk, ArenaSet<BasicBlock *> *newEmptyBlocks)
540 {
541 auto markerHolder = MarkerHolder(GetGraph());
542 auto mrk = markerHolder.GetMarker();
543 auto liveMarkerHolder = MarkerHolder(GetGraph());
544 auto liveMrk = liveMarkerHolder.GetMarker();
545
546 // Step 1. Marking
547 Marking(deadMrk, mrk, liveMrk);
548
549 // Step 2. Removal
550 return Removal(newEmptyBlocks);
551 }
552
BuildDominatorsVisitPhi(Inst * inst,size_t & amount)553 void Cleanup::BuildDominatorsVisitPhi(Inst *inst, size_t &amount)
554 {
555 amount++;
556 map_.insert({inst, amount});
557 for (auto input : inst->GetInputs()) {
558 auto pred = input.GetInst();
559 if (!pred->IsPhi() && map_.count(pred) == 0) {
560 amount++;
561 map_.insert({pred, amount});
562 }
563 }
564 }
565
BuildDominators()566 void Cleanup::BuildDominators()
567 {
568 size_t amount = 0;
569 fakeRoot_ = reinterpret_cast<Inst *>(sizeof(Inst *));
570 map_.insert({fakeRoot_, amount});
571 for (auto bb : GetGraph()->GetBlocksRPO()) {
572 for (auto inst : bb->PhiInsts()) {
573 BuildDominatorsVisitPhi(inst, amount);
574 }
575 }
576 Init(amount + 1);
577 SetVertex(0, fakeRoot_);
578 for (auto bb : GetGraph()->GetBlocksRPO()) {
579 for (auto inst : bb->Insts()) {
580 if (map_.count(inst) > 0 && GetSemi(inst) == DEFAULT_DFS_VAL) {
581 SetParent(inst, fakeRoot_);
582 DfsNumbering(inst);
583 }
584 }
585 }
586 ASSERT(static_cast<size_t>(dfsNum_) == amount);
587
588 for (size_t i = amount; i > 0; i--) {
589 ComputeImmediateDominators(GetVertex(i));
590 }
591
592 for (size_t i = 1; i <= amount; i++) {
593 AdjustImmediateDominators(GetVertex(i));
594 }
595 }
596
597 /*
598 * Adjust immediate dominators,
599 * Update dominator information for 'inst'
600 */
AdjustImmediateDominators(Inst * inst)601 void Cleanup::AdjustImmediateDominators(Inst *inst)
602 {
603 ASSERT(inst != nullptr);
604
605 if (GetIdom(inst) != GetVertex(GetSemi(inst))) {
606 SetIdom(inst, GetIdom(GetIdom(inst)));
607 }
608 }
609
610 /*
611 * Compute initial values for semidominators,
612 * store instructions with the same semidominator in the same bucket,
613 * compute immediate dominators for instructions in the bucket of 'inst' parent
614 */
ComputeImmediateDominators(Inst * inst)615 void Cleanup::ComputeImmediateDominators(Inst *inst)
616 {
617 ASSERT(inst != nullptr);
618
619 if (inst->IsPhi()) {
620 for (auto input : inst->GetInputs()) {
621 auto pred = input.GetInst();
622 auto eval = Eval(pred);
623 if (GetSemi(eval) < GetSemi(inst)) {
624 SetSemi(inst, GetSemi(eval));
625 }
626 }
627 } else {
628 auto eval = fakeRoot_;
629 if (GetSemi(eval) < GetSemi(inst)) {
630 SetSemi(inst, GetSemi(eval));
631 }
632 }
633
634 auto vertex = GetVertex(GetSemi(inst));
635 GetBucket(vertex).push_back(inst);
636 auto parent = GetParent(inst);
637 SetAncestor(inst, parent);
638
639 auto &bucket = GetBucket(parent);
640 while (!bucket.empty()) {
641 auto v = *bucket.rbegin();
642 auto eval = Eval(v);
643 if (GetSemi(eval) < GetSemi(v)) {
644 SetIdom(v, eval);
645 } else {
646 SetIdom(v, parent);
647 }
648 bucket.pop_back();
649 }
650 }
651
652 /*
653 * Compress ancestor path to 'inst' to the instruction whose label has the maximal semidominator number
654 */
Compress(Inst * inst)655 void Cleanup::Compress(Inst *inst)
656 {
657 auto anc = GetAncestor(inst);
658 ASSERT(anc != nullptr);
659
660 if (GetAncestor(anc) != nullptr) {
661 Compress(anc);
662 if (GetSemi(GetLabel(anc)) < GetSemi(GetLabel(inst))) {
663 SetLabel(inst, GetLabel(anc));
664 }
665 SetAncestor(inst, GetAncestor(anc));
666 }
667 }
668
669 /*
670 * Depth-first search with numbering instruction in order they are reaching
671 */
DfsNumbering(Inst * inst)672 void Cleanup::DfsNumbering(Inst *inst)
673 {
674 ASSERT(inst != nullptr || inst != fakeRoot_);
675 dfsNum_++;
676 ASSERT_PRINT(static_cast<size_t>(dfsNum_) < vertices_.size(), "DFS-number overflow");
677
678 SetVertex(dfsNum_, inst);
679 SetLabel(inst, inst);
680 SetSemi(inst, dfsNum_);
681 SetAncestor(inst, nullptr);
682
683 for (auto &user : inst->GetUsers()) {
684 auto succ = user.GetInst();
685 if (succ->IsPhi() && GetSemi(succ) == DEFAULT_DFS_VAL) {
686 SetParent(succ, inst);
687 DfsNumbering(succ);
688 }
689 }
690 }
691
692 /*
693 * Return 'inst' if it is the root of a tree
694 * Otherwise, after tree compressing
695 * return the instruction in the ancestors chain with the minimal semidominator DFS-number
696 */
Eval(Inst * inst)697 Inst *Cleanup::Eval(Inst *inst)
698 {
699 ASSERT(inst != nullptr);
700 if (GetAncestor(inst) == nullptr) {
701 return inst;
702 }
703 Compress(inst);
704 return GetLabel(inst);
705 }
706
707 /*
708 * Initialize data structures to start DFS
709 */
Init(size_t count)710 void Cleanup::Init(size_t count)
711 {
712 ancestors_.clear();
713 idoms_.clear();
714 labels_.clear();
715 parents_.clear();
716 vertices_.clear();
717 semi_.clear();
718
719 ancestors_.resize(count);
720 idoms_.resize(count);
721 labels_.resize(count);
722 parents_.resize(count);
723 vertices_.resize(count);
724 semi_.resize(count);
725
726 std::fill(vertices_.begin(), vertices_.end(), nullptr);
727 std::fill(semi_.begin(), semi_.end(), DEFAULT_DFS_VAL);
728
729 if (buckets_.size() < count) {
730 buckets_.resize(count, InstVector(GetGraph()->GetLocalAllocator()->Adapter()));
731 }
732 for (auto &bucket : buckets_) {
733 bucket.clear();
734 }
735 dfsNum_ = DEFAULT_DFS_VAL;
736 }
737
PhiHasSameInputs(Inst * phi)738 static bool PhiHasSameInputs(Inst *phi)
739 {
740 auto input = phi->GetInput(0).GetInst();
741 for (size_t i = 1; i < phi->GetInputsCount(); i++) {
742 if (input != phi->GetInput(i).GetInst()) {
743 return false;
744 }
745 }
746 return true;
747 }
748
749 // Remove only phi with same inputs
PhiCheckerLight() const750 bool Cleanup::PhiCheckerLight() const
751 {
752 bool modified = false;
753 for (auto bb : GetGraph()->GetBlocksRPO()) {
754 for (auto phi : bb->PhiInstsSafe()) {
755 if (PhiHasSameInputs(phi)) {
756 phi->ReplaceUsers(phi->GetInput(0).GetInst());
757 bb->RemoveInst(phi);
758 modified = true;
759 }
760 }
761 }
762 return modified;
763 }
764
765 /*
766 * Selecting phi instructions that can be deleted
767 * and replaced with a single instruction for all uses
768 *
769 * Example
770 * ...
771 * 6p.u64 Phi v2(bb4), v2(bb3)
772 * 7.u64 Return v6p
773 *
774 * Removing instruction 6 and replacing use with v2
775 *
776 * ...
777 * 7.u64 Return v2
778 */
PhiChecker()779 bool Cleanup::PhiChecker()
780 {
781 BuildDominators();
782 bool modified = false;
783 for (auto bb : GetGraph()->GetBlocksRPO()) {
784 for (auto phi : bb->PhiInstsSafe()) {
785 auto change = GetIdom(phi);
786 if (change == fakeRoot_) {
787 continue;
788 }
789 while (GetIdom(change) != fakeRoot_) {
790 change = GetIdom(change);
791 }
792 auto basicBlock = phi->GetBasicBlock();
793 phi->ReplaceUsers(change);
794 basicBlock->RemoveInst(phi);
795 modified = true;
796 }
797 }
798 map_.clear();
799 return modified;
800 }
801
InvalidateAnalyses()802 void Cleanup::InvalidateAnalyses()
803 {
804 GetGraph()->InvalidateAnalysis<LinearOrder>();
805 GetGraph()->InvalidateAnalysis<BoundsAnalysis>();
806 GetGraph()->InvalidateAnalysis<AliasAnalysis>();
807 }
808 } // namespace ark::compiler
809