• 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 "optimizer/ir/basicblock.h"
17 #include "loop_analyzer.h"
18 #include "optimizer/ir/graph.h"
19 #include "optimizer/analysis/dominators_tree.h"
20 #include "optimizer/analysis/rpo.h"
21 
22 namespace ark::compiler {
RunImpl()23 bool LoopAnalyzer::RunImpl()
24 {
25     GetGraph()->RunPass<DominatorsTree>();
26     ResetLoopInfo();
27     CreateRootLoop();
28     CollectBackEdges();
29     PopulateLoops();
30     for (auto loop : GetGraph()->GetRootLoop()->GetInnerLoops()) {
31         FindAndInsertPreHeaders(loop);
32     }
33     SetLoopProperties(GetGraph()->GetRootLoop(), 0);
34     return true;
35 }
36 
ResetLoopInfo()37 void LoopAnalyzer::ResetLoopInfo()
38 {
39     for (auto block : GetGraph()->GetVectorBlocks()) {
40         if (block != nullptr) {
41             block->SetLoop(nullptr);
42             block->SetNextLoop(nullptr);
43         }
44     }
45     GetGraph()->SetRootLoop(nullptr);
46     GetGraph()->SetHasIrreducibleLoop(false);
47     GetGraph()->SetHasInfiniteLoop(false);
48     loopCounter_ = 0;
49 }
50 
CreateNewLoop(BasicBlock * loopHeader)51 Loop *LoopAnalyzer::CreateNewLoop(BasicBlock *loopHeader)
52 {
53     auto loop = GetGraph()->GetAllocator()->New<Loop>(GetGraph()->GetAllocator(), loopHeader, loopCounter_++);
54     loop->AppendBlock(loopHeader);
55     return loop;
56 }
57 
CreateRootLoop()58 void LoopAnalyzer::CreateRootLoop()
59 {
60     ASSERT(GetGraph()->GetRootLoop() == nullptr);
61     auto rootLoop = GetGraph()->GetAllocator()->New<Loop>(GetGraph()->GetAllocator(), nullptr, loopCounter_++);
62     rootLoop->SetAsRoot();
63     GetGraph()->SetRootLoop(rootLoop);
64 }
65 
CollectBackEdges()66 void LoopAnalyzer::CollectBackEdges()
67 {
68     blackMarker_ = GetGraph()->NewMarker();
69     grayMarker_ = GetGraph()->NewMarker();
70     BackEdgeSearch(GetGraph()->GetStartBlock());
71     GetGraph()->EraseMarker(blackMarker_);
72     GetGraph()->EraseMarker(grayMarker_);
73 }
74 
75 /*
76  * Depth-first search to find back edges in the graph.
77  * When a block is visited for the first time it is marked with a gray marker,
78  * after visiting all his successors, a block is marked with a black marker.
79  * While doing DFS, if we encounter a block with a gray mark, then edge to this block is back edge.
80  */
BackEdgeSearch(BasicBlock * block)81 void LoopAnalyzer::BackEdgeSearch(BasicBlock *block)
82 {
83     block->SetMarker(grayMarker_);
84     block->SetMarker(blackMarker_);
85     for (auto succ : block->GetSuccsBlocks()) {
86         if (succ->IsMarked(grayMarker_)) {
87             ProcessNewBackEdge(succ, block);
88         } else if (!succ->IsMarked(blackMarker_)) {
89             BackEdgeSearch(succ);
90         }
91     }
92     block->ResetMarker(grayMarker_);
93 }
94 
95 /*
96  * Create new Loop if it doesn't exists.
97  * Append information about its header, back edge and check if this loop is irreducible.
98  * Loop is irreducible when its header doesn't dominate back edge.
99  */
ProcessNewBackEdge(BasicBlock * header,BasicBlock * backEdge)100 void LoopAnalyzer::ProcessNewBackEdge(BasicBlock *header, BasicBlock *backEdge)
101 {
102     auto loop = header->GetLoop();
103     if (loop == nullptr) {
104         loop = CreateNewLoop(header);
105     }
106 
107     loop->AppendBackEdge(backEdge);
108     if (!header->IsDominate(backEdge)) {
109         loop->SetIsIrreducible(true);
110         GetGraph()->SetHasIrreducibleLoop(true);
111     }
112 }
113 
114 /*
115  * Get vector of forward edges indexes in descending order
116  */
GetForwardEdgesIndexes(BasicBlock * header)117 ArenaVector<int> LoopAnalyzer::GetForwardEdgesIndexes(BasicBlock *header)
118 {
119     // Mark back-edges
120     auto markerHolder = compiler::MarkerHolder(GetGraph());
121     auto backEdgeMarker = markerHolder.GetMarker();
122     auto &backEdges = header->GetLoop()->GetBackEdges();
123     for (auto backEdge : backEdges) {
124         backEdge->SetMarker(backEdgeMarker);
125     }
126 
127     ArenaVector<int> indexes(header->GetGraph()->GetAllocator()->Adapter());
128     auto &predBlocks = header->GetPredsBlocks();
129     for (int idx = static_cast<int>(predBlocks.size()) - 1; idx >= 0; idx--) {
130         if (!predBlocks[idx]->IsMarked(backEdgeMarker)) {
131             indexes.push_back(idx);
132         }
133     }
134     ASSERT(indexes.size() + backEdges.size() == predBlocks.size());
135     return indexes;
136 }
137 
MovePhiInputsToPreHeader(BasicBlock * header,BasicBlock * preHeader,const ArenaVector<int> & fwEdgesIndexes)138 void LoopAnalyzer::MovePhiInputsToPreHeader(BasicBlock *header, BasicBlock *preHeader,
139                                             const ArenaVector<int> &fwEdgesIndexes)
140 {
141     for (auto phi : header->PhiInsts()) {
142         auto newPhi = GetGraph()->CreateInstPhi(phi->GetType(), phi->GetPc());
143         for (auto idx : fwEdgesIndexes) {
144             auto pred {header->GetPredBlockByIndex(idx)};
145             auto phiIdx {phi->CastToPhi()->GetPredBlockIndex(pred)};
146             newPhi->AppendInput(phi->GetInput(phiIdx).GetInst());
147             phi->RemoveInput(phiIdx);
148         }
149         preHeader->AppendPhi(newPhi);
150         phi->AppendInput(newPhi);
151     }
152 }
153 
UpdateControlFlowWithPreHeader(BasicBlock * header,BasicBlock * preHeader,const ArenaVector<int> & fwEdgesIndexes)154 void LoopAnalyzer::UpdateControlFlowWithPreHeader(BasicBlock *header, BasicBlock *preHeader,
155                                                   const ArenaVector<int> &fwEdgesIndexes)
156 {
157     constexpr size_t IMM_2 = 2;
158     if (fwEdgesIndexes.size() >= IMM_2) {
159         for (auto predIdx : fwEdgesIndexes) {
160             auto edge = header->GetPredBlockByIndex(predIdx);
161             edge->ReplaceSucc(header, preHeader);
162             header->RemovePred(edge);
163         }
164         preHeader->AddSucc(header);
165     } else {
166         ASSERT(fwEdgesIndexes.size() == 1);
167         auto edge = header->GetPredBlockByIndex(fwEdgesIndexes[0]);
168         edge->ReplaceSucc(header, preHeader);
169         header->ReplacePred(edge, preHeader);
170     }
171     // Update RPO
172     GetGraph()->GetAnalysis<Rpo>().SetValid(true);
173     GetGraph()->GetAnalysis<Rpo>().AddBasicBlockBefore(header, preHeader);
174 }
175 
176 /*
177  * Create block with the same amount of phi instructions as in a `header` and insert it before a `header`.
178  * Move relevant to forward edges phi inputs to pre-header.
179  */
CreatePreHeader(BasicBlock * header)180 BasicBlock *LoopAnalyzer::CreatePreHeader(BasicBlock *header)
181 {
182     auto fwEdgesIndexes = GetForwardEdgesIndexes(header);
183     auto preHeader = header->CreateImmediateDominator();
184     preHeader->SetGuestPc(header->GetGuestPc());
185     if (fwEdgesIndexes.size() >= 2U) {
186         MovePhiInputsToPreHeader(header, preHeader, fwEdgesIndexes);
187     }
188     UpdateControlFlowWithPreHeader(header, preHeader, fwEdgesIndexes);
189     return preHeader;
190 }
191 
PreHeaderExists(Loop * loop)192 bool LoopAnalyzer::PreHeaderExists(Loop *loop)
193 {
194     auto header = loop->GetHeader();
195     return header->GetPredsBlocks().size() - loop->GetBackEdges().size() == 1 &&
196            header->GetDominator()->GetLoop() == loop->GetOuterLoop() &&
197            header->GetDominator() != GetGraph()->GetStartBlock() && header->GetDominator()->GetNextLoop() == nullptr;
198 }
199 
200 /*
201  * Find all loop pre-headers. If loop doesn't have pre-header, insert it
202  */
FindAndInsertPreHeaders(Loop * loop)203 void LoopAnalyzer::FindAndInsertPreHeaders(Loop *loop)
204 {
205     ASSERT(loop != nullptr && loop->GetHeader() != nullptr);
206     auto header = loop->GetHeader();
207 
208     if (loop->IsTryCatchLoop()) {
209         loop->SetPreHeader(nullptr);
210     } else if (!loop->IsIrreducible()) {
211         BasicBlock *preHeader = nullptr;
212         if (PreHeaderExists(loop)) {
213             preHeader = header->GetDominator();
214         } else {
215             preHeader = CreatePreHeader(header);
216             preHeader->CopyTryCatchProps(header);
217             loop->GetOuterLoop()->AppendBlock(preHeader);
218         }
219         ASSERT(preHeader->GetNextLoop() == nullptr);  // a preheader shouldn't relate to several loops.
220         loop->SetPreHeader(preHeader);
221     }
222 
223     for (auto innerLoop : loop->GetInnerLoops()) {
224         FindAndInsertPreHeaders(innerLoop);
225     }
226 }
227 
GetPreHeader() const228 BasicBlock *Loop::GetPreHeader() const
229 {
230     ASSERT(!preHeader_ || preHeader_->GetNextLoop() == this);
231     return preHeader_;
232 }
233 
SetPreHeader(BasicBlock * preHeader)234 void Loop::SetPreHeader(BasicBlock *preHeader)
235 {
236     if (preHeader_ != nullptr && preHeader_->GetNextLoop() == this) {
237         preHeader_->SetNextLoop(nullptr);
238     }
239     preHeader_ = preHeader;
240     preHeader_->SetNextLoop(this);
241 }
SetPreHeader(std::nullptr_t preHeader)242 void Loop::SetPreHeader(std::nullptr_t preHeader)
243 {
244     if (preHeader_ != nullptr && preHeader_->GetNextLoop() == this) {
245         preHeader_->SetNextLoop(nullptr);
246     }
247     preHeader_ = preHeader;
248 }
249 
PopulateIrreducibleLoop(Loop * loop)250 void LoopAnalyzer::PopulateIrreducibleLoop(Loop *loop)
251 {
252     // Add back-edges to the loop for further analysis
253     // Note that other blocks of `loop` besides the header are not added to it,
254     // and outer loop of inner loops will be not `loop`, but its outer reducible (maybe root) loop
255     for (auto backEdge : loop->GetBackEdges()) {
256         if (backEdge->GetLoop() != loop) {
257             loop->AppendBlock(backEdge);
258         }
259     }
260 }
261 
262 /*
263  * Visiting existing loop headers to populate loops with blocks
264  * Search algorithm starts from the loop back edge and recursively adds all predecessors until loop header not found
265  */
PopulateLoops()266 void LoopAnalyzer::PopulateLoops()
267 {
268     for (auto it = GetGraph()->GetBlocksRPO().rbegin(); it != GetGraph()->GetBlocksRPO().rend(); it++) {
269         auto block = *it;
270         if (block->GetLoop() == nullptr || !block->IsLoopHeader()) {
271             continue;
272         }
273         auto loop = block->GetLoop();
274         if (loop->IsIrreducible()) {
275             PopulateIrreducibleLoop(loop);
276         } else {
277             blackMarker_ = GetGraph()->NewMarker();
278             block->SetMarker(blackMarker_);
279             for (auto backEdge : loop->GetBackEdges()) {
280                 NaturalLoopSearch(loop, backEdge);
281             }
282             GetGraph()->EraseMarker(blackMarker_);
283         }
284     }
285 
286     // Populate the root loop with blocks which are not assign to any loops
287     // Link all outer loops with the root loop
288     auto rootLoop = GetGraph()->GetRootLoop();
289     for (auto block : GetGraph()->GetBlocksRPO()) {
290         if (block->GetLoop() == nullptr) {
291             rootLoop->AppendBlock(block);
292         } else if (block->GetLoop()->GetOuterLoop() == nullptr) {
293             block->GetLoop()->SetOuterLoop(rootLoop);
294             rootLoop->AppendInnerLoop(block->GetLoop());
295         }
296     }
297 }
298 
299 /*
300  * Depth-first search to find blocks in the loop.
301  * When a block is visited for the first time it is marked with a black marker, added to the loop
302  * (if it hasn't been already added to the inner loop), and search runs for all its predecessors.
303  * Header block is marked firstly to stop search on it.
304  */
NaturalLoopSearch(Loop * loop,BasicBlock * block)305 void LoopAnalyzer::NaturalLoopSearch(Loop *loop, BasicBlock *block)
306 {
307     if (!block->IsMarked(blackMarker_)) {
308         block->SetMarker(blackMarker_);
309 
310         if (block->GetLoop() == nullptr) {
311             // `block` without assignment to any loop is found
312             loop->AppendBlock(block);
313         } else if (block->GetLoop()->GetHeader() != loop->GetHeader()) {
314             // `block` from an inner loop id found, because its header differs from searching loop header
315             if (block->GetLoop()->GetOuterLoop() == nullptr) {
316                 // Link outer loop and inner loop
317                 block->GetLoop()->SetOuterLoop(loop);
318                 loop->AppendInnerLoop(block->GetLoop());
319             }
320         }
321 
322         for (auto pred : block->GetPredsBlocks()) {
323             NaturalLoopSearch(loop, pred);
324         }
325     }
326 }
327 
SetLoopProperties(Loop * loop,uint32_t depth)328 void LoopAnalyzer::SetLoopProperties(Loop *loop, uint32_t depth)
329 {
330     loop->CheckInfinity();
331     loop->SetDepth(depth);
332     if (loop->IsInfinite()) {
333         GetGraph()->SetHasInfiniteLoop(true);
334     }
335     depth++;
336     for (auto innerLoop : loop->GetInnerLoops()) {
337         SetLoopProperties(innerLoop, depth);
338     }
339 }
340 
AppendBlock(BasicBlock * block)341 void Loop::AppendBlock(BasicBlock *block)
342 {
343     ASSERT(std::find(blocks_.begin(), blocks_.end(), block) == blocks_.end());
344     block->SetLoop(this);
345     blocks_.push_back(block);
346 }
347 
RemoveBlock(BasicBlock * block)348 void Loop::RemoveBlock(BasicBlock *block)
349 {
350     ASSERT(block != GetHeader());
351     ASSERT(!HasBackEdge(block));
352 #ifndef NDEBUG
353     for (auto innerLoop : GetInnerLoops()) {
354         ASSERT(block != innerLoop->GetPreHeader());
355     }
356 #endif
357 
358     auto blockIt = std::find(blocks_.begin(), blocks_.end(), block);
359     ASSERT(blockIt != blocks_.end());
360     blocks_.erase(blockIt);
361 }
362 
IsOsrLoop() const363 bool Loop::IsOsrLoop() const
364 {
365     return !IsRoot() && GetHeader()->IsOsrEntry();
366 }
367 
IsTryCatchLoop() const368 bool Loop::IsTryCatchLoop() const
369 {
370     return !IsRoot() && GetHeader()->IsCatchBegin();
371 }
372 
373 /*
374  * Check if this loop is inside other
375  */
IsInside(Loop * other)376 bool Loop::IsInside(Loop *other)
377 {
378     auto outer = this->GetOuterLoop();
379     while (outer != nullptr) {
380         if (outer == other) {
381             return true;
382         }
383         outer = outer->GetOuterLoop();
384     }
385     return false;
386 }
387 
MoveHeaderToSucc()388 void Loop::MoveHeaderToSucc()
389 {
390     ASSERT(header_->GetSuccsBlocks().size() == 1);
391     header_ = header_->GetSuccessor(0);
392     ASSERT(header_->GetLoop() == this);
393     auto it = std::find(blocks_.begin(), blocks_.end(), header_);
394     ASSERT(it != blocks_.end());
395     std::swap(*it, *blocks_.begin());
396 }
397 
CheckInfinity()398 void Loop::CheckInfinity()
399 {
400     isInfinite_ = false;
401     if (isRoot_) {
402         return;
403     }
404     auto outerLoop = GetOuterLoop();
405     for (auto block : GetBlocks()) {
406         const auto &succs = block->GetSuccsBlocks();
407         bool hasExit = std::find_if(succs.begin(), succs.end(), [&outerLoop](const BasicBlock *bb) {
408                            return bb->GetLoop() == outerLoop;
409                        }) != succs.end();
410         if (hasExit) {
411             return;
412         }
413     }
414     isInfinite_ = true;
415 }
416 
IsPostExitBlock(const BasicBlock * block) const417 bool Loop::IsPostExitBlock(const BasicBlock *block) const
418 {
419     for (auto loopBlock : GetBlocks()) {
420         for (auto succ : loopBlock->GetSuccsBlocks()) {
421             if (succ->GetLoop() != GetOuterLoop()) {
422                 continue;
423             }
424 
425             if (succ == block) {
426                 return true;
427             }
428         }
429     }
430 
431     return false;
432 }
433 
434 /*
435  * Find outside block for the loop with single back-edge exit
436  */
GetLoopOutsideSuccessor(Loop * loop)437 BasicBlock *GetLoopOutsideSuccessor(Loop *loop)
438 {
439     ASSERT(loop->GetBackEdges().size() == 1);
440     auto backEdge = loop->GetBackEdges()[0];
441     auto headerSuccIdx = backEdge->GetSuccBlockIndex(loop->GetHeader());
442     ASSERT(backEdge->GetSuccsBlocks().size() == MAX_SUCCS_NUM);
443     auto outsideBlock = backEdge->GetSuccessor(1 - headerSuccIdx);
444     ASSERT(outsideBlock != nullptr);
445     return outsideBlock;
446 }
447 
448 /**
449  * Check if the loop block sequence meets the requirements:
450  * - there is only one back-edge;
451  * - there is only one exit-point - from the back-edge;
452  */
IsLoopSingleBackEdgeExitPoint(Loop * loop)453 bool IsLoopSingleBackEdgeExitPoint(Loop *loop)
454 {
455     ASSERT(loop != nullptr);
456     if (loop->IsIrreducible()) {
457         return false;
458     }
459     if (loop->GetBackEdges().size() != 1) {
460         return false;
461     }
462     auto backEdge = loop->GetBackEdges()[0];
463     // Check there are no side-exits
464     for (auto block : loop->GetBlocks()) {
465         if (block == backEdge) {
466             continue;
467         }
468         for (auto succ : block->GetSuccsBlocks()) {
469             if (succ->GetLoop() != loop && !succ->GetLoop()->IsInside(loop)) {
470                 return false;
471             }
472         }
473     }
474     return true;
475 }
476 }  // namespace ark::compiler
477