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