• 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/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 panda::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     return edgeBb->IsDominate(targetBb) && edgeBb->GetPredsBlocks().size() == 1;
239 }
240 
SplitBlockWithEquivalentIf(BasicBlock * bb,BasicBlock * trueBb,bool invertedIf)241 void IfMerging::SplitBlockWithEquivalentIf(BasicBlock *bb, BasicBlock *trueBb, bool invertedIf)
242 {
243     auto trueBranchBb = SplitBlock(bb);
244     // Phi instructions are replaced with one of their inputs
245     for (auto inst : bb->PhiInstsSafe()) {
246         for (auto it = inst->GetUsers().begin(); it != inst->GetUsers().end(); it = inst->GetUsers().begin()) {
247             auto userInst = it->GetInst();
248             auto userBb = userInst->GetBasicBlock();
249             auto phiInputBlockIndex = (userBb == trueBranchBb || IsDominateEdge(trueBb, userBb)) ? 0 : 1;
250             if (invertedIf) {
251                 phiInputBlockIndex = 1 - phiInputBlockIndex;
252             }
253             userInst->ReplaceInput(inst, inst->CastToPhi()->GetPhiInput(bb->GetPredecessor(phiInputBlockIndex)));
254         }
255         bb->RemoveInst(inst);
256     }
257 
258     // True branches of both Ifs are disconnected from bb and connected to the new block
259     trueBb->ReplacePred(bb, trueBranchBb);
260     bb->RemoveSucc(trueBb);
261     auto truePred = bb->GetPredecessor(invertedIf ? 1 : 0);
262     truePred->ReplaceSucc(bb, trueBranchBb);
263     bb->RemovePred(truePred);
264 
265     FixDominatorsTree(trueBranchBb, bb);
266 }
267 
SplitBlockWithConstantPhi(BasicBlock * bb,BasicBlock * trueBb,PhiInst * phi,uint64_t constant,ConditionCode cc)268 void IfMerging::SplitBlockWithConstantPhi(BasicBlock *bb, BasicBlock *trueBb, PhiInst *phi, uint64_t constant,
269                                           ConditionCode cc)
270 {
271     if (trueBb == bb) {
272         // Avoid case when true_bb is bb itself
273         trueBb = bb->InsertNewBlockToSuccEdge(trueBb);
274     }
275     auto trueBranchBb = SplitBlock(bb);
276     // Move Phi inputs corresponding to true branch to a new Phi instruction
277     auto trueBranchPhi = GetGraph()->CreateInstPhi(phi->GetType(), phi->GetPc());
278     for (auto i = phi->GetInputsCount(); i > 0U; --i) {
279         auto inst = phi->GetInput(i - 1).GetInst();
280         if (Compare(cc, inst->CastToConstant()->GetRawValue(), constant)) {
281             auto bbIndex = phi->GetPhiInputBbNum(i - 1);
282             phi->RemoveInput(i - 1);
283             bb->GetPredBlockByIndex(bbIndex)->ReplaceSucc(bb, trueBranchBb);
284             bb->RemovePred(bbIndex);
285             trueBranchPhi->AppendInput(inst);
286         }
287     }
288     for (auto it = phi->GetUsers().begin(); it != phi->GetUsers().end();) {
289         auto userInst = it->GetInst();
290         auto userBb = userInst->GetBasicBlock();
291         // Skip list nodes that can be deleted inside ReplaceInput
292         while (it != phi->GetUsers().end() && it->GetInst() == userInst) {
293             ++it;
294         }
295         if (userBb == trueBranchBb || IsDominateEdge(trueBb, userBb)) {
296             userInst->ReplaceInput(phi, trueBranchPhi);
297         }
298     }
299     // Phi instructions with single inputs need to be removed
300     if (trueBranchPhi->GetInputsCount() == 1) {
301         trueBranchPhi->ReplaceUsers(trueBranchPhi->GetInput(0).GetInst());
302         trueBranchPhi->RemoveInputs();
303     } else {
304         trueBranchBb->AppendPhi(trueBranchPhi);
305     }
306     if (phi->GetInputsCount() == 1) {
307         phi->ReplaceUsers(phi->GetInput(0).GetInst());
308         bb->RemoveInst(phi);
309     }
310 
311     // True branches of both Ifs are disconnected from bb and connected to the new block
312     trueBb->ReplacePred(bb, trueBranchBb);
313     bb->RemoveSucc(trueBb);
314     FixDominatorsTree(trueBranchBb, bb);
315 }
316 
317 // Moves instructions that should be in the true branch to a new block and returns it
SplitBlock(BasicBlock * bb)318 BasicBlock *IfMerging::SplitBlock(BasicBlock *bb)
319 {
320     auto trueBranchBb = GetGraph()->CreateEmptyBlock(bb->GetGuestPc());
321     // Dominators tree will be fixed after connecting the new block
322     GetGraph()->GetAnalysis<DominatorsTree>().SetValid(true);
323     for (auto inst : bb->InstsSafeReverse()) {
324         if (inst->IsMarked(trueBranchMarker_)) {
325             bb->EraseInst(inst);
326             trueBranchBb->PrependInst(inst);
327         }
328     }
329     return trueBranchBb;
330 }
331 
332 // Makes dominator tree valid after false_branch_bb was split into two blocks
FixDominatorsTree(BasicBlock * trueBranchBb,BasicBlock * falseBranchBb)333 void IfMerging::FixDominatorsTree(BasicBlock *trueBranchBb, BasicBlock *falseBranchBb)
334 {
335     auto &domTree = GetGraph()->GetAnalysis<DominatorsTree>();
336     auto dom = falseBranchBb->GetDominator();
337     // Dominator of bb will remain (maybe not immediate) dominator of blocks dominated by bb
338     for (auto dominatedBlock : falseBranchBb->GetDominatedBlocks()) {
339         domTree.SetDomPair(dom, dominatedBlock);
340     }
341     falseBranchBb->ClearDominatedBlocks();
342 
343     TryJoinSuccessorBlock(trueBranchBb);
344     TryJoinSuccessorBlock(falseBranchBb);
345     TryUpdateDominator(trueBranchBb);
346     TryUpdateDominator(falseBranchBb);
347 }
348 
TryJoinSuccessorBlock(BasicBlock * bb)349 void IfMerging::TryJoinSuccessorBlock(BasicBlock *bb)
350 {
351     if (bb->GetSuccsBlocks().size() == 1 && bb->GetSuccessor(0)->GetPredsBlocks().size() == 1 &&
352         !bb->GetSuccessor(0)->IsPseudoControlFlowBlock() && bb->IsTry() == bb->GetSuccessor(0)->IsTry() &&
353         bb->GetSuccessor(0) != bb) {
354         bb->JoinSuccessorBlock();
355     }
356 }
357 
358 // If bb has only one precessor, set it as dominator of bb
TryUpdateDominator(BasicBlock * bb)359 void IfMerging::TryUpdateDominator(BasicBlock *bb)
360 {
361     if (bb->GetPredsBlocks().size() != 1) {
362         return;
363     }
364     auto pred = bb->GetPredecessor(0);
365     auto dom = bb->GetDominator();
366     if (dom != pred) {
367         if (dom != nullptr) {
368             dom->RemoveDominatedBlock(bb);
369         }
370         GetGraph()->GetAnalysis<DominatorsTree>().SetDomPair(pred, bb);
371     }
372 }
373 
374 #ifndef NDEBUG
375 // Checks if dominators in dominators tree are indeed dominators (but not necessary immediate)
CheckDomTreeValid()376 void IfMerging::CheckDomTreeValid()
377 {
378     ArenaVector<BasicBlock *> dominators(GetGraph()->GetVectorBlocks().size(),
379                                          GetGraph()->GetLocalAllocator()->Adapter());
380     GetGraph()->GetAnalysis<DominatorsTree>().SetValid(true);
381     for (auto block : GetGraph()->GetBlocksRPO()) {
382         dominators[block->GetId()] = block->GetDominator();
383     }
384     GetGraph()->InvalidateAnalysis<DominatorsTree>();
385     GetGraph()->RunPass<DominatorsTree>();
386 
387     for (auto block : GetGraph()->GetBlocksRPO()) {
388         auto dom = dominators[block->GetId()];
389         ASSERT_DO(dom == nullptr || dom->IsDominate(block),
390                   (std::cerr << "Basic block with id " << block->GetId() << " has incorrect dominator with id "
391                              << dom->GetId() << std::endl));
392     }
393 }
394 #endif
395 }  // namespace panda::compiler
396