• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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