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 "branch_elimination.h"
17 #include "compiler_logger.h"
18 #include "optimizer/analysis/loop_analyzer.h"
19 #include "optimizer/analysis/rpo.h"
20 #include "optimizer/ir/basicblock.h"
21 
22 namespace panda::compiler {
23 /**
24  * Branch Elimination optimization finds `if-true` blocks with resolvable conditional instruction.
25  * Condition can be resolved in the following ways:
26  * - Condition is constant;
27  */
BranchElimination(Graph *graph)28 BranchElimination::BranchElimination(Graph *graph)
29     : Optimization(graph)
30 {
31 }
32 
RunImpl()33 bool BranchElimination::RunImpl()
34 {
35     GetGraph()->RunPass<DominatorsTree>();
36     isApplied_ = false;
37     auto markerHolder = MarkerHolder(GetGraph());
38     rmBlockMarker_ = markerHolder.GetMarker();
39     for (auto block : GetGraph()->GetBlocksRPO()) {
40         if (block->IsEmpty() || (block->IsTry() && GetGraph()->IsBytecodeOptimizer())) {
41             continue;
42         }
43         if (block->GetLastInst()->GetOpcode() == Opcode::IfImm) {
44             VisitBlock(block);
45         }
46     }
47     DisconnectBlocks();
48 
49     COMPILER_LOG(DEBUG, BRANCH_ELIM) << "Branch elimination complete";
50     return isApplied_;
51 }
52 
InvalidateAnalyses()53 void BranchElimination::InvalidateAnalyses()
54 {
55     GetGraph()->InvalidateAnalysis<LoopAnalyzer>();
56     InvalidateBlocksOrderAnalyzes(GetGraph());
57 }
58 
BranchEliminationConst(BasicBlock *ifBlock)59 void BranchElimination::BranchEliminationConst(BasicBlock *ifBlock)
60 {
61     auto ifImm = ifBlock->GetLastInst()->CastToIfImm();
62     auto conditionInst = ifBlock->GetLastInst()->GetInput(0).GetInst();
63     COMPILER_LOG(DEBUG, BRANCH_ELIM) << "Block with constant if instruction input is visited, id = "
64                                      << ifBlock->GetId();
65 
66     uint64_t constValue = conditionInst->CastToConstant()->GetIntValue();
67     bool condResult = (constValue == ifImm->GetImm());
68     if (ifImm->GetCc() == CC_NE) {
69         condResult = !condResult;
70     } else {
71         ASSERT(ifImm->GetCc() == CC_EQ);
72     }
73     auto eliminatedSuccessor = ifBlock->GetFalseSuccessor();
74     if (!condResult) {
75         eliminatedSuccessor = ifBlock->GetTrueSuccessor();
76     }
77     EliminateBranch(ifBlock, eliminatedSuccessor);
78     GetGraph()->GetEventWriter().EventBranchElimination(ifBlock->GetId(), ifBlock->GetGuestPc(), conditionInst->GetId(),
79                                                         conditionInst->GetPc(), "const-condition",
80                                                         eliminatedSuccessor == ifBlock->GetTrueSuccessor());
81 }
82 
BranchEliminationIntrinsic(BasicBlock *ifBlock)83 void BranchElimination::BranchEliminationIntrinsic(BasicBlock *ifBlock)
84 {
85     auto ifImm = ifBlock->GetLastInst()->CastToIfImm();
86     auto conditionInst = ifBlock->GetLastInst()->GetInput(0).GetInst()->CastToIntrinsic();
87     if (conditionInst->CastToIntrinsic()->GetIntrinsicId() != IntrinsicInst::IntrinsicId::LDTRUE &&
88         conditionInst->CastToIntrinsic()->GetIntrinsicId() != IntrinsicInst::IntrinsicId::LDFALSE) {
89         return;
90     }
91 
92     COMPILER_LOG(DEBUG, BRANCH_ELIM) << "Block with intrinsic ldtrue/ldfalse if instruction input is visited, id = "
93                                      << ifBlock->GetId();
94 
95     bool constValue = conditionInst->GetIntrinsicId() == IntrinsicInst::IntrinsicId::LDTRUE;
96     ASSERT(ifImm->GetImm() == 0);
97     bool condResult = (constValue == ifImm->GetImm());
98     if (ifImm->GetCc() == CC_NE) {
99         condResult = !condResult;
100     } else {
101         ASSERT(ifImm->GetCc() == CC_EQ);
102     }
103     auto eliminatedSuccessor = ifBlock->GetFalseSuccessor();
104     if (!condResult) {
105         eliminatedSuccessor = ifBlock->GetTrueSuccessor();
106     }
107     EliminateBranch(ifBlock, eliminatedSuccessor);
108     GetGraph()->GetEventWriter().EventBranchElimination(ifBlock->GetId(), ifBlock->GetGuestPc(), conditionInst->GetId(),
109                                                         conditionInst->GetPc(), "const-condition",
110                                                         eliminatedSuccessor == ifBlock->GetTrueSuccessor());
111 }
112 
113 /**
114  * Select unreachable successor and run elimination process
115  * @param blocks - list of blocks with constant `if` instruction input
116  */
VisitBlock(BasicBlock *ifBlock)117 void BranchElimination::VisitBlock(BasicBlock *ifBlock)
118 {
119     ASSERT(ifBlock != nullptr);
120     ASSERT(ifBlock->GetGraph() == GetGraph());
121     ASSERT(ifBlock->GetLastInst()->GetOpcode() == Opcode::IfImm);
122     ASSERT(ifBlock->GetSuccsBlocks().size() == MAX_SUCCS_NUM);
123 
124     auto conditionInst = ifBlock->GetLastInst()->GetInput(0).GetInst();
125     switch (conditionInst->GetOpcode()) {
126         case Opcode::Constant:
127             BranchEliminationConst(ifBlock);
128             break;
129         case Opcode::Intrinsic:
130             BranchEliminationIntrinsic(ifBlock);
131             break;
132         default:
133             break;
134     }
135 }
136 
137 /**
138  * If `eliminated_block` has no other predecessor than `if_block`, mark `eliminated_block` to be disconnected
139  * If `eliminated_block` dominates all predecessors, exclude `if_block`, mark `eliminated_block` to be disconneted
140  * Else remove edge between these blocks
141  * @param if_block - block with constant 'if' instruction input
142  * @param eliminated_block - unreachable form `if_block`
143  */
EliminateBranch(BasicBlock *ifBlock, BasicBlock *eliminatedBlock)144 void BranchElimination::EliminateBranch(BasicBlock *ifBlock, BasicBlock *eliminatedBlock)
145 {
146     ASSERT(ifBlock != nullptr && ifBlock->GetGraph() == GetGraph());
147     ASSERT(eliminatedBlock != nullptr && eliminatedBlock->GetGraph() == GetGraph());
148     // find predecessor which is not dominated by `eliminated_block`
149     auto preds = eliminatedBlock->GetPredsBlocks();
150     auto it = std::find_if(preds.begin(), preds.end(), [ifBlock, eliminatedBlock](BasicBlock *pred) {
151         return pred != ifBlock && !eliminatedBlock->IsDominate(pred);
152     });
153     bool dominatesAllPreds = (it == preds.cend());
154     if (preds.size() > 1 && !dominatesAllPreds) {
155         RemovePredecessorUpdateDF(eliminatedBlock, ifBlock);
156         ifBlock->RemoveSucc(eliminatedBlock);
157         ifBlock->RemoveInst(ifBlock->GetLastInst());
158         GetGraph()->GetAnalysis<Rpo>().SetValid(true);
159         // NOTE (a.popov) DominatorsTree could be restored inplace
160         GetGraph()->RunPass<DominatorsTree>();
161     } else {
162         eliminatedBlock->SetMarker(rmBlockMarker_);
163     }
164     isApplied_ = true;
165 }
166 
167 /**
168  * Before disconnecting the `block` find and disconnect all its successors dominated by it.
169  * Use DFS to disconnect blocks in the PRO
170  * @param block - unreachable block to disconnect from the graph
171  */
MarkUnreachableBlocks(BasicBlock *block)172 void BranchElimination::MarkUnreachableBlocks(BasicBlock *block)
173 {
174     for (auto dom : block->GetDominatedBlocks()) {
175         dom->SetMarker(rmBlockMarker_);
176         MarkUnreachableBlocks(dom);
177     }
178 }
179 
AllPredecessorsMarked(BasicBlock *block, Marker marker)180 bool AllPredecessorsMarked(BasicBlock *block, Marker marker)
181 {
182     if (block->GetPredsBlocks().empty()) {
183         ASSERT(block->IsStartBlock());
184         return false;
185     }
186 
187     for (auto pred : block->GetPredsBlocks()) {
188         if (!pred->IsMarked(marker)) {
189             return false;
190         }
191     }
192     block->SetMarker(marker);
193     return true;
194 }
195 
196 /// Disconnect selected blocks
DisconnectBlocks()197 void BranchElimination::DisconnectBlocks()
198 {
199     for (auto block : GetGraph()->GetBlocksRPO()) {
200         if (block->IsMarked(rmBlockMarker_) || AllPredecessorsMarked(block, rmBlockMarker_)) {
201             MarkUnreachableBlocks(block);
202         }
203     }
204 
205     const auto &rpoBlocks = GetGraph()->GetBlocksRPO();
206     for (auto it = rpoBlocks.rbegin(); it != rpoBlocks.rend(); it++) {
207         auto block = *it;
208         if (block != nullptr && block->IsMarked(rmBlockMarker_)) {
209             GetGraph()->DisconnectBlock(block);
210             COMPILER_LOG(DEBUG, BRANCH_ELIM) << "Block was disconnected, id = " << block->GetId();
211         }
212     }
213 }
214 }  // namespace panda::compiler
215