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