• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "compiler_logger.h"
17 #include "optimizer/analysis/dominators_tree.h"
18 #include "optimizer/analysis/loop_analyzer.h"
19 #include "optimizer/ir/basicblock.h"
20 #include "optimizer/ir/inst.h"
21 #include "optimizer/optimizations/cleanup.h"
22 #include "optimizer/optimizations/try_catch_resolving.h"
23 
24 namespace ark::compiler {
TryCatchResolving(Graph * graph)25 TryCatchResolving::TryCatchResolving(Graph *graph)
26     : Optimization(graph),
27       tryBlocks_(graph->GetLocalAllocator()->Adapter()),
28       throwInsts_(graph->GetLocalAllocator()->Adapter()),
29       throwInsts0_(graph->GetLocalAllocator()->Adapter()),
30       catchBlocks_(graph->GetLocalAllocator()->Adapter()),
31       phiInsts_(graph->GetLocalAllocator()->Adapter()),
32       cphi2phi_(graph->GetLocalAllocator()->Adapter()),
33       catch2cphis_(graph->GetLocalAllocator()->Adapter())
34 {
35 }
36 
RunImpl()37 bool TryCatchResolving::RunImpl()
38 {
39     COMPILER_LOG(DEBUG, TRY_CATCH_RESOLVING) << "Running try-catch-resolving";
40     for (auto bb : GetGraph()->GetBlocksRPO()) {
41         if (bb->IsTryBegin()) {
42             tryBlocks_.emplace_back(bb);
43         }
44     }
45     if (!g_options.IsCompilerNonOptimizing()) {
46         CollectCandidates();
47         if (!catchBlocks_.empty() && !throwInsts_.empty()) {
48             ConnectThrowCatch();
49         }
50     }
51     for (auto bb : tryBlocks_) {
52         COMPILER_LOG(DEBUG, TRY_CATCH_RESOLVING) << "Visit try-begin BB " << bb->GetId();
53         VisitTryInst(GetTryBeginInst(bb));
54     }
55     if (!g_options.IsCompilerNonOptimizing() && !GetGraph()->IsAotMode()) {
56         if (!throwInsts0_.empty()) {
57             DeoptimizeIfs();
58         }
59     }
60     GetGraph()->RemoveUnreachableBlocks();
61     GetGraph()->ClearTryCatchInfo();
62     InvalidateAnalyses();
63     // Cleanup should be done inside pass, to satisfy GraphChecker
64     GetGraph()->RunPass<Cleanup>();
65     COMPILER_LOG(DEBUG, TRY_CATCH_RESOLVING) << "Finishing try-catch-resolving";
66     return true;
67 }
68 
DeoptimizeIfs()69 void TryCatchResolving::DeoptimizeIfs()
70 {
71     for (auto thr0w : throwInsts0_) {
72         auto *tryBB = thr0w->GetBasicBlock();
73         if (tryBB->GetPredsBlocks().size() != 1) {
74             continue;
75         }
76         auto pred = tryBB->GetPredBlockByIndex(0);
77         auto lastInst = pred->GetLastInst();
78         if (lastInst == nullptr || lastInst->GetOpcode() != Opcode::IfImm) {
79             continue;
80         }
81         auto ifImm = lastInst->CastToIfImm();
82         ASSERT(ifImm->GetCc() == ConditionCode::CC_NE || ifImm->GetCc() == ConditionCode::CC_EQ);
83         ASSERT(ifImm->GetImm() == 0);
84         auto saveState = tryBB->GetFirstInst();
85         if (saveState == nullptr || saveState->GetOpcode() != Opcode::SaveState) {
86             continue;
87         }
88         auto cc = ifImm->GetCc();
89         if (tryBB == pred->GetFalseSuccessor()) {
90             cc = GetInverseConditionCode(cc);
91         }
92         auto compInst = GetGraph()->CreateInstCompare(DataType::BOOL, ifImm->GetPc(), ifImm->GetInput(0).GetInst(),
93                                                       GetGraph()->FindOrCreateConstant(0), DataType::BOOL, cc);
94 #ifdef PANDA_COMPILER_DEBUG_INFO
95         compInst->SetCurrentMethod(ifImm->GetCurrentMethod());
96 #endif
97         saveState->RemoveUsers<false>();
98         tryBB->EraseInst(saveState, true);
99         auto deoptimizeIf =
100             GetGraph()->CreateInstDeoptimizeIf(saveState->GetPc(), compInst, saveState, DeoptimizeType::IFIMM_TRY);
101         deoptimizeIf->SetInput(0, compInst);
102         deoptimizeIf->SetInput(1, saveState);
103         lastInst->InsertBefore(compInst);
104         lastInst->InsertBefore(saveState);
105         lastInst->InsertBefore(deoptimizeIf);
106         COMPILER_LOG(DEBUG, TRY_CATCH_RESOLVING) << "IfImm " << lastInst->GetId() << " BB " << pred->GetId()
107                                                  << " is replaced by DeoptimizeIf " << deoptimizeIf->GetId();
108         pred->RemoveInst(lastInst);
109         pred->RemoveSucc(tryBB);
110         tryBB->RemovePred(pred);
111     }
112 }
113 
FindCatchBeginBlock(BasicBlock * bb)114 BasicBlock *TryCatchResolving::FindCatchBeginBlock(BasicBlock *bb)
115 {
116     for (auto pred : bb->GetPredsBlocks()) {
117         if (pred->IsCatchBegin()) {
118             return pred;
119         }
120     }
121     return nullptr;
122 }
123 
CollectCandidates()124 void TryCatchResolving::CollectCandidates()
125 {
126     for (auto bb : GetGraph()->GetBlocksRPO()) {
127         if (bb->IsCatch() && !(bb->IsCatchBegin() || bb->IsTryBegin() || bb->IsTryEnd())) {
128             catchBlocks_.emplace(bb->GetGuestPc(), bb);
129             BasicBlock *cblPred = FindCatchBeginBlock(bb);
130             if (cblPred != nullptr) {
131                 cblPred->RemoveSucc(bb);
132                 bb->RemovePred(cblPred);
133                 catch2cphis_.emplace(bb, cblPred);
134             }
135         } else if (bb->IsTry()) {
136             auto throwInst = bb->GetLastInst();
137             if (throwInst != nullptr && throwInst->GetOpcode() != Opcode::Throw) {
138                 throwInst = nullptr;
139             }
140             if (GetGraph()->GetThrowCounter(bb) > 0) {
141                 ASSERT(throwInst != nullptr);
142                 throwInsts_.emplace_back(throwInst);
143             } else if (throwInst != nullptr) {
144                 throwInsts0_.emplace_back(throwInst);
145             }
146         }
147     }
148 }
149 
ConnectThrowCatchImpl(BasicBlock * catchBlock,BasicBlock * throwBlock,uint32_t catchPc,Inst * newObj,Inst * thr0w)150 void TryCatchResolving::ConnectThrowCatchImpl(BasicBlock *catchBlock, BasicBlock *throwBlock, uint32_t catchPc,
151                                               Inst *newObj, Inst *thr0w)
152 {
153     auto throwBlockSucc = throwBlock->GetSuccessor(0);
154     throwBlock->RemoveSucc(throwBlockSucc);
155     throwBlockSucc->RemovePred(throwBlock);
156     throwBlock->AddSucc(catchBlock);
157     PhiInst *phiInst = nullptr;
158     auto pit = phiInsts_.find(catchPc);
159     if (pit == phiInsts_.end()) {
160         phiInst = GetGraph()->CreateInstPhi(newObj->GetType(), catchPc);
161         catchBlock->AppendPhi(phiInst);
162         phiInsts_.emplace(catchPc, phiInst);
163     } else {
164         phiInst = pit->second;
165     }
166     phiInst->AppendInput(newObj);
167     auto cpit = catch2cphis_.find(catchBlock);
168     ASSERT(cpit != catch2cphis_.end());
169     auto cphisBlock = cpit->second;
170     RemoveCatchPhis(cphisBlock, catchBlock, thr0w, phiInst);
171     COMPILER_LOG(DEBUG, TRY_CATCH_RESOLVING) << "throw I " << thr0w->GetId() << " BB " << throwBlock->GetId()
172                                              << " is connected with catch BB " << catchBlock->GetId() << " and removed";
173     throwBlock->RemoveInst(thr0w);
174 }
175 
ConnectThrowCatch()176 void TryCatchResolving::ConnectThrowCatch()
177 {
178     auto *graph = GetGraph();
179     auto *runtime = graph->GetRuntime();
180     auto *method = graph->GetMethod();
181     for (auto thr0w : throwInsts_) {
182         auto throwBlock = thr0w->GetBasicBlock();
183         auto throwInst = thr0w->CastToThrow();
184         // Inlined throws generate the problem with matching calls and returns now. NOTE Should be fixed.
185         if (GetGraph()->GetThrowCounter(throwBlock) == 0 || throwInst->IsInlined()) {
186             continue;
187         }
188         auto newObj = thr0w->GetInput(0).GetInst();
189         RuntimeInterface::ClassPtr cls = nullptr;
190         if (newObj->GetOpcode() != Opcode::NewObject) {
191             continue;
192         }
193         auto initClass = newObj->GetInput(0).GetInst();
194         if (initClass->GetOpcode() == Opcode::LoadAndInitClass) {
195             cls = initClass->CastToLoadAndInitClass()->GetClass();
196         } else {
197             ASSERT(initClass->GetOpcode() == Opcode::LoadImmediate);
198             cls = initClass->CastToLoadImmediate()->GetClass();
199         }
200         if (cls == nullptr) {
201             continue;
202         }
203         auto catchPc = runtime->FindCatchBlock(method, cls, thr0w->GetPc());
204         if (catchPc == panda_file::INVALID_OFFSET) {
205             continue;
206         }
207         auto cit = catchBlocks_.find(catchPc);
208         if (cit == catchBlocks_.end()) {
209             continue;
210         }
211         ConnectThrowCatchImpl(cit->second, throwBlock, catchPc, newObj, thr0w);
212     }
213 }
214 
215 /**
216  * Search throw instruction with known at compile-time `object_id`
217  * and directly connect catch-handler for this `object_id` if it exists in the current graph
218  */
VisitTryInst(TryInst * tryInst)219 void TryCatchResolving::VisitTryInst(TryInst *tryInst)
220 {
221     auto tryBegin = tryInst->GetBasicBlock();
222     auto tryEnd = tryInst->GetTryEndBlock();
223     ASSERT(tryBegin != nullptr && tryBegin->IsTryBegin());
224     ASSERT(tryEnd != nullptr && tryEnd->IsTryEnd());
225 
226     // Now, when catch-handler was searched - remove all edges from `try_begin` and `try_end` blocks
227     DeleteTryCatchEdges(tryBegin, tryEnd);
228     // Clean-up labels and `try_inst`
229     COMPILER_LOG(DEBUG, TRY_CATCH_RESOLVING) << "Erase try-inst I " << tryInst->GetId();
230     tryBegin->EraseInst(tryInst);
231     COMPILER_LOG(DEBUG, TRY_CATCH_RESOLVING) << "Unset try-begin BB " << tryBegin->GetId();
232     tryBegin->SetTryBegin(false);
233     COMPILER_LOG(DEBUG, TRY_CATCH_RESOLVING) << "Unset try-end BB " << tryEnd->GetId();
234     tryEnd->SetTryEnd(false);
235 }
236 
237 /// Disconnect auxiliary `try_begin` and `try_end`. That means all related catch-handlers become unreachable
DeleteTryCatchEdges(BasicBlock * tryBegin,BasicBlock * tryEnd)238 void TryCatchResolving::DeleteTryCatchEdges(BasicBlock *tryBegin, BasicBlock *tryEnd)
239 {
240     while (tryBegin->GetSuccsBlocks().size() > 1U) {
241         auto catchSucc = tryBegin->GetSuccessor(1U);
242         ASSERT(catchSucc->IsCatchBegin());
243         tryBegin->RemoveSucc(catchSucc);
244         catchSucc->RemovePred(tryBegin);
245         COMPILER_LOG(DEBUG, TRY_CATCH_RESOLVING)
246             << "Remove edge between try_begin BB " << tryBegin->GetId() << " and catch-begin BB " << catchSucc->GetId();
247         if (tryEnd->GetGraph() != nullptr) {
248             ASSERT(tryEnd->GetSuccessor(1) == catchSucc);
249             tryEnd->RemoveSucc(catchSucc);
250             catchSucc->RemovePred(tryEnd);
251             COMPILER_LOG(DEBUG, TRY_CATCH_RESOLVING)
252                 << "Remove edge between try_end BB " << tryEnd->GetId() << " and catch-begin BB " << catchSucc->GetId();
253         }
254     }
255 }
256 
RemoveCatchPhisImpl(CatchPhiInst * catchPhi,BasicBlock * catchBlock,Inst * throwInst)257 void TryCatchResolving::RemoveCatchPhisImpl(CatchPhiInst *catchPhi, BasicBlock *catchBlock, Inst *throwInst)
258 {
259     auto throwInsts = catchPhi->GetThrowableInsts();
260     auto it = std::find(throwInsts->begin(), throwInsts->end(), throwInst);
261     if (it != throwInsts->end()) {
262         auto inputIndex = std::distance(throwInsts->begin(), it);
263         auto inputInst = catchPhi->GetInput(inputIndex).GetInst();
264         PhiInst *phi = nullptr;
265         auto cit = cphi2phi_.find(catchPhi);
266         if (cit == cphi2phi_.end()) {
267             phi = GetGraph()->CreateInstPhi(catchPhi->GetType(), catchBlock->GetGuestPc())->CastToPhi();
268             catchBlock->AppendPhi(phi);
269             catchPhi->ReplaceUsers(phi);
270             cphi2phi_.emplace(catchPhi, phi);
271         } else {
272             phi = cit->second;
273         }
274         phi->AppendInput(inputInst);
275     } else {
276         while (!catchPhi->GetUsers().Empty()) {
277             auto &user = catchPhi->GetUsers().Front();
278             auto userInst = user.GetInst();
279             if (userInst->IsSaveState() || userInst->IsCatchPhi()) {
280                 userInst->RemoveInput(user.GetIndex());
281             } else {
282                 auto inputInst = catchPhi->GetInput(0).GetInst();
283                 userInst->ReplaceInput(catchPhi, inputInst);
284             }
285         }
286     }
287 }
288 
289 /**
290  * Replace all catch-phi instructions with their inputs
291  * Replace accumulator's catch-phi with exception's object
292  */
RemoveCatchPhis(BasicBlock * cphisBlock,BasicBlock * catchBlock,Inst * throwInst,Inst * phiInst)293 void TryCatchResolving::RemoveCatchPhis(BasicBlock *cphisBlock, BasicBlock *catchBlock, Inst *throwInst, Inst *phiInst)
294 {
295     ASSERT(cphisBlock->IsCatchBegin());
296     for (auto inst : cphisBlock->AllInstsSafe()) {
297         if (!inst->IsCatchPhi()) {
298             break;
299         }
300         auto catchPhi = inst->CastToCatchPhi();
301         if (catchPhi->IsAcc()) {
302             catchPhi->ReplaceUsers(phiInst);
303         } else {
304             RemoveCatchPhisImpl(catchPhi, catchBlock, throwInst);
305         }
306     }
307 }
308 
InvalidateAnalyses()309 void TryCatchResolving::InvalidateAnalyses()
310 {
311     GetGraph()->InvalidateAnalysis<DominatorsTree>();
312     GetGraph()->InvalidateAnalysis<LoopAnalyzer>();
313     InvalidateBlocksOrderAnalyzes(GetGraph());
314 }
315 }  // namespace ark::compiler
316