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