• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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