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