• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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