1 /**
2 * Copyright (c) 2021-2022 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 "optimizer/ir/graph.h"
18 #include "optimizer/analysis/rpo.h"
19 #include "loop_analyzer.h"
20
21 namespace panda::compiler {
RunImpl()22 bool LoopAnalyzer::RunImpl()
23 {
24 GetGraph()->RunPass<DominatorsTree>();
25 ResetLoopInfo();
26 CreateRootLoop();
27 CollectBackEdges();
28 PopulateLoops();
29 for (auto loop : GetGraph()->GetRootLoop()->GetInnerLoops()) {
30 FindAndInsertPreHeaders(loop);
31 }
32 SearchInfiniteLoops(GetGraph()->GetRootLoop());
33 return true;
34 }
35
ResetLoopInfo()36 void LoopAnalyzer::ResetLoopInfo()
37 {
38 for (auto block : GetGraph()->GetVectorBlocks()) {
39 if (block != nullptr) {
40 block->SetLoop(nullptr);
41 }
42 }
43 GetGraph()->SetRootLoop(nullptr);
44 GetGraph()->SetHasIrreducibleLoop(false);
45 GetGraph()->SetHasInfiniteLoop(false);
46 loop_counter_ = 0;
47 }
48
CreateNewLoop(BasicBlock * loop_header)49 Loop *LoopAnalyzer::CreateNewLoop(BasicBlock *loop_header)
50 {
51 auto loop = GetGraph()->GetAllocator()->New<Loop>(GetGraph()->GetAllocator(), loop_header, loop_counter_++);
52 CHECK_NOT_NULL(loop);
53 loop->AppendBlock(loop_header);
54 return loop;
55 }
56
CreateRootLoop()57 void LoopAnalyzer::CreateRootLoop()
58 {
59 ASSERT(GetGraph()->GetRootLoop() == nullptr);
60 auto root_loop = GetGraph()->GetAllocator()->New<Loop>(GetGraph()->GetAllocator(), nullptr, loop_counter_++);
61 CHECK_NOT_NULL(root_loop);
62 root_loop->SetAsRoot();
63 GetGraph()->SetRootLoop(root_loop);
64 }
65
CollectBackEdges()66 void LoopAnalyzer::CollectBackEdges()
67 {
68 black_marker_ = GetGraph()->NewMarker();
69 gray_marker_ = GetGraph()->NewMarker();
70 BackEdgeSearch(GetGraph()->GetStartBlock());
71 GetGraph()->EraseMarker(black_marker_);
72 GetGraph()->EraseMarker(gray_marker_);
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(gray_marker_);
84 block->SetMarker(black_marker_);
85 for (auto succ : block->GetSuccsBlocks()) {
86 if (succ->IsMarked(gray_marker_)) {
87 ProcessNewBackEdge(succ, block);
88 } else if (!succ->IsMarked(black_marker_)) {
89 BackEdgeSearch(succ);
90 }
91 }
92 block->ResetMarker(gray_marker_);
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 * back_edge)100 void LoopAnalyzer::ProcessNewBackEdge(BasicBlock *header, BasicBlock *back_edge)
101 {
102 auto loop = header->GetLoop();
103 if (loop == nullptr) {
104 loop = CreateNewLoop(header);
105 }
106
107 loop->AppendBackEdge(back_edge);
108 if (!header->IsDominate(back_edge)) {
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 marker_holder = compiler::MarkerHolder(GetGraph());
121 auto back_edge_marker = marker_holder.GetMarker();
122 auto &back_edges = header->GetLoop()->GetBackEdges();
123 for (auto back_edge : back_edges) {
124 back_edge->SetMarker(back_edge_marker);
125 }
126
127 ArenaVector<int> indexes(header->GetGraph()->GetAllocator()->Adapter());
128 auto &pred_blocks = header->GetPredsBlocks();
129 for (int idx = static_cast<int>(pred_blocks.size()) - 1; idx >= 0; idx--) {
130 if (!pred_blocks[idx]->IsMarked(back_edge_marker)) {
131 indexes.push_back(idx);
132 }
133 }
134 ASSERT(indexes.size() + back_edges.size() == pred_blocks.size());
135 return indexes;
136 }
137
MovePhiInputsToPreHeader(BasicBlock * header,BasicBlock * pre_header,const ArenaVector<int> & fw_edges_indexes)138 void LoopAnalyzer::MovePhiInputsToPreHeader(BasicBlock *header, BasicBlock *pre_header,
139 const ArenaVector<int> &fw_edges_indexes)
140 {
141 for (auto phi : header->PhiInsts()) {
142 auto new_phi = GetGraph()->CreateInstPhi(phi->GetType(), phi->GetPc());
143 for (auto idx : fw_edges_indexes) {
144 auto pred {header->GetPredBlockByIndex(idx)};
145 auto phi_idx {phi->CastToPhi()->GetPredBlockIndex(pred)};
146 new_phi->AppendInput(phi->GetInput(phi_idx).GetInst());
147 phi->RemoveInput(phi_idx);
148 }
149 pre_header->AppendPhi(new_phi);
150 phi->AppendInput(new_phi);
151 }
152 }
153
UpdateControlFlowWithPreHeader(BasicBlock * header,BasicBlock * pre_header,const ArenaVector<int> & fw_edges_indexes)154 void LoopAnalyzer::UpdateControlFlowWithPreHeader(BasicBlock *header, BasicBlock *pre_header,
155 const ArenaVector<int> &fw_edges_indexes)
156 {
157 constexpr size_t IMM_2 = 2;
158 if (fw_edges_indexes.size() >= IMM_2) {
159 for (auto pred_idx : fw_edges_indexes) {
160 auto edge = header->GetPredBlockByIndex(pred_idx);
161 edge->ReplaceSucc(header, pre_header);
162 header->RemovePred(edge);
163 }
164 pre_header->AddSucc(header);
165 } else {
166 ASSERT(fw_edges_indexes.size() == 1);
167 auto edge = header->GetPredBlockByIndex(fw_edges_indexes[0]);
168 edge->ReplaceSucc(header, pre_header);
169 header->ReplacePred(edge, pre_header);
170 }
171 // Update RPO
172 GetGraph()->GetAnalysis<Rpo>().SetValid(true);
173 GetGraph()->GetAnalysis<Rpo>().AddBasicBlockBefore(header, pre_header);
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 fw_edges_indexes = GetForwardEdgesIndexes(header);
183 auto pre_header = header->CreateImmediateDominator();
184 pre_header->SetGuestPc(header->GetGuestPc());
185 if (fw_edges_indexes.size() >= 2U) {
186 MovePhiInputsToPreHeader(header, pre_header, fw_edges_indexes);
187 }
188 UpdateControlFlowWithPreHeader(header, pre_header, fw_edges_indexes);
189 return pre_header;
190 }
191
PreHeaderExists(Loop * loop)192 bool LoopAnalyzer::PreHeaderExists(Loop *loop)
193 {
194 auto header = loop->GetHeader();
195
196 return header->GetPredsBlocks().size() - loop->GetBackEdges().size() == 1 &&
197 header->GetDominator()->GetLoop() == loop->GetOuterLoop() &&
198 header->GetDominator() != GetGraph()->GetStartBlock();
199 }
200
201 /*
202 * Find all loop pre-headers. If loop doesn't have pre-header, insert it
203 */
FindAndInsertPreHeaders(Loop * loop)204 void LoopAnalyzer::FindAndInsertPreHeaders(Loop *loop)
205 {
206 ASSERT(loop != nullptr && loop->GetHeader() != nullptr);
207 auto header = loop->GetHeader();
208
209 if (loop->IsTryCatchLoop()) {
210 loop->SetPreHeader(nullptr);
211 } else if (!loop->IsIrreducible()) {
212 BasicBlock *pre_header = nullptr;
213 if (PreHeaderExists(loop)) {
214 pre_header = header->GetDominator();
215 } else {
216 pre_header = CreatePreHeader(header);
217 pre_header->CopyTryCatchProps(header);
218 loop->GetOuterLoop()->AppendBlock(pre_header);
219 }
220 loop->SetPreHeader(pre_header);
221 pre_header->SetNextLoop(loop);
222 }
223
224 for (auto inner_loop : loop->GetInnerLoops()) {
225 FindAndInsertPreHeaders(inner_loop);
226 }
227 }
228
229 /*
230 * Visiting existing loop headers to populate loops with blocks
231 * Search algorithm starts from the loop back edge and recursively adds all predecessors until loop header not found
232 */
PopulateLoops()233 void LoopAnalyzer::PopulateLoops()
234 {
235 for (auto it = GetGraph()->GetBlocksRPO().rbegin(); it != GetGraph()->GetBlocksRPO().rend(); it++) {
236 auto block = *it;
237 if (block->GetLoop() == nullptr || !block->IsLoopHeader()) {
238 continue;
239 }
240 auto loop = block->GetLoop();
241 if (loop->IsIrreducible()) {
242 // Add back-edges to the loop for further analysis
243 for (auto back_edge : loop->GetBackEdges()) {
244 if (back_edge->GetLoop() != loop) {
245 loop->AppendBlock(back_edge);
246 }
247 }
248 } else {
249 black_marker_ = GetGraph()->NewMarker();
250 block->SetMarker(black_marker_);
251 for (auto back_edge : loop->GetBackEdges()) {
252 NaturalLoopSearch(loop, back_edge);
253 }
254 GetGraph()->EraseMarker(black_marker_);
255 }
256 }
257
258 // Populate the root loop with blocks which are not assign to any loops
259 // Link all outer loops with the root loop
260 auto root_loop = GetGraph()->GetRootLoop();
261 for (auto block : GetGraph()->GetBlocksRPO()) {
262 if (block->GetLoop() == nullptr) {
263 root_loop->AppendBlock(block);
264 } else if (block->GetLoop()->GetOuterLoop() == nullptr) {
265 block->GetLoop()->SetOuterLoop(root_loop);
266 root_loop->AppendInnerLoop(block->GetLoop());
267 }
268 }
269 }
270
271 /*
272 * Depth-first search to find blocks in the loop.
273 * When a block is visited for the first time it is marked with a black marker, added to the loop
274 * (if it hasn't been already added to the inner loop), and search runs for all its predecessors.
275 * Header block is marked firstly to stop search on it.
276 */
NaturalLoopSearch(Loop * loop,BasicBlock * block)277 void LoopAnalyzer::NaturalLoopSearch(Loop *loop, BasicBlock *block)
278 {
279 if (!block->IsMarked(black_marker_)) {
280 block->SetMarker(black_marker_);
281
282 if (block->GetLoop() == nullptr) {
283 // `block` without assignment to any loop is found
284 loop->AppendBlock(block);
285 } else if (block->GetLoop()->GetHeader() != loop->GetHeader()) {
286 // `block` from an inner loop id found, because its header differs from searching loop header
287 if (block->GetLoop()->GetOuterLoop() == nullptr) {
288 // Link outer loop and inner loop
289 block->GetLoop()->SetOuterLoop(loop);
290 loop->AppendInnerLoop(block->GetLoop());
291 }
292 }
293
294 for (auto pred : block->GetPredsBlocks()) {
295 NaturalLoopSearch(loop, pred);
296 }
297 }
298 }
299
SearchInfiniteLoops(Loop * loop)300 void LoopAnalyzer::SearchInfiniteLoops(Loop *loop)
301 {
302 loop->CheckInfinity();
303 if (loop->IsInfinite()) {
304 GetGraph()->SetHasInfiniteLoop(true);
305 }
306 for (auto inner_loop : loop->GetInnerLoops()) {
307 SearchInfiniteLoops(inner_loop);
308 }
309 }
310
AppendBlock(BasicBlock * block)311 void Loop::AppendBlock(BasicBlock *block)
312 {
313 ASSERT(std::find(blocks_.begin(), blocks_.end(), block) == blocks_.end());
314 block->SetLoop(this);
315 blocks_.push_back(block);
316 }
317
RemoveBlock(BasicBlock * block)318 void Loop::RemoveBlock(BasicBlock *block)
319 {
320 ASSERT(block != GetHeader());
321 ASSERT(!HasBackEdge(block));
322 #ifndef NDEBUG
323 for (auto inner_loop : GetInnerLoops()) {
324 ASSERT(block != inner_loop->GetPreHeader());
325 }
326 #endif
327
328 auto block_it = std::find(blocks_.begin(), blocks_.end(), block);
329 ASSERT(block_it != blocks_.end());
330 blocks_.erase(block_it);
331 }
332
IsOsrLoop() const333 bool Loop::IsOsrLoop() const
334 {
335 return !IsRoot() && GetHeader()->IsOsrEntry();
336 }
337
IsTryCatchLoop() const338 bool Loop::IsTryCatchLoop() const
339 {
340 return !IsRoot() && GetHeader()->IsCatchBegin();
341 }
342
343 /*
344 * Check if this loop is inside other
345 */
IsInside(Loop * other)346 bool Loop::IsInside(Loop *other)
347 {
348 auto outer = this->GetOuterLoop();
349 while (outer != nullptr) {
350 if (outer == other) {
351 return true;
352 }
353 outer = outer->GetOuterLoop();
354 }
355 return false;
356 }
357
MoveHeaderToSucc()358 void Loop::MoveHeaderToSucc()
359 {
360 ASSERT(header_->GetSuccsBlocks().size() == 1);
361 header_ = header_->GetSuccessor(0);
362 ASSERT(header_->GetLoop() == this);
363 auto it = std::find(blocks_.begin(), blocks_.end(), header_);
364 ASSERT(it != blocks_.end());
365 std::swap(*it, *blocks_.begin());
366 }
367
CheckInfinity()368 void Loop::CheckInfinity()
369 {
370 is_infinite_ = false;
371 if (is_root_) {
372 return;
373 }
374 auto outer_loop = GetOuterLoop();
375 for (auto block : GetBlocks()) {
376 const auto &succs = block->GetSuccsBlocks();
377 bool has_exit = std::find_if(succs.begin(), succs.end(), [&outer_loop](const BasicBlock *bb) {
378 return bb->GetLoop() == outer_loop;
379 }) != succs.end();
380 if (has_exit) {
381 return;
382 }
383 }
384 is_infinite_ = true;
385 }
386
387 /*
388 * Find outside block for the loop with single back-edge exit
389 */
GetLoopOutsideSuccessor(Loop * loop)390 BasicBlock *GetLoopOutsideSuccessor(Loop *loop)
391 {
392 ASSERT(loop->GetBackEdges().size() == 1);
393 auto back_edge = loop->GetBackEdges()[0];
394 auto header_succ_idx = back_edge->GetSuccBlockIndex(loop->GetHeader());
395 ASSERT(back_edge->GetSuccsBlocks().size() == MAX_SUCCS_NUM);
396 auto outside_block = back_edge->GetSuccessor(1 - header_succ_idx);
397 ASSERT(outside_block != nullptr);
398 return outside_block;
399 }
400
401 /**
402 * Check if the loop block sequence meets the requirements:
403 * - there is only one back-edge;
404 * - there is only one exit-point - from the back-edge;
405 */
IsLoopSingleBackEdgeExitPoint(Loop * loop)406 bool IsLoopSingleBackEdgeExitPoint(Loop *loop)
407 {
408 ASSERT(loop != nullptr);
409 if (loop->IsIrreducible()) {
410 return false;
411 }
412 if (loop->GetBackEdges().size() != 1) {
413 return false;
414 }
415 auto back_edge = loop->GetBackEdges()[0];
416 // Check there are no side-exits
417 for (auto block : loop->GetBlocks()) {
418 if (block == back_edge) {
419 continue;
420 }
421 for (auto succ : block->GetSuccsBlocks()) {
422 if (succ->GetLoop() != loop) {
423 return false;
424 }
425 }
426 }
427 return true;
428 }
429 } // namespace panda::compiler
430