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