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