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