• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2022 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 "optimizer/analysis/dominators_tree.h"
20 #include "optimizer/analysis/loop_analyzer.h"
21 #include "optimizer/ir/basicblock.h"
22 #include "optimizer/ir/inst.h"
23 #include "optimizer/optimizations/cleanup.h"
24 #include "optimizer/optimizations/try_catch_resolving.h"
25 
26 namespace panda::compiler {
TryCatchResolving(Graph * graph)27 TryCatchResolving::TryCatchResolving(Graph *graph) : Optimization(graph) {}
28 
RunImpl()29 bool TryCatchResolving::RunImpl()
30 {
31     marker_ = GetGraph()->NewMarker();
32     for (auto block : GetGraph()->GetBlocksRPO()) {
33         if (!block->IsTryBegin()) {
34             continue;
35         }
36         COMPILER_LOG(DEBUG, TRY_CATCH_RESOLVING) << "Visit try-begin id = " << std::to_string(block->GetId());
37         VisitTry(GetTryBeginInst(block));
38     }
39     GetGraph()->RemoveUnreachableBlocks();
40     GetGraph()->ClearTryCatchInfo();
41     GetGraph()->EraseMarker(marker_);
42 #ifndef NDEBUG
43     InvalidateAnalyses();
44     // Cleanup should be done inside pass, to satisfy GraphChecker
45     GetGraph()->RunPass<Cleanup>();
46 #endif
47     return true;
48 }
49 
50 /**
51  * Search throw instruction with known at compile-time `object_id`
52  * and directly connect catch-handler for this `object_id` if it exists in the current graph
53  */
VisitTry(TryInst * try_inst)54 void TryCatchResolving::VisitTry(TryInst *try_inst)
55 {
56     auto try_begin = try_inst->GetBasicBlock();
57     auto try_end = try_inst->GetTryEndBlock();
58     ASSERT(try_begin->IsTryBegin());
59     ASSERT(try_end->IsTryEnd());
60     // First of all, try to find catch-handler that can be directly connected to the block with `throw`
61     auto resolved_catch_handler = TryFindResolvedCatchHandler(try_begin, try_end);
62     // Now, when catch-handler was searched - remove all edges from `try_begin` and `try_end` blocks
63     DeleteTryCatchEdges(try_begin, try_end);
64     // If resolved catch-handler exists, connect it
65     if (resolved_catch_handler != nullptr) {
66         ConnectCatchHandlerAfterThrow(try_end, resolved_catch_handler);
67     }
68     // Clean-up lables and `try_inst`
69     try_begin->EraseInst(try_inst);
70     try_begin->SetTryBegin(false);
71     try_end->SetTryEnd(false);
72 }
73 
TryFindResolvedCatchHandler(BasicBlock * try_begin,BasicBlock * try_end)74 BasicBlock *TryCatchResolving::TryFindResolvedCatchHandler(BasicBlock *try_begin, BasicBlock *try_end)
75 {
76     // `try_end` could be removed after inlining
77     if (try_end->GetPredsBlocks().size() != 1U) {
78         return nullptr;
79     }
80     auto throw_inst = try_end->GetPredecessor(0)->GetLastInst();
81     if (throw_inst == nullptr || throw_inst->GetOpcode() != Opcode::Throw) {
82         return nullptr;
83     }
84     auto object_id = TryGetObjectId(throw_inst);
85     if (!object_id) {
86         return nullptr;
87     }
88     // There should be no side-exits from try
89     auto holder = MarkerHolder(GetGraph());
90     auto marker = holder.GetMarker();
91     if (!DFS(try_begin->GetSuccessor(0), marker, try_begin->GetTryId())) {
92         return nullptr;
93     }
94     // We've got `object_id` which is thrown form try-block
95     // Let's find handler, which catches this `object_id`
96     BasicBlock *resolved_handler = nullptr;
97     try_begin->EnumerateCatchHandlers([this, object_id, &resolved_handler](BasicBlock *catch_handler, size_t type_id) {
98         // We don't connect catch-handlers which are related to more than one try-block.
99         // So that we skip blocks that:
100         // - have more than 2 predecessors (one try-begin/end pair);
101         // - were already visited from another try-block and were marked;
102         if (catch_handler->GetPredsBlocks().size() > 2U || catch_handler->IsMarked(marker_)) {
103             return true;
104         }
105         static constexpr size_t CATCH_ALL_ID = 0;
106         if (type_id == CATCH_ALL_ID) {
107             resolved_handler = catch_handler;
108             return false;
109         }
110         auto runtime = GetGraph()->GetRuntime();
111         auto *cls = runtime->ResolveType(GetGraph()->GetMethod(), object_id.value());
112         auto *handler_cls = runtime->ResolveType(GetGraph()->GetMethod(), type_id);
113         if (static_cast<Class *>(cls)->IsSubClassOf(static_cast<Class *>(handler_cls))) {
114             resolved_handler = catch_handler;
115             return false;
116         }
117         return true;
118     });
119     return resolved_handler;
120 }
121 
122 /**
123  * Disconnect auxiliary `try_begin` and `try_end`. That means all related catch-handlers become unreachable
124  */
DeleteTryCatchEdges(BasicBlock * try_begin,BasicBlock * try_end)125 void TryCatchResolving::DeleteTryCatchEdges(BasicBlock *try_begin, BasicBlock *try_end)
126 {
127     while (try_begin->GetSuccsBlocks().size() > 1U) {
128         auto catch_succ = try_begin->GetSuccessor(1U);
129         ASSERT(catch_succ->IsCatchBegin());
130         try_begin->RemoveSucc(catch_succ);
131         catch_succ->RemovePred(try_begin);
132         if (try_end->GetGraph() != nullptr) {
133             ASSERT(try_end->GetSuccessor(1) == catch_succ);
134             try_end->RemoveSucc(catch_succ);
135             catch_succ->RemovePred(try_end);
136         }
137         // Mark that catch-handler was visited
138         catch_succ->SetMarker(marker_);
139     }
140 }
141 
142 /**
143  * Add edge between catch-handler and `try_end` follows `throw` instruction
144  */
ConnectCatchHandlerAfterThrow(BasicBlock * try_end,BasicBlock * catch_block)145 void TryCatchResolving::ConnectCatchHandlerAfterThrow(BasicBlock *try_end, BasicBlock *catch_block)
146 {
147     ASSERT(try_end != nullptr && try_end->IsTryEnd());
148     ASSERT(catch_block != nullptr && catch_block->IsCatchBegin());
149     ASSERT(try_end->GetPredsBlocks().size() == 1U);
150     auto throw_inst = try_end->GetPredecessor(0)->GetLastInst();
151     ASSERT(throw_inst != nullptr && throw_inst->GetOpcode() == Opcode::Throw);
152 
153     COMPILER_LOG(DEBUG, TRY_CATCH_RESOLVING)
154         << "Connect blocks: " << std::to_string(try_end->GetId()) << " -> " << std::to_string(catch_block->GetId());
155 
156     auto succ = try_end->GetSuccessor(0);
157     try_end->ReplaceSucc(succ, catch_block);
158     succ->RemovePred(try_end);
159     RemoveCatchPhis(catch_block, throw_inst);
160 
161     auto save_state = throw_inst->GetInput(1).GetInst();
162     ASSERT(save_state->GetOpcode() == Opcode::SaveState);
163     auto throw_block = throw_inst->GetBasicBlock();
164     throw_block->RemoveInst(throw_inst);
165     throw_block->RemoveInst(save_state);
166 }
167 
168 /**
169  * Replace all catch-phi instructions with their inputs
170  * Replace accumulator's catch-phi with exception's object
171  */
RemoveCatchPhis(BasicBlock * block,Inst * throw_inst)172 void TryCatchResolving::RemoveCatchPhis(BasicBlock *block, Inst *throw_inst)
173 {
174     ASSERT(block->IsCatchBegin());
175     for (auto inst : block->AllInstsSafe()) {
176         if (!inst->IsCatchPhi()) {
177             break;
178         }
179         auto catch_phi = inst->CastToCatchPhi();
180         if (catch_phi->IsAcc()) {
181             auto throw_obj = throw_inst->GetInput(0).GetInst();
182             catch_phi->ReplaceUsers(throw_obj);
183         } else {
184             auto throw_insts = catch_phi->GetThrowableInsts();
185             auto it = std::find(throw_insts->begin(), throw_insts->end(), throw_inst);
186             if (it != throw_insts->end()) {
187                 auto input_index = std::distance(throw_insts->begin(), it);
188                 auto input_inst = catch_phi->GetInput(input_index).GetInst();
189                 catch_phi->ReplaceUsers(input_inst);
190             } else {
191                 // Virtual register related to the catch-phi is undefined, so that there should be no real users
192                 auto users = catch_phi->GetUsers();
193                 while (!users.Empty()) {
194                     auto &user = users.Front();
195                     ASSERT(user.GetInst()->IsSaveState() || user.GetInst()->IsCatchPhi());
196                     user.GetInst()->RemoveInput(user.GetIndex());
197                 }
198             }
199         }
200         block->RemoveInst(catch_phi);
201     }
202     block->SetCatch(false);
203     block->SetCatchBegin(false);
204 }
205 
206 /**
207  * Return object's id if `Throw` has `NewObject` input
208  */
TryGetObjectId(const Inst * inst)209 std::optional<uint32_t> TryCatchResolving::TryGetObjectId(const Inst *inst)
210 {
211     ASSERT(inst->GetOpcode() == Opcode::Throw);
212     auto ref_input = inst->GetInput(0).GetInst();
213     if (ref_input->GetOpcode() == Opcode::NewObject) {
214         return ref_input->CastToNewObject()->GetTypeId();
215     }
216     return std::nullopt;
217 }
218 
DFS(BasicBlock * block,Marker marker,uint32_t try_id)219 bool TryCatchResolving::DFS(BasicBlock *block, Marker marker, uint32_t try_id)
220 {
221     if (block->GetTryId() != try_id) {
222         return false;
223     }
224     if (block->IsTryEnd() && block->GetTryId() == try_id) {
225         return true;
226     }
227     block->SetMarker(marker);
228     for (auto succ : block->GetSuccsBlocks()) {
229         if (succ->IsMarked(marker)) {
230             continue;
231         }
232         if (!DFS(succ, marker, try_id)) {
233             return false;
234         }
235     }
236     return true;
237 }
238 
InvalidateAnalyses()239 void TryCatchResolving::InvalidateAnalyses()
240 {
241     GetGraph()->InvalidateAnalysis<DominatorsTree>();
242     GetGraph()->InvalidateAnalysis<LoopAnalyzer>();
243     InvalidateBlocksOrderAnalyzes(GetGraph());
244 }
245 }  // namespace panda::compiler
246