1 /*
2 * Copyright (c) 2021-2025 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/if_merging.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/analysis.h"
24
25 namespace ark::compiler {
IfMerging(Graph * graph)26 IfMerging::IfMerging(Graph *graph) : Optimization(graph) {}
27
RunImpl()28 bool IfMerging::RunImpl()
29 {
30 GetGraph()->RunPass<DominatorsTree>();
31 // Do not try to fix LoopAnalyzer during optimization
32 GetGraph()->InvalidateAnalysis<LoopAnalyzer>();
33 isApplied_ = false;
34 trueBranchMarker_ = GetGraph()->NewMarker();
35 ArenaVector<BasicBlock *> blocks(GetGraph()->GetVectorBlocks(), GetGraph()->GetLocalAllocator()->Adapter());
36 for (auto block : blocks) {
37 if (block == nullptr) {
38 continue;
39 }
40 if (TryMergeEquivalentIfs(block) || TrySimplifyConstantPhi(block)) {
41 isApplied_ = true;
42 }
43 }
44 GetGraph()->RemoveUnreachableBlocks();
45
46 #ifndef NDEBUG
47 CheckDomTreeValid();
48 #endif
49 GetGraph()->EraseMarker(trueBranchMarker_);
50 COMPILER_LOG(DEBUG, IF_MERGING) << "If merging complete";
51 return isApplied_;
52 }
53
InvalidateAnalyses()54 void IfMerging::InvalidateAnalyses()
55 {
56 GetGraph()->InvalidateAnalysis<BoundsAnalysis>();
57 GetGraph()->InvalidateAnalysis<AliasAnalysis>();
58 GetGraph()->InvalidateAnalysis<LoopAnalyzer>();
59 InvalidateBlocksOrderAnalyzes(GetGraph());
60 }
61
62 // Splits block while it contains If comparing phi with constant inputs to another constant
63 // (After removing one If and joining blocks a new If may go to this block)
TrySimplifyConstantPhi(BasicBlock * block)64 bool IfMerging::TrySimplifyConstantPhi(BasicBlock *block)
65 {
66 auto isApplied = false;
67 while (TryRemoveConstantPhiIf(block)) {
68 isApplied = true;
69 }
70 return isApplied;
71 }
72
73 // Tries to remove If that compares phi with constant inputs to another constant
TryRemoveConstantPhiIf(BasicBlock * ifBlock)74 bool IfMerging::TryRemoveConstantPhiIf(BasicBlock *ifBlock)
75 {
76 auto ifImm = GetIfImm(ifBlock);
77 if (ifImm == nullptr || ifBlock->GetGraph() == nullptr || ifBlock->IsTry()) {
78 return false;
79 }
80 auto input = ifImm->GetInput(0).GetInst();
81 if (input->GetOpcode() != Opcode::Compare) {
82 return false;
83 }
84 auto compare = input->CastToCompare();
85 auto lhs = compare->GetInput(0).GetInst();
86 auto rhs = compare->GetInput(1).GetInst();
87 if (rhs->IsConst() && lhs->GetOpcode() == Opcode::Phi &&
88 TryRemoveConstantPhiIf(ifImm, lhs->CastToPhi(), rhs->CastToConstant()->GetRawValue(), compare->GetCc())) {
89 return true;
90 }
91 if (lhs->IsConst() && rhs->GetOpcode() == Opcode::Phi &&
92 TryRemoveConstantPhiIf(ifImm, rhs->CastToPhi(), lhs->CastToConstant()->GetRawValue(),
93 SwapOperandsConditionCode(compare->GetCc()))) {
94 return true;
95 }
96 return false;
97 }
98
99 // Returns IfImm instruction terminating the block or nullptr
GetIfImm(BasicBlock * block)100 IfImmInst *IfMerging::GetIfImm(BasicBlock *block)
101 {
102 if (block->IsEmpty() || block->GetLastInst()->GetOpcode() != Opcode::IfImm) {
103 return nullptr;
104 }
105 auto ifImm = block->GetLastInst()->CastToIfImm();
106 if ((ifImm->GetCc() != CC_EQ && ifImm->GetCc() != CC_NE) || ifImm->GetImm() != 0) {
107 return nullptr;
108 }
109 return ifImm;
110 }
111
112 // If bb and its dominator end with the same IfImm instruction, removes IfImm in bb and returns true
TryMergeEquivalentIfs(BasicBlock * bb)113 bool IfMerging::TryMergeEquivalentIfs(BasicBlock *bb)
114 {
115 auto ifImm = GetIfImm(bb);
116 if (ifImm == nullptr || bb->GetGraph() == nullptr || bb->IsTry()) {
117 return false;
118 }
119 if (bb->GetPredsBlocks().size() != MAX_SUCCS_NUM) {
120 return false;
121 }
122 auto dom = bb->GetDominator();
123 auto domIf = GetIfImm(dom);
124 if (domIf == nullptr || domIf->GetInput(0).GetInst() != ifImm->GetInput(0).GetInst()) {
125 return false;
126 }
127 auto invertedIf = IsIfInverted(bb, domIf);
128 if (invertedIf == std::nullopt) {
129 // Not applied, true and false branches of dominate If intersect
130 return false;
131 }
132 auto trueBb = ifImm->GetEdgeIfInputTrue();
133 auto falseBb = ifImm->GetEdgeIfInputFalse();
134 if (!MarkInstBranches(bb, trueBb, falseBb)) {
135 // Not applied, there are instructions in bb used both in true and false branches
136 return false;
137 }
138 COMPILER_LOG(DEBUG, IF_MERGING) << "Equivalent IfImm instructions were found. Dominant block id = " << dom->GetId()
139 << ", dominated block id = " << bb->GetId();
140 bb->RemoveInst(ifImm);
141 SplitBlockWithEquivalentIf(bb, trueBb, *invertedIf);
142 return true;
143 }
144
145 // In case If has constant phi input and can be removed, removes it and returns true
TryRemoveConstantPhiIf(IfImmInst * ifImm,PhiInst * phi,uint64_t constant,ConditionCode cc)146 bool IfMerging::TryRemoveConstantPhiIf(IfImmInst *ifImm, PhiInst *phi, uint64_t constant, ConditionCode cc)
147 {
148 auto bb = phi->GetBasicBlock();
149 if (bb != ifImm->GetBasicBlock()) {
150 return false;
151 }
152 for (auto input : phi->GetInputs()) {
153 if (!input.GetInst()->IsConst()) {
154 return false;
155 }
156 }
157 auto trueBb = ifImm->GetEdgeIfInputTrue();
158 auto falseBb = ifImm->GetEdgeIfInputFalse();
159 if (!MarkInstBranches(bb, trueBb, falseBb)) {
160 // Not applied, there are instructions in bb used both in true and false branches
161 return false;
162 }
163 for (auto inst : bb->PhiInsts()) {
164 if (inst != phi) {
165 return false;
166 }
167 }
168
169 COMPILER_LOG(DEBUG, IF_MERGING) << "Comparison of constant and Phi instruction with constant inputs was found. "
170 << "Phi id = " << phi->GetId() << ", IfImm id = " << ifImm->GetId();
171 bb->RemoveInst(ifImm);
172 SplitBlockWithConstantPhi(bb, trueBb, phi, constant, cc);
173 return true;
174 }
175
176 // Marks instructions in bb for moving to true or false branch
177 // Returns true if it was possible
MarkInstBranches(BasicBlock * bb,BasicBlock * trueBb,BasicBlock * falseBb)178 bool IfMerging::MarkInstBranches(BasicBlock *bb, BasicBlock *trueBb, BasicBlock *falseBb)
179 {
180 for (auto inst : bb->AllInstsSafeReverse()) {
181 if (inst->IsNotRemovable() && inst != bb->GetLastInst()) {
182 return false;
183 }
184 auto trueBranch = false;
185 auto falseBranch = false;
186 for (auto &user : inst->GetUsers()) {
187 auto userBranch = GetUserBranch(user.GetInst(), bb, trueBb, falseBb);
188 if (!userBranch) {
189 // If instruction has users not in true or false branch, than splitting is
190 // impossible (even if the instruction is Phi)
191 return false;
192 }
193 if (*userBranch) {
194 trueBranch = true;
195 } else {
196 falseBranch = true;
197 }
198 }
199 if (inst->IsPhi()) {
200 // Phi instruction will be replaced with one of its inputs
201 continue;
202 }
203 if (trueBranch && falseBranch) {
204 // If instruction is used in both branches, it would need to be copied -> not applied
205 return false;
206 }
207 if (trueBranch) {
208 inst->SetMarker(trueBranchMarker_);
209 } else {
210 inst->ResetMarker(trueBranchMarker_);
211 }
212 }
213 return true;
214 }
215
GetUserBranch(Inst * userInst,BasicBlock * bb,BasicBlock * trueBb,BasicBlock * falseBb)216 std::optional<bool> IfMerging::GetUserBranch(Inst *userInst, BasicBlock *bb, BasicBlock *trueBb, BasicBlock *falseBb)
217 {
218 auto userBb = userInst->GetBasicBlock();
219 if (userBb == bb) {
220 return userInst->IsMarked(trueBranchMarker_);
221 }
222 if (IsDominateEdge(trueBb, userBb)) {
223 // user_inst can be executed only in true branch of If
224 return true;
225 }
226 if (IsDominateEdge(falseBb, userBb)) {
227 // user_inst can be executed only in false branch of If
228 return false;
229 }
230 // user_inst can be executed both in true and false branches
231 return std::nullopt;
232 }
233
234 // Returns true if edge_bb is always the first block in the path
235 // from its predecessor to target_bb
IsDominateEdge(BasicBlock * edgeBb,BasicBlock * targetBb)236 bool IfMerging::IsDominateEdge(BasicBlock *edgeBb, BasicBlock *targetBb)
237 {
238 ASSERT(edgeBb != nullptr);
239 return edgeBb->IsDominate(targetBb) && edgeBb->GetPredsBlocks().size() == 1;
240 }
241
SplitBlockWithEquivalentIf(BasicBlock * bb,BasicBlock * trueBb,bool invertedIf)242 void IfMerging::SplitBlockWithEquivalentIf(BasicBlock *bb, BasicBlock *trueBb, bool invertedIf)
243 {
244 auto trueBranchBb = SplitBlock(bb);
245 // Phi instructions are replaced with one of their inputs
246 for (auto inst : bb->PhiInstsSafe()) {
247 for (auto it = inst->GetUsers().begin(); it != inst->GetUsers().end(); it = inst->GetUsers().begin()) {
248 auto userInst = it->GetInst();
249 auto userBb = userInst->GetBasicBlock();
250 auto phiInputBlockIndex = (userBb == trueBranchBb || IsDominateEdge(trueBb, userBb)) ? 0 : 1;
251 if (invertedIf) {
252 phiInputBlockIndex = 1 - phiInputBlockIndex;
253 }
254 userInst->ReplaceInput(inst, inst->CastToPhi()->GetPhiInput(bb->GetPredecessor(phiInputBlockIndex)));
255 }
256 bb->RemoveInst(inst);
257 }
258
259 // True branches of both Ifs are disconnected from bb and connected to the new block
260 trueBb->ReplacePred(bb, trueBranchBb);
261 bb->RemoveSucc(trueBb);
262 auto truePred = bb->GetPredecessor(invertedIf ? 1 : 0);
263 truePred->ReplaceSucc(bb, trueBranchBb);
264 bb->RemovePred(truePred);
265
266 FixDominatorsTree(trueBranchBb, bb);
267 }
268
SplitBlockWithConstantPhi(BasicBlock * bb,BasicBlock * trueBb,PhiInst * phi,uint64_t constant,ConditionCode cc)269 void IfMerging::SplitBlockWithConstantPhi(BasicBlock *bb, BasicBlock *trueBb, PhiInst *phi, uint64_t constant,
270 ConditionCode cc)
271 {
272 if (trueBb == bb) {
273 // Avoid case when true_bb is bb itself
274 trueBb = bb->InsertNewBlockToSuccEdge(trueBb);
275 }
276 auto trueBranchBb = SplitBlock(bb);
277 // Move Phi inputs corresponding to true branch to a new Phi instruction
278 auto trueBranchPhi = GetGraph()->CreateInstPhi(phi->GetType(), phi->GetPc());
279 for (auto i = phi->GetInputsCount(); i > 0U; --i) {
280 auto inst = phi->GetInput(i - 1).GetInst();
281 if (Compare(cc, inst->CastToConstant()->GetRawValue(), constant)) {
282 auto bbIndex = phi->GetPhiInputBbNum(i - 1);
283 phi->RemoveInput(i - 1);
284 bb->GetPredBlockByIndex(bbIndex)->ReplaceSucc(bb, trueBranchBb);
285 bb->RemovePred(bbIndex);
286 trueBranchPhi->AppendInput(inst);
287 }
288 }
289 for (auto it = phi->GetUsers().begin(); it != phi->GetUsers().end();) {
290 auto userInst = it->GetInst();
291 auto userBb = userInst->GetBasicBlock();
292 // Skip list nodes that can be deleted inside ReplaceInput
293 while (it != phi->GetUsers().end() && it->GetInst() == userInst) {
294 ++it;
295 }
296 if (userBb == trueBranchBb || IsDominateEdge(trueBb, userBb)) {
297 userInst->ReplaceInput(phi, trueBranchPhi);
298 }
299 }
300 // Phi instructions with single inputs need to be removed
301 if (trueBranchPhi->GetInputsCount() == 1) {
302 trueBranchPhi->ReplaceUsers(trueBranchPhi->GetInput(0).GetInst());
303 trueBranchPhi->RemoveInputs();
304 } else {
305 trueBranchBb->AppendPhi(trueBranchPhi);
306 }
307 if (phi->GetInputsCount() == 1) {
308 phi->ReplaceUsers(phi->GetInput(0).GetInst());
309 bb->RemoveInst(phi);
310 }
311
312 // True branches of both Ifs are disconnected from bb and connected to the new block
313 trueBb->ReplacePred(bb, trueBranchBb);
314 bb->RemoveSucc(trueBb);
315 FixDominatorsTree(trueBranchBb, bb);
316 }
317
318 // Moves instructions that should be in the true branch to a new block and returns it
SplitBlock(BasicBlock * bb)319 BasicBlock *IfMerging::SplitBlock(BasicBlock *bb)
320 {
321 auto trueBranchBb = GetGraph()->CreateEmptyBlock(bb->GetGuestPc());
322 // Dominators tree will be fixed after connecting the new block
323 GetGraph()->GetAnalysis<DominatorsTree>().SetValid(true);
324 for (auto inst : bb->InstsSafeReverse()) {
325 if (inst->IsMarked(trueBranchMarker_)) {
326 bb->EraseInst(inst);
327 ASSERT(trueBranchBb != nullptr);
328 trueBranchBb->PrependInst(inst);
329 }
330 }
331 return trueBranchBb;
332 }
333
334 // Makes dominator tree valid after false_branch_bb was split into two blocks
FixDominatorsTree(BasicBlock * trueBranchBb,BasicBlock * falseBranchBb)335 void IfMerging::FixDominatorsTree(BasicBlock *trueBranchBb, BasicBlock *falseBranchBb)
336 {
337 auto &domTree = GetGraph()->GetAnalysis<DominatorsTree>();
338 auto dom = falseBranchBb->GetDominator();
339 // Dominator of bb will remain (maybe not immediate) dominator of blocks dominated by bb
340 for (auto dominatedBlock : falseBranchBb->GetDominatedBlocks()) {
341 domTree.SetDomPair(dom, dominatedBlock);
342 }
343 falseBranchBb->ClearDominatedBlocks();
344
345 TryJoinSuccessorBlock(trueBranchBb);
346 TryJoinSuccessorBlock(falseBranchBb);
347 TryUpdateDominator(trueBranchBb);
348 TryUpdateDominator(falseBranchBb);
349 }
350
TryJoinSuccessorBlock(BasicBlock * bb)351 void IfMerging::TryJoinSuccessorBlock(BasicBlock *bb)
352 {
353 if (bb->GetSuccsBlocks().size() == 1 && bb->GetSuccessor(0)->GetPredsBlocks().size() == 1 &&
354 !bb->GetSuccessor(0)->IsPseudoControlFlowBlock() && bb->IsTry() == bb->GetSuccessor(0)->IsTry() &&
355 bb->GetSuccessor(0) != bb) {
356 bb->JoinSuccessorBlock();
357 }
358 }
359
360 // If bb has only one precessor, set it as dominator of bb
TryUpdateDominator(BasicBlock * bb)361 void IfMerging::TryUpdateDominator(BasicBlock *bb)
362 {
363 if (bb->GetPredsBlocks().size() != 1) {
364 return;
365 }
366 auto pred = bb->GetPredecessor(0);
367 auto dom = bb->GetDominator();
368 if (dom != pred) {
369 if (dom != nullptr) {
370 dom->RemoveDominatedBlock(bb);
371 }
372 GetGraph()->GetAnalysis<DominatorsTree>().SetDomPair(pred, bb);
373 }
374 }
375
376 #ifndef NDEBUG
377 // Checks if dominators in dominators tree are indeed dominators (but not necessary immediate)
CheckDomTreeValid()378 void IfMerging::CheckDomTreeValid()
379 {
380 ArenaVector<BasicBlock *> dominators(GetGraph()->GetVectorBlocks().size(),
381 GetGraph()->GetLocalAllocator()->Adapter());
382 GetGraph()->GetAnalysis<DominatorsTree>().SetValid(true);
383 for (auto block : GetGraph()->GetBlocksRPO()) {
384 dominators[block->GetId()] = block->GetDominator();
385 }
386 GetGraph()->InvalidateAnalysis<DominatorsTree>();
387 GetGraph()->RunPass<DominatorsTree>();
388
389 for (auto block : GetGraph()->GetBlocksRPO()) {
390 auto dom = dominators[block->GetId()];
391 ASSERT_DO(dom == nullptr || dom->IsDominate(block),
392 (std::cerr << "Basic block with id " << block->GetId() << " has incorrect dominator with id "
393 << dom->GetId() << std::endl));
394 }
395 }
396 #endif
397 } // namespace ark::compiler
398