• 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 "linear_order.h"
17 #include "optimizer/analysis/loop_analyzer.h"
18 #include "optimizer/ir/basicblock.h"
19 #include "optimizer/ir/graph.h"
20 
21 namespace ark::compiler {
LinearOrder(Graph * graph)22 LinearOrder::LinearOrder(Graph *graph)
23     : Analysis(graph),
24       linearBlocks_(graph->GetAllocator()->Adapter()),
25       rpoBlocks_(graph->GetAllocator()->Adapter()),
26       reorderedBlocks_(graph->GetAllocator()->Adapter())
27 {
28 }
29 
HandleIfBlock(BasicBlock * ifTrueBlock,BasicBlock * nextBlock)30 void LinearOrder::HandleIfBlock(BasicBlock *ifTrueBlock, BasicBlock *nextBlock)
31 {
32     ASSERT(ifTrueBlock != nullptr && nextBlock != nullptr);
33     ASSERT(!ifTrueBlock->IsEmpty());
34     if (ifTrueBlock->GetTrueSuccessor() == nextBlock) {
35         // The following swap of successors could break loop analyzer results in the case of irreducible loop
36         GetGraph()->InvalidateAnalysis<LoopAnalyzer>();
37 
38         auto ifInst = ifTrueBlock->GetLastInst();
39         ifTrueBlock->SwapTrueFalseSuccessors<true>();
40         if (ifInst->GetOpcode() == Opcode::IfImm) {
41             ifInst->CastToIfImm()->InverseConditionCode();
42         } else if (ifInst->GetOpcode() == Opcode::If) {
43             ifInst->CastToIf()->InverseConditionCode();
44         } else if (ifInst->GetOpcode() == Opcode::AddOverflow) {
45             ifInst->CastToAddOverflow()->InverseConditionCode();
46         } else if (ifInst->GetOpcode() == Opcode::SubOverflow) {
47             ifInst->CastToSubOverflow()->InverseConditionCode();
48         } else {
49             LOG(FATAL, COMPILER) << "Unexpected `If` instruction: " << *ifInst;
50         }
51     } else if (ifTrueBlock->GetFalseSuccessor() != nextBlock && !ifTrueBlock->GetSuccessor(0)->IsEndBlock()) {
52         ifTrueBlock->SetNeedsJump(true);
53     }
54 }
55 
HandlePrevInstruction(BasicBlock * block,BasicBlock * prevBlock)56 void LinearOrder::HandlePrevInstruction(BasicBlock *block, BasicBlock *prevBlock)
57 {
58     ASSERT(block != nullptr && prevBlock != nullptr);
59     ASSERT(!prevBlock->NeedsJump());
60     if (!prevBlock->IsEmpty()) {
61         auto prevInst = prevBlock->GetLastInst();
62         switch (prevInst->GetOpcode()) {
63             case Opcode::IfImm:
64             case Opcode::If:
65             case Opcode::AddOverflow:
66             case Opcode::SubOverflow:
67                 ASSERT(prevBlock->GetSuccsBlocks().size() == MAX_SUCCS_NUM);
68                 HandleIfBlock(prevBlock, block);
69                 break;
70 
71             case Opcode::Throw:
72                 break;
73 
74             default:
75                 if (GetGraph()->IsAbcKit()) {
76                     ABCKIT_MODE_CHECK(prevInst->GetOpcode() == Opcode::Intrinsic &&
77                                           prevInst->CastToIntrinsic()->GetIntrinsicId() ==
78                                               RuntimeInterface::IntrinsicId::INTRINSIC_ABCKIT_THROW,
79                                       break);
80                 }
81                 ASSERT(prevBlock->GetSuccsBlocks().size() == 1 || prevBlock->IsTryBegin() || prevBlock->IsTryEnd());
82                 if (block != prevBlock->GetSuccessor(0) && !prevBlock->GetLastInst()->IsControlFlow()) {
83                     prevBlock->SetNeedsJump(true);
84                 }
85                 break;
86         }
87     } else if (!prevBlock->IsEndBlock() && block != prevBlock->GetSuccessor(0) &&
88                !prevBlock->GetSuccessor(0)->IsEndBlock()) {
89         ASSERT(prevBlock->GetSuccsBlocks().size() == 1 || prevBlock->IsTryEnd());
90         prevBlock->SetNeedsJump(true);
91     }
92 }
93 
AddSortedByPc(ArenaList<BasicBlock * > * rpoBlocks,BasicBlock * bb)94 static void AddSortedByPc(ArenaList<BasicBlock *> *rpoBlocks, BasicBlock *bb)
95 {
96     auto cmp = [](BasicBlock *lhs, BasicBlock *rhs) { return lhs->GetGuestPc() >= rhs->GetGuestPc(); };
97 
98     if (rpoBlocks->empty()) {
99         rpoBlocks->push_back(bb);
100         return;
101     }
102 
103     auto iter = rpoBlocks->end();
104     --iter;
105     while (true) {
106         if (cmp(bb, *iter)) {
107             rpoBlocks->insert(++iter, bb);
108             break;
109         }
110         if (iter == rpoBlocks->begin()) {
111             rpoBlocks->push_front(bb);
112             break;
113         }
114         --iter;
115     }
116 }
117 
118 template <class T>
MakeLinearOrder(const T & blocks)119 void LinearOrder::MakeLinearOrder(const T &blocks)
120 {
121     linearBlocks_.clear();
122     linearBlocks_.reserve(blocks.size());
123 
124     BasicBlock *prev = nullptr;
125     for (auto block : blocks) {
126         if (prev != nullptr) {
127             HandlePrevInstruction(block, prev);
128         }
129         linearBlocks_.push_back(block);
130         prev = block;
131     }
132 
133     if (prev != nullptr && !prev->IsEndBlock() && !prev->GetSuccessor(0)->IsEndBlock()) {
134         // Handle last block
135         ASSERT(prev->GetSuccsBlocks().size() == 1 || prev->IsIfBlock());
136         prev->SetNeedsJump(true);
137     }
138 }
139 
LeastLikelySuccessor(const BasicBlock * block)140 BasicBlock *LinearOrder::LeastLikelySuccessor(const BasicBlock *block)
141 {
142     auto leastLikelySuccessor = LeastLikelySuccessorByBranchCounter(block);
143     if (leastLikelySuccessor != nullptr) {
144         return leastLikelySuccessor;
145     }
146 
147     leastLikelySuccessor = LeastLikelySuccessorByPreference(block);
148     if (leastLikelySuccessor != nullptr) {
149         return leastLikelySuccessor;
150     }
151 
152     if (block->GetSuccsBlocks().size() != MAX_SUCCS_NUM) {
153         return nullptr;
154     }
155 
156     auto trueSucc = block->GetTrueSuccessor();
157     auto falseSucc = block->GetFalseSuccessor();
158     ASSERT(trueSucc != nullptr && falseSucc != nullptr);
159     if (falseSucc->IsMarked(blocksMarker_) != trueSucc->IsMarked(blocksMarker_)) {
160         return falseSucc->IsMarked(blocksMarker_) ? falseSucc : trueSucc;
161     }
162     return nullptr;
163 }
164 
LeastLikelySuccessorByBranchCounter(const BasicBlock * block)165 BasicBlock *LinearOrder::LeastLikelySuccessorByBranchCounter(const BasicBlock *block)
166 {
167     if (!g_options.IsCompilerFreqBasedBranchReorder()) {
168         return nullptr;
169     }
170 
171     if (block->GetSuccsBlocks().size() != MAX_SUCCS_NUM) {
172         return nullptr;
173     }
174 
175     auto counter0 = GetBranchCounter(block, true);
176     auto counter1 = GetBranchCounter(block, false);
177     if (counter0 > 0 || counter1 > 0) {
178         auto denom = std::max(counter0, counter1);
179         ASSERT(denom != 0);
180         // NOLINTNEXTLINE(readability-magic-numbers)
181         auto r = (counter0 - counter1) * 100 / denom;
182         if (std::abs(r) < g_options.GetCompilerFreqBasedBranchReorderThreshold()) {
183             return nullptr;
184         }
185         return r < 0 ? block->GetTrueSuccessor() : block->GetFalseSuccessor();
186     }
187 
188     return nullptr;
189 }
190 
GetBranchCounter(const BasicBlock * block,bool trueSucc)191 int64_t LinearOrder::GetBranchCounter(const BasicBlock *block, bool trueSucc)
192 {
193     auto counter = GetGraph()->GetBranchCounter(block, trueSucc);
194     if (counter > 0) {
195         return counter;
196     }
197     if (IsConditionChainCounter(block)) {
198         return GetConditionChainCounter(block, trueSucc);
199     }
200     return 0;
201 }
202 
IsConditionChainCounter(const BasicBlock * block)203 bool LinearOrder::IsConditionChainCounter(const BasicBlock *block)
204 {
205     auto lastInst = block->GetLastInst();
206     if (lastInst->GetOpcode() != Opcode::IfImm) {
207         return false;
208     }
209     auto lastInstInput = lastInst->GetInput(0).GetInst();
210     if (!lastInstInput->IsPhi()) {
211         return false;
212     }
213     for (auto &input : lastInstInput->GetInputs()) {
214         if (!input.GetInst()->IsConst()) {
215             return false;
216         }
217         if (input.GetInst()->GetType() != DataType::INT64) {
218             return false;
219         }
220         auto val = input.GetInst()->CastToConstant()->GetIntValue();
221         if (val != 0 && val != 1) {
222             return false;
223         }
224     }
225     return true;
226 }
227 
GetConditionChainCounter(const BasicBlock * block,bool trueSucc)228 int64_t LinearOrder::GetConditionChainCounter(const BasicBlock *block, bool trueSucc)
229 {
230     if (trueSucc != block->IsInverted()) {
231         return GetConditionChainTrueSuccessorCounter(block);
232     }
233 
234     return GetConditionChainFalseSuccessorCounter(block);
235 }
236 
GetConditionChainTrueSuccessorCounter(const BasicBlock * block)237 int64_t LinearOrder::GetConditionChainTrueSuccessorCounter(const BasicBlock *block)
238 {
239     auto lastInst = block->GetLastInst();
240     auto lastInstInput = lastInst->GetInput(0).GetInst();
241     int64_t counter = 0;
242     for (size_t i = 0; i < lastInstInput->GetInputsCount(); i++) {
243         auto input = lastInstInput->GetInput(i);
244         auto val = input.GetInst()->CastToConstant()->GetIntValue();
245         if (val != 1) {
246             continue;
247         }
248         auto bb = lastInstInput->GetBasicBlock();
249         auto pred = bb->GetPredBlockByIndex(i);
250         while (pred->GetSuccsBlocks().size() != MAX_SUCCS_NUM) {
251             bb = pred;
252             if (pred->GetPredsBlocks().empty()) {
253                 return 0;
254             }
255             pred = pred->GetPredBlockByIndex(0);
256         }
257         counter += GetGraph()->GetBranchCounter(pred, pred->GetTrueSuccessor() == bb);
258     }
259     return counter;
260 }
261 
GetConditionChainFalseSuccessorCounter(const BasicBlock * block)262 int64_t LinearOrder::GetConditionChainFalseSuccessorCounter(const BasicBlock *block)
263 {
264     auto lastInst = block->GetLastInst();
265     auto lastInstInput = lastInst->GetInput(0).GetInst();
266     auto bb = lastInstInput->GetBasicBlock();
267     BasicBlock *falsePred = nullptr;
268     for (size_t i = 0; i < lastInstInput->GetInputsCount(); i++) {
269         auto input = lastInstInput->GetInput(i);
270         auto val = input.GetInst()->CastToConstant()->GetIntValue();
271         if (val == 0) {
272             falsePred = bb->GetPredBlockByIndex(i);
273             break;
274         }
275     }
276     if (falsePred == nullptr) {
277         return 0;
278     }
279     while (falsePred->GetSuccsBlocks().size() != MAX_SUCCS_NUM) {
280         bb = falsePred;
281         if (bb->GetPredsBlocks().empty()) {
282             return 0;
283         }
284         falsePred = falsePred->GetPredBlockByIndex(0);
285     }
286     return GetGraph()->GetBranchCounter(falsePred, falsePred->GetTrueSuccessor() == bb);
287 }
288 
LeastLikelySuccessorByPreference(const BasicBlock * block)289 BasicBlock *LinearOrder::LeastLikelySuccessorByPreference(const BasicBlock *block)
290 {
291     if (block->GetSuccsBlocks().size() != MAX_SUCCS_NUM) {
292         return nullptr;
293     }
294 
295     auto lastInst = block->GetLastInst();
296     switch (lastInst->GetOpcode()) {
297         case Opcode::If: {
298             auto ifInst = lastInst->CastToIf();
299             if (ifInst->IsLikely()) {
300                 ASSERT(!ifInst->IsUnlikely());
301                 return block->GetFalseSuccessor();
302             }
303             if (ifInst->IsUnlikely()) {
304                 ASSERT(!ifInst->IsLikely());
305                 return block->GetTrueSuccessor();
306             }
307             return nullptr;
308         }
309         case Opcode::IfImm: {
310             auto ifimmInst = lastInst->CastToIfImm();
311             if (ifimmInst->IsLikely()) {
312                 ASSERT(!ifimmInst->IsUnlikely());
313                 return block->GetFalseSuccessor();
314             }
315             if (ifimmInst->IsUnlikely()) {
316                 ASSERT(!ifimmInst->IsLikely());
317                 return block->GetTrueSuccessor();
318             }
319             return nullptr;
320         }
321         default:
322             return nullptr;
323     }
324 }
325 // Similar to DFS but move least frequent branch to the end.
326 // First time method is called with defer_least_frequent=true template param which moves least likely successors to the
327 // end. After all most likely successors are processed call method with defer_least_frequent=false and process least
328 // frequent successors with DFS.
329 template <bool DEFER_LEAST_FREQUENT>
DFSAndDeferLeastFrequentBranches(BasicBlock * block,size_t * blocksCount)330 void LinearOrder::DFSAndDeferLeastFrequentBranches(BasicBlock *block, size_t *blocksCount)
331 {
332     ASSERT(block != nullptr);
333     block->SetMarker(marker_);
334 
335     auto leastLikelySuccessor = DEFER_LEAST_FREQUENT ? LeastLikelySuccessor(block) : nullptr;
336     if (leastLikelySuccessor == nullptr) {
337         for (auto succBlock : block->GetSuccsBlocks()) {
338             if (!succBlock->IsMarked(marker_)) {
339                 DFSAndDeferLeastFrequentBranches<DEFER_LEAST_FREQUENT>(succBlock, blocksCount);
340             }
341         }
342     } else {
343         linearBlocks_.push_back(leastLikelySuccessor);
344         auto mostLikelySuccessor =
345             leastLikelySuccessor == block->GetTrueSuccessor() ? block->GetFalseSuccessor() : block->GetTrueSuccessor();
346         if (!mostLikelySuccessor->IsMarked(marker_)) {
347             DFSAndDeferLeastFrequentBranches<DEFER_LEAST_FREQUENT>(mostLikelySuccessor, blocksCount);
348         }
349     }
350 
351     if constexpr (DEFER_LEAST_FREQUENT) {  // NOLINT(readability-braces-around-statements,bugprone-suspicious-semicolon)
352         for (auto succBlock : linearBlocks_) {
353             if (!succBlock->IsMarked(marker_)) {
354                 DFSAndDeferLeastFrequentBranches<false>(succBlock, blocksCount);
355             }
356         }
357         linearBlocks_.clear();
358     }
359 
360     ASSERT(blocksCount != nullptr && *blocksCount > 0);
361     reorderedBlocks_[--(*blocksCount)] = block;
362 }
363 
MarkSideExitsBlocks()364 void LinearOrder::MarkSideExitsBlocks()
365 {
366     auto endBlock = GetGraph()->GetEndBlock();
367     // Check on infinite loop
368     if (endBlock == nullptr) {
369         return;
370     }
371     for (auto predBlock : endBlock->GetPredsBlocks()) {
372         if (predBlock->IsEmpty() || predBlock->IsStartBlock()) {
373             continue;
374         }
375         ASSERT(predBlock->GetSuccsBlocks().size() == 1);
376         auto lastInst = predBlock->GetLastInst();
377         ASSERT(lastInst != nullptr);
378         if (lastInst->IsReturn()) {
379             continue;
380         }
381         predBlock->SetMarker(blocksMarker_);
382     }
383 }
384 
DumpUnreachableBlocks()385 void LinearOrder::DumpUnreachableBlocks()
386 {
387     std::cerr << "There are unreachable blocks:\n";
388     for (auto bb : *GetGraph()) {
389         if (bb != nullptr && !bb->IsMarked(marker_)) {
390             bb->Dump(&std::cerr);
391         }
392     }
393     UNREACHABLE();
394 }
395 
RunImpl()396 bool LinearOrder::RunImpl()
397 {
398     if (GetGraph()->IsBytecodeOptimizer()) {
399         // Make blocks order sorted by bytecode PC
400         rpoBlocks_.clear();
401         for (auto bb : GetGraph()->GetBlocksRPO()) {
402             ASSERT(bb->GetGuestPc() != INVALID_PC);
403             AddSortedByPc(&rpoBlocks_, bb);
404         }
405         MakeLinearOrder(rpoBlocks_);
406     } else {
407         marker_ = GetGraph()->NewMarker();
408         blocksMarker_ = GetGraph()->NewMarker();
409         size_t blocksCount = GetGraph()->GetAliveBlocksCount();
410         linearBlocks_.clear();
411         reorderedBlocks_.clear();
412         reorderedBlocks_.resize(blocksCount);
413         MarkSideExitsBlocks();
414         DFSAndDeferLeastFrequentBranches<true>(GetGraph()->GetStartBlock(), &blocksCount);
415 #ifndef NDEBUG
416         if (blocksCount != 0) {
417             DumpUnreachableBlocks();
418         }
419 #endif  // NDEBUG
420         MakeLinearOrder(reorderedBlocks_);
421 
422         GetGraph()->EraseMarker(marker_);
423         GetGraph()->EraseMarker(blocksMarker_);
424     }
425     return true;
426 }
427 }  // namespace ark::compiler
428