• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2023 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/analysis/alias_analysis.h"
17 #include "optimizer/optimizations/branch_elimination.h"
18 #include "compiler_logger.h"
19 #include "optimizer/analysis/bounds_analysis.h"
20 #include "optimizer/analysis/dominators_tree.h"
21 #include "optimizer/analysis/rpo.h"
22 #include "optimizer/analysis/loop_analyzer.h"
23 #include "optimizer/ir/basicblock.h"
24 #include "optimizer/ir/inst.h"
25 #include "optimizer/ir/analysis.h"
26 
27 namespace panda::compiler {
28 /**
29  * Branch Elimination optimization finds `if-true` blocks with resolvable conditional instruction.
30  * Condition can be resolved in the following ways:
31  * - Condition is constant;
32  * - Condition is dominated by the equal one condition with the same inputs and the only one successor of the dominant
33  * reaches dominated condition
34  */
BranchElimination(Graph * graph)35 BranchElimination::BranchElimination(Graph *graph)
36     : Optimization(graph),
37       sameInputCompares_(graph->GetLocalAllocator()->Adapter()),
38       sameInputCompareAnyType_(graph->GetLocalAllocator()->Adapter())
39 {
40 }
41 
RunImpl()42 bool BranchElimination::RunImpl()
43 {
44     GetGraph()->RunPass<DominatorsTree>();
45     isApplied_ = false;
46     sameInputCompares_.clear();
47     auto markerHolder = MarkerHolder(GetGraph());
48     rmBlockMarker_ = markerHolder.GetMarker();
49     for (auto block : GetGraph()->GetBlocksRPO()) {
50         if (block->IsEmpty() || (block->IsTry() && GetGraph()->IsBytecodeOptimizer())) {
51             continue;
52         }
53         if (block->GetLastInst()->GetOpcode() == Opcode::IfImm) {
54             if (SkipForOsr(block)) {
55                 COMPILER_LOG(DEBUG, BRANCH_ELIM) << "Skip for OSR, id = " << block->GetId();
56                 continue;
57             }
58             /* skip branch() elimination at the end of the
59              * preheader until LoopUnrolling pass is done */
60             if (!GetGraph()->IsBytecodeOptimizer() && g_options.IsCompilerDeferPreheaderTransform() &&
61                 !GetGraph()->IsUnrollComplete() && block->IsLoopValid() && block->IsLoopPreHeader()) {
62                 continue;
63             }
64             VisitBlock(block);
65         }
66     }
67     DisconnectBlocks();
68 
69     if (isApplied_ && GetGraph()->IsOsrMode()) {
70         CleanupGraphSaveStateOSR(GetGraph());
71     }
72     COMPILER_LOG(DEBUG, BRANCH_ELIM) << "Branch elimination complete";
73     return isApplied_;
74 }
75 
SkipForOsr(const BasicBlock * block)76 bool BranchElimination::SkipForOsr(const BasicBlock *block)
77 {
78     if (!block->IsLoopValid()) {
79         return false;
80     }
81     auto loop = block->GetLoop();
82     if (loop->IsRoot() || !loop->GetHeader()->IsOsrEntry()) {
83         return false;
84     }
85     if (loop->GetBackEdges().size() > 1) {
86         return false;
87     }
88     return block->IsDominate(loop->GetBackEdges()[0]);
89 }
90 
InvalidateAnalyses()91 void BranchElimination::InvalidateAnalyses()
92 {
93     GetGraph()->InvalidateAnalysis<BoundsAnalysis>();
94     GetGraph()->InvalidateAnalysis<AliasAnalysis>();
95     // Before in "CleanupGraphSaveStateOSR" already run "LoopAnalyzer"
96     // in case (is_applied_ && GetGraph()->IsOsrMode())
97     if (!(isApplied_ && GetGraph()->IsOsrMode())) {
98         GetGraph()->InvalidateAnalysis<LoopAnalyzer>();
99     }
100     InvalidateBlocksOrderAnalyzes(GetGraph());
101 }
102 
BranchEliminationConst(BasicBlock * ifBlock)103 void BranchElimination::BranchEliminationConst(BasicBlock *ifBlock)
104 {
105     auto ifImm = ifBlock->GetLastInst()->CastToIfImm();
106     auto conditionInst = ifBlock->GetLastInst()->GetInput(0).GetInst();
107     COMPILER_LOG(DEBUG, BRANCH_ELIM) << "Block with constant if instruction input is visited, id = "
108                                      << ifBlock->GetId();
109 
110     uint64_t constValue = conditionInst->CastToConstant()->GetIntValue();
111     bool condResult = (constValue == ifImm->GetImm());
112     if (ifImm->GetCc() == CC_NE) {
113         condResult = !condResult;
114     } else {
115         ASSERT(ifImm->GetCc() == CC_EQ);
116     }
117     auto eliminatedSuccessor = ifBlock->GetFalseSuccessor();
118     if (!condResult) {
119         eliminatedSuccessor = ifBlock->GetTrueSuccessor();
120     }
121     EliminateBranch(ifBlock, eliminatedSuccessor);
122     GetGraph()->GetEventWriter().EventBranchElimination(ifBlock->GetId(), ifBlock->GetGuestPc(), conditionInst->GetId(),
123                                                         conditionInst->GetPc(), "const-condition",
124                                                         eliminatedSuccessor == ifBlock->GetTrueSuccessor());
125 }
126 
BranchEliminationCompare(BasicBlock * ifBlock)127 void BranchElimination::BranchEliminationCompare(BasicBlock *ifBlock)
128 {
129     auto ifImm = ifBlock->GetLastInst()->CastToIfImm();
130     auto conditionInst = ifBlock->GetLastInst()->GetInput(0).GetInst();
131     if (auto result = GetConditionResult(conditionInst)) {
132         COMPILER_LOG(DEBUG, BRANCH_ELIM) << "Compare instruction result was resolved. Instruction id = "
133                                          << conditionInst->GetId()
134                                          << ", resolved result: " << (result.value() ? "true" : "false");
135         auto eliminatedSuccessor = ifImm->GetEdgeIfInputFalse();
136         if (!result.value()) {
137             eliminatedSuccessor = ifImm->GetEdgeIfInputTrue();
138         }
139         EliminateBranch(ifBlock, eliminatedSuccessor);
140         GetGraph()->GetEventWriter().EventBranchElimination(
141             ifBlock->GetId(), ifBlock->GetGuestPc(), conditionInst->GetId(), conditionInst->GetPc(), "dominant-if",
142             eliminatedSuccessor == ifBlock->GetTrueSuccessor());
143     } else {
144         ConditionOps ops {conditionInst->GetInput(0).GetInst(), conditionInst->GetInput(1).GetInst()};
145         auto it = sameInputCompares_.try_emplace(ops, GetGraph()->GetLocalAllocator()->Adapter());
146         it.first->second.push_back(conditionInst);
147     }
148 }
149 
BranchEliminationCompareAnyType(BasicBlock * ifBlock)150 void BranchElimination::BranchEliminationCompareAnyType(BasicBlock *ifBlock)
151 {
152     auto ifImm = ifBlock->GetLastInst()->CastToIfImm();
153     auto compareAny = ifBlock->GetLastInst()->GetInput(0).GetInst()->CastToCompareAnyType();
154     if (auto result = GetCompareAnyTypeResult(ifImm)) {
155         COMPILER_LOG(DEBUG, BRANCH_ELIM) << "CompareAnyType instruction result was resolved. Instruction id = "
156                                          << compareAny->GetId()
157                                          << ", resolved result: " << (result.value() ? "true" : "false");
158         auto eliminatedSuccessor = ifImm->GetEdgeIfInputFalse();
159         if (!result.value()) {
160             eliminatedSuccessor = ifImm->GetEdgeIfInputTrue();
161         }
162         EliminateBranch(ifBlock, eliminatedSuccessor);
163         GetGraph()->GetEventWriter().EventBranchElimination(ifBlock->GetId(), ifBlock->GetGuestPc(),
164                                                             compareAny->GetId(), compareAny->GetPc(), "dominant-if",
165                                                             eliminatedSuccessor == ifBlock->GetTrueSuccessor());
166         return;
167     }
168 
169     Inst *input = compareAny->GetInput(0).GetInst();
170     auto it = sameInputCompareAnyType_.try_emplace(input, GetGraph()->GetLocalAllocator()->Adapter());
171     it.first->second.push_back(compareAny);
172 }
173 
174 /**
175  * Select unreachable successor and run elimination process
176  * @param blocks - list of blocks with constant `if` instruction input
177  */
VisitBlock(BasicBlock * ifBlock)178 void BranchElimination::VisitBlock(BasicBlock *ifBlock)
179 {
180     ASSERT(ifBlock != nullptr);
181     ASSERT(ifBlock->GetGraph() == GetGraph());
182     ASSERT(ifBlock->GetLastInst()->GetOpcode() == Opcode::IfImm);
183     ASSERT(ifBlock->GetSuccsBlocks().size() == MAX_SUCCS_NUM);
184 
185     auto conditionInst = ifBlock->GetLastInst()->GetInput(0).GetInst();
186     switch (conditionInst->GetOpcode()) {
187         case Opcode::Constant:
188             BranchEliminationConst(ifBlock);
189             break;
190         case Opcode::Compare:
191             BranchEliminationCompare(ifBlock);
192             break;
193         case Opcode::CompareAnyType:
194             BranchEliminationCompareAnyType(ifBlock);
195             break;
196         default:
197             break;
198     }
199 }
200 
201 /**
202  * If `eliminated_block` has no other predecessor than `if_block`, mark `eliminated_block` to be disconnected
203  * If `eliminated_block` dominates all predecessors, exclude `if_block`, mark `eliminated_block` to be disconneted
204  * Else remove edge between these blocks
205  * @param if_block - block with constant 'if' instruction input
206  * @param eliminated_block - unreachable form `if_block`
207  */
EliminateBranch(BasicBlock * ifBlock,BasicBlock * eliminatedBlock)208 void BranchElimination::EliminateBranch(BasicBlock *ifBlock, BasicBlock *eliminatedBlock)
209 {
210     ASSERT(ifBlock != nullptr && ifBlock->GetGraph() == GetGraph());
211     ASSERT(eliminatedBlock != nullptr && eliminatedBlock->GetGraph() == GetGraph());
212     // find predecessor which is not dominated by `eliminated_block`
213     auto preds = eliminatedBlock->GetPredsBlocks();
214     auto it = std::find_if(preds.begin(), preds.end(), [ifBlock, eliminatedBlock](BasicBlock *pred) {
215         return pred != ifBlock && !eliminatedBlock->IsDominate(pred);
216     });
217     bool dominatesAllPreds = (it == preds.cend());
218     if (preds.size() > 1 && !dominatesAllPreds) {
219         RemovePredecessorUpdateDF(eliminatedBlock, ifBlock);
220         ifBlock->RemoveSucc(eliminatedBlock);
221         ifBlock->RemoveInst(ifBlock->GetLastInst());
222         GetGraph()->GetAnalysis<Rpo>().SetValid(true);
223         // NOTE (a.popov) DominatorsTree could be restored inplace
224         GetGraph()->RunPass<DominatorsTree>();
225     } else {
226         eliminatedBlock->SetMarker(rmBlockMarker_);
227     }
228     isApplied_ = true;
229 }
230 
231 /**
232  * Before disconnecting the `block` find and disconnect all its successors dominated by it.
233  * Use DFS to disconnect blocks in the PRO
234  * @param block - unreachable block to disconnect from the graph
235  */
MarkUnreachableBlocks(BasicBlock * block)236 void BranchElimination::MarkUnreachableBlocks(BasicBlock *block)
237 {
238     for (auto dom : block->GetDominatedBlocks()) {
239         dom->SetMarker(rmBlockMarker_);
240         MarkUnreachableBlocks(dom);
241     }
242 }
243 
AllPredecessorsMarked(BasicBlock * block,Marker marker)244 bool AllPredecessorsMarked(BasicBlock *block, Marker marker)
245 {
246     if (block->GetPredsBlocks().empty()) {
247         ASSERT(block->IsStartBlock());
248         return false;
249     }
250 
251     for (auto pred : block->GetPredsBlocks()) {
252         if (!pred->IsMarked(marker)) {
253             return false;
254         }
255     }
256     block->SetMarker(marker);
257     return true;
258 }
259 
260 /// Disconnect selected blocks
DisconnectBlocks()261 void BranchElimination::DisconnectBlocks()
262 {
263     for (auto block : GetGraph()->GetBlocksRPO()) {
264         if (block->IsMarked(rmBlockMarker_) || AllPredecessorsMarked(block, rmBlockMarker_)) {
265             MarkUnreachableBlocks(block);
266         }
267     }
268 
269     const auto &rpoBlocks = GetGraph()->GetBlocksRPO();
270     for (auto it = rpoBlocks.rbegin(); it != rpoBlocks.rend(); it++) {
271         auto block = *it;
272         if (block != nullptr && block->IsMarked(rmBlockMarker_)) {
273             GetGraph()->DisconnectBlock(block);
274             COMPILER_LOG(DEBUG, BRANCH_ELIM) << "Block was disconnected, id = " << block->GetId();
275         }
276     }
277 }
278 
279 /**
280  * Check if the `target_block` is reachable from one successor only of the `dominant_block`
281  *
282  * If `target_block` is dominated by one of the successors, need to check that `target_block`
283  * is NOT reachable by the other successor
284  */
BlockIsReachedFromOnlySuccessor(BasicBlock * targetBlock,BasicBlock * dominantBlock)285 bool BlockIsReachedFromOnlySuccessor(BasicBlock *targetBlock, BasicBlock *dominantBlock)
286 {
287     ASSERT(dominantBlock->IsDominate(targetBlock));
288     BasicBlock *otherSuccesor = nullptr;
289     if (dominantBlock->GetTrueSuccessor()->IsDominate(targetBlock)) {
290         otherSuccesor = dominantBlock->GetFalseSuccessor();
291     } else if (dominantBlock->GetFalseSuccessor()->IsDominate(targetBlock)) {
292         otherSuccesor = dominantBlock->GetTrueSuccessor();
293     } else {
294         return false;
295     }
296 
297     auto markerHolder = MarkerHolder(targetBlock->GetGraph());
298     if (BlocksPathDfsSearch(markerHolder.GetMarker(), otherSuccesor, targetBlock)) {
299         return false;
300     }
301     ASSERT(!otherSuccesor->IsDominate(targetBlock));
302     return true;
303 }
304 
305 /**
306  * NOTE (a.popov) Here can be supported more complex case:
307  * when `dom_compare` has 2 or more `if_imm` users and `target_compare` is reachable from the same successors of these
308  * if_imms
309  */
FindIfImmDominatesCondition(Inst * domCompare,Inst * targetCompare)310 Inst *FindIfImmDominatesCondition(Inst *domCompare, Inst *targetCompare)
311 {
312     for (auto &user : domCompare->GetUsers()) {
313         auto inst = user.GetInst();
314         if (inst->GetOpcode() == Opcode::IfImm && inst->IsDominate(targetCompare)) {
315             return inst;
316         }
317     }
318     return nullptr;
319 }
320 
321 /// Resolve condition result if there is a dominant equal condition
GetConditionResult(Inst * condition)322 std::optional<bool> BranchElimination::GetConditionResult(Inst *condition)
323 {
324     ConditionOps ops {condition->GetInput(0).GetInst(), condition->GetInput(1).GetInst()};
325     if (sameInputCompares_.count(ops) <= 0) {
326         return std::nullopt;
327     }
328     auto instructions = sameInputCompares_.at(ops);
329     ASSERT(!instructions.empty());
330     for (auto domCond : instructions) {
331         // Find dom_cond's if_imm, that dominates target condition
332         auto ifImm = FindIfImmDominatesCondition(domCond, condition);
333         if (ifImm == nullptr) {
334             continue;
335         }
336         if (BlockIsReachedFromOnlySuccessor(condition->GetBasicBlock(), ifImm->GetBasicBlock())) {
337             if (auto result = TryResolveResult(condition, domCond, ifImm->CastToIfImm())) {
338                 COMPILER_LOG(DEBUG, BRANCH_ELIM)
339                     << "Equal compare instructions were found. Dominant id = " << domCond->GetId()
340                     << ", dominated id = " << condition->GetId();
341                 return result;
342             }
343         }
344     }
345     return std::nullopt;
346 }
347 
348 /// Resolve condition result if there is a dominant IfImmInst after CompareAnyTypeInst.
GetCompareAnyTypeResult(IfImmInst * ifImm)349 std::optional<bool> BranchElimination::GetCompareAnyTypeResult(IfImmInst *ifImm)
350 {
351     auto compareAny = ifImm->GetInput(0).GetInst()->CastToCompareAnyType();
352     Inst *input = compareAny->GetInput(0).GetInst();
353     const auto it = sameInputCompareAnyType_.find(input);
354     if (it == sameInputCompareAnyType_.end()) {
355         return std::nullopt;
356     }
357 
358     const ArenaVector<CompareAnyTypeInst *> &instructions = it->second;
359     ASSERT(!instructions.empty());
360     for (const auto domCompareAny : instructions) {
361         // Find dom_cond's if_imm, that dominates target condition.
362         auto ifImmDomBlock = FindIfImmDominatesCondition(domCompareAny, ifImm);
363         if (ifImmDomBlock == nullptr) {
364             continue;
365         }
366 
367         if (!BlockIsReachedFromOnlySuccessor(ifImm->GetBasicBlock(), ifImmDomBlock->GetBasicBlock())) {
368             continue;
369         }
370 
371         auto result = TryResolveCompareAnyTypeResult(compareAny, domCompareAny, ifImmDomBlock->CastToIfImm());
372         if (!result) {
373             continue;
374         }
375 
376         COMPILER_LOG(DEBUG, BRANCH_ELIM) << "Equal CompareAnyType instructions were found. Dominant id = "
377                                          << domCompareAny->GetId() << ", dominated id = " << compareAny->GetId();
378         return result;
379     }
380     return std::nullopt;
381 }
382 
383 /**
384  * Try to resolve CompareAnyTypeInst result with the information
385  * about dominant CompareAnyTypeInst with the same inputs.
386  */
TryResolveCompareAnyTypeResult(CompareAnyTypeInst * compareAny,CompareAnyTypeInst * domCompareAny,IfImmInst * ifImmDomBlock)387 std::optional<bool> BranchElimination::TryResolveCompareAnyTypeResult(CompareAnyTypeInst *compareAny,
388                                                                       CompareAnyTypeInst *domCompareAny,
389                                                                       IfImmInst *ifImmDomBlock)
390 {
391     auto compareAnyBb = compareAny->GetBasicBlock();
392     bool isTrueDomBranch = ifImmDomBlock->GetEdgeIfInputTrue()->IsDominate(compareAnyBb);
393 
394     auto graph = compareAnyBb->GetGraph();
395     auto language = graph->GetRuntime()->GetMethodSourceLanguage(graph->GetMethod());
396     auto res = IsAnyTypeCanBeSubtypeOf(language, compareAny->GetAnyType(), domCompareAny->GetAnyType());
397     if (!res) {
398         // We cannot compare types in compile-time
399         return std::nullopt;
400     }
401 
402     if (res.value()) {
403         // If CompareAnyTypeInst has the same types for any, then it can be optimized in any case.
404         return isTrueDomBranch;
405     }
406 
407     // If CompareAnyTypeInst has the different types for any, then it can be optimized only in true-branch.
408     if (!isTrueDomBranch) {
409         return std::nullopt;
410     }
411 
412     return false;
413 }
414 
415 /**
416  * Try to resolve condition result with the information about dominant condition with the same inputs
417  *
418  * - The result of the dominant condition is counted using dom-tree by checking which successor of if_imm_block
419  * (true/false) dominates the current block;
420  *
421  * - Then this result is applied to the current condition, if it is possible, using the table of condition codes
422  * relation
423  */
TryResolveResult(Inst * condition,Inst * dominantCondition,IfImmInst * ifImm)424 std::optional<bool> BranchElimination::TryResolveResult(Inst *condition, Inst *dominantCondition, IfImmInst *ifImm)
425 {
426     using std::nullopt;
427 
428     // Table keeps the result of the condition in the row if the condition in the column is true
429     // Order must be same as in "ir/inst.h"
430     static constexpr std::array<std::array<std::optional<bool>, ConditionCode::CC_LAST + 1>, ConditionCode::CC_LAST + 1>
431         // clang-format off
432         COND_RELATION = {{
433         //  CC_EQ     CC_NE    CC_LT    CC_LE    CC_GT    CC_GE    CC_B     CC_BE    CC_A     CC_AE
434             {true,    false,   false,   nullopt, false,   nullopt, false,   nullopt, false,   nullopt}, // CC_EQ
435             {false,   true,    true,    nullopt, true,    nullopt, true,    nullopt, true,    nullopt}, // CC_NE
436             {false,   nullopt, true,    nullopt, false,   false,   nullopt, nullopt, nullopt, nullopt}, // CC_LT
437             {true,    nullopt, true,    true,    false,   nullopt, nullopt, nullopt, nullopt, nullopt}, // CC_LE
438             {false,   nullopt, false,   false,   true,    nullopt, nullopt, nullopt, nullopt, nullopt}, // CC_GT
439             {true,    nullopt, false,   nullopt, true,    true,    nullopt, nullopt, nullopt, nullopt}, // CC_GE
440             {false,   nullopt, nullopt, nullopt, nullopt, nullopt, true,    nullopt, false,   false},   // CC_B
441             {true,    nullopt, nullopt, nullopt, nullopt, nullopt, true,    true,    false,   nullopt}, // CC_BE
442             {false,   nullopt, nullopt, nullopt, nullopt, nullopt, false,   false,   true,    nullopt}, // CC_A
443             {true,    nullopt, nullopt, nullopt, nullopt, nullopt, false,   nullopt, true,    true},    // CC_AE
444         }};
445     // clang-format on
446 
447     auto dominantCc = dominantCondition->CastToCompare()->GetCc();
448     // Swap the dominant condition code, if inputs are reversed: 'if (a < b)' -> 'if (b > a)'
449     if (condition->GetInput(0) != dominantCondition->GetInput(0)) {
450         ASSERT(condition->GetInput(0) == dominantCondition->GetInput(1));
451         dominantCc = SwapOperandsConditionCode(dominantCc);
452     }
453 
454     // Reverse the `dominant_cc` if the `condition` is reached after branching the false succesor of the
455     // if_imm's basic block
456     auto conditionBb = condition->GetBasicBlock();
457     if (ifImm->GetEdgeIfInputFalse()->IsDominate(conditionBb)) {
458         dominantCc = GetInverseConditionCode(dominantCc);
459     } else {
460         ASSERT(ifImm->GetEdgeIfInputTrue()->IsDominate(conditionBb));
461     }
462     // After these transformations dominant condition with current `dominant_cc` is equal to `true`
463     // So `condition` result is resolved via table
464     return COND_RELATION[condition->CastToCompare()->GetCc()][dominantCc];
465 }
466 }  // namespace panda::compiler
467