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