1 /*
2 * Copyright (c) 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 "try_catch.h"
17 namespace maplebe {
CreateNewBB(StmtNode * first,StmtNode * last)18 BBT *TryCatchBlocksLower::CreateNewBB(StmtNode *first, StmtNode *last)
19 {
20 BBT *newBB = memPool.New<BBT>(first, last, &memPool);
21 bbList.emplace_back(newBB);
22 return newBB;
23 }
24
FindTargetBBlock(LabelIdx idx,const std::vector<BBT * > & bbs)25 BBT *TryCatchBlocksLower::FindTargetBBlock(LabelIdx idx, const std::vector<BBT *> &bbs)
26 {
27 for (auto &target : bbs) {
28 if (target->GetLabelIdx() == idx) {
29 return target;
30 }
31 }
32 return nullptr;
33 }
34
35 /* returns the first statement that is moved in into the try block. If none is moved in, nullptr is returned */
MoveCondGotoIntoTry(BBT & jtBB,BBT & condbrBB,const MapleVector<BBT * > & labeledBBsInTry)36 StmtNode *TryCatchBlocksLower::MoveCondGotoIntoTry(BBT &jtBB, BBT &condbrBB, const MapleVector<BBT *> &labeledBBsInTry)
37 {
38 StmtNode *firstStmtMovedIn = nullptr;
39 const MapleVector<BBT *> &bbs = labeledBBsInTry;
40 StmtNode *jtStmt = jtBB.GetKeyStmt();
41 #if DEBUG
42 StmtNode *js = jtBB.GetFirstStmt();
43 while (js->GetOpCode() != OP_try) {
44 js = js->GetNext();
45 }
46 CHECK_FATAL(js == jtStmt, "make sure js equal jtStmt");
47 #endif
48 StmtNode *ts = jtBB.GetFirstStmt()->GetPrev();
49 while ((ts != nullptr) && (ts->GetOpCode() == OP_comment)) {
50 ts = ts->GetPrev();
51 }
52
53 if (ts != nullptr && ts->IsCondBr()) {
54 CHECK_FATAL(ts->GetNext() == jtBB.GetFirstStmt(), "make sure ts's next equal jtBB's firstStmt");
55 StmtNode *firstStmtNode = jtBB.GetFirstStmt();
56 /* [ jtbb_b..jtstmt ]; either jtbb_b is a comment or jtbb_b == jtstmt */
57 LabelIdx id = static_cast<CondGotoNode *>(ts)->GetOffset();
58 for (auto &lbb : bbs) {
59 if (lbb->GetLabelIdx() == id) {
60 /*
61 * this cond goto jumps into the try block; let the try block enclose it
62 * first find the preceding comment statements if any
63 */
64 StmtNode *brS = ts;
65 while ((ts->GetPrev() != nullptr) && (ts->GetPrev()->GetOpCode() == OP_comment)) {
66 ts = ts->GetPrev();
67 }
68 StmtNode *secondStmtNode = ts; /* beginning statement of branch block */
69 /* [ brbb_b..br_s ]; either brbb_b is a comment or brbb_b == br_s */
70 firstStmtNode->SetPrev(secondStmtNode->GetPrev());
71 if (secondStmtNode->GetPrev()) {
72 secondStmtNode->GetPrev()->SetNext(firstStmtNode);
73 }
74 jtStmt->GetNext()->SetPrev(brS);
75 brS->SetNext(jtStmt->GetNext());
76 secondStmtNode->SetPrev(jtStmt);
77 jtStmt->SetNext(secondStmtNode);
78 condbrBB.SetLastStmt(*firstStmtNode->GetPrev());
79 CHECK_FATAL(condbrBB.GetFallthruBranch() == &jtBB, "make sure condbrBB's fallthruBranch equal &jtBB");
80 condbrBB.SetFallthruBranch(&jtBB);
81 condbrBB.SetCondJumpBranch(nullptr);
82 firstStmtMovedIn = secondStmtNode;
83 break;
84 }
85 }
86 }
87 return firstStmtMovedIn;
88 }
89
RecoverBasicBlock()90 void TryCatchBlocksLower::RecoverBasicBlock()
91 {
92 std::vector<BBT *> condbrBBs;
93 std::vector<BBT *> switchBBs;
94 std::vector<BBT *> labeledBBs;
95 using BBTPair = std::pair<BBT *, BBT *>;
96 std::vector<BBTPair> tryBBs;
97 std::vector<BBT *> catchBBs;
98
99 CHECK_FATAL(body.GetFirst() != nullptr, "body should not be NULL");
100 bodyFirst = body.GetFirst();
101 StmtNode *next = bodyFirst;
102 /*
103 * comment block [ begin, end ], We treat comment statements as if they are parts
104 * of the immediately following non-comment statement
105 */
106 StmtNode *commentB = nullptr;
107 StmtNode *commentE = nullptr;
108
109 BBT *curBB = nullptr;
110 BBT *lastBB = nullptr;
111 BBT *openTry = nullptr;
112
113 /* recover basic blocks */
114 for (StmtNode *stmt = next; stmt != nullptr; stmt = next) {
115 next = stmt->GetNext();
116
117 if (stmt->GetOpCode() == OP_comment) {
118 if (commentB == nullptr) {
119 commentB = stmt;
120 commentE = stmt;
121 } else {
122 CHECK_FATAL(commentE != nullptr, "nullptr is not expected");
123 CHECK_FATAL(commentE->GetNext() == stmt, "make sure commentE's next is stmt");
124 commentE = stmt;
125 }
126 continue;
127 }
128
129 CHECK_FATAL(stmt->GetOpCode() != OP_comment, "make sure stmt's opcde not equal OP_comment");
130 CHECK_FATAL(commentB == nullptr || (commentE != nullptr && commentE->GetNext() == stmt),
131 "make sure commentB is nullptr or commentE's next is stmt");
132
133 if (curBB != nullptr) {
134 if (stmt->GetOpCode() != OP_label && stmt->GetOpCode() != OP_try && stmt->GetOpCode() != OP_endtry) {
135 curBB->Extend(commentB, stmt);
136 } else {
137 /* java catch blockes always start with a label (i.e., OP_catch) */
138 CHECK_FATAL(curBB->GetCondJumpBranch() == nullptr, "expect curBB's condJumpBranch is nullptr");
139 CHECK_FATAL(curBB->GetFallthruBranch() == nullptr, "expect curBB's fallthruBranch is nullptr");
140 /* a 'label' statement starts a new basic block */
141 BBT *newBB = CreateNewBB(commentB, stmt);
142 /*
143 * if the immediately preceding statement (discounting comments) was throw, goto or return,
144 * curBB is to be reset to nullptr, so the control won't come here.
145 */
146 curBB->SetFallthruBranch(newBB);
147 curBB = newBB;
148 }
149 } else {
150 /* start a new basic block with 'comment_b -- stmt' */
151 curBB = CreateNewBB(commentB, stmt);
152 if (lastBB != nullptr) {
153 Opcode lastBBLastStmtOp = lastBB->GetLastStmt()->GetOpCode();
154 if (lastBB->GetLastStmt()->IsCondBr() || lastBBLastStmtOp == OP_endtry) {
155 lastBB->SetFallthruBranch(curBB);
156 }
157 /* else don't connect curBB to last_bb */
158 }
159 }
160 commentB = nullptr;
161 commentE = nullptr;
162
163 switch (stmt->GetOpCode()) {
164 case OP_throw:
165 case OP_return:
166 case OP_goto:
167 /* start a new bb at the next stmt */
168 lastBB = curBB;
169 curBB = nullptr;
170 break;
171 case OP_label: {
172 LabelNode *labelStmt = static_cast<LabelNode *>(stmt);
173 labeledBBs.emplace_back(curBB);
174 curBB->SetLabelIdx(static_cast<LabelIdx>(labelStmt->GetLabelIdx()));
175 break;
176 }
177 case OP_brtrue:
178 case OP_brfalse:
179 condbrBBs.emplace_back(curBB);
180 lastBB = curBB;
181 curBB = nullptr;
182 break;
183 case OP_switch:
184 switchBBs.emplace_back(curBB);
185 lastBB = curBB;
186 curBB = nullptr;
187 break;
188 /*
189 * We deal try and endtry slightly differently.
190 * 1. try begins a basic block which includes the try statement and the subsequent statements up to one that
191 * results in non-sequential control transfer such as unconditional/conditional branches.
192 * 2. endtry will create its own basic block which contains the endtry statement and nothing else.
193 */
194 case OP_try:
195 case OP_endtry: {
196 /* because a label statement is inserted at the function entry */
197 CHECK_FATAL(curBB != nullptr, "expect curBB is not nullptr");
198 CHECK_FATAL(curBB->GetCondJumpBranch() == nullptr, "expect curBB's condJumpBranch is nullptr");
199 CHECK_FATAL(curBB->GetFallthruBranch() == nullptr, "expect curBB's fallthruBranch is nullptr");
200 CHECK_FATAL(curBB->GetLastStmt()->GetOpCode() == stmt->GetOpCode(),
201 "the opcode of curBB's lastStmt should equal stmt's opcocde");
202 if (stmt->GetOpCode() == OP_try) {
203 CHECK_FATAL(openTry == nullptr, "trys are not expected to be nested");
204 curBB->SetType(BBT::kBBTry, *stmt);
205 openTry = curBB;
206 prevBBOfTry[openTry] = lastBB;
207 } else {
208 tryBBs.emplace_back(BBTPair(openTry, curBB));
209 openTry = nullptr;
210 curBB->SetType(BBT::kBBEndTry, *stmt);
211 lastBB = curBB;
212 curBB = nullptr;
213 }
214 break;
215 }
216 case OP_catch: {
217 #if DEBUG
218 StmtNode *ss = stmt->GetPrev();
219 while ((ss != nullptr) && (ss->GetOpCode() == OP_comment)) {
220 ss = ss->GetPrev();
221 }
222 CHECK_FATAL(ss != nullptr, "expect ss is not nullptr");
223 CHECK_FATAL(ss->GetOpCode() == OP_label, "expect op equal OP_label");
224 for (auto &tb : catchBBs) {
225 CHECK_FATAL(tb != curBB, "tb should not equal curBB");
226 }
227 #endif
228 catchBBs.emplace_back(curBB);
229 curBB->SetType(BBT::kBBCatch, *stmt);
230 break;
231 }
232 case OP_block:
233 CHECK_FATAL(0, "should not run here");
234 default:
235 break;
236 }
237 }
238
239 for (auto &cbBB : condbrBBs) {
240 CHECK_FATAL(cbBB->GetLastStmt()->IsCondBr(), "cbBB's lastStmt is not condBr");
241 CondGotoNode *cbBBLastStmt = static_cast<CondGotoNode *>(cbBB->GetLastStmt());
242 cbBB->SetCondJumpBranch(FindTargetBBlock(static_cast<LabelIdx>(cbBBLastStmt->GetOffset()), labeledBBs));
243 }
244
245 for (auto &swBB : switchBBs) {
246 CHECK_FATAL(swBB->GetLastStmt()->GetOpCode() == OP_switch,
247 "the opcode of sw's lastStmt should equal OP_switch");
248 SwitchNode *ss = static_cast<SwitchNode *>(swBB->GetLastStmt());
249
250 swBB->AddSuccs(FindTargetBBlock(ss->GetDefaultLabel(), labeledBBs));
251 for (auto &cp : ss->GetSwitchTable()) {
252 swBB->AddSuccs(FindTargetBBlock(cp.second, labeledBBs));
253 }
254 }
255
256 for (auto &bb : bbList) {
257 firstStmtToBBMap[bb->GetFirstStmt()] = bb;
258 }
259 CHECK_FATAL(openTry == nullptr, "trys are not expected to be nested");
260 }
261
262 /* if catchBB is in try-endtry block and catch is own to current try-endtry, process it and return true */
CheckAndProcessCatchNodeInCurrTryBlock(BBT & origLowerBB,LabelIdx ebbLabel,uint32 index)263 bool TryCatchBlocksLower::CheckAndProcessCatchNodeInCurrTryBlock(BBT &origLowerBB, LabelIdx ebbLabel, uint32 index)
264 {
265 MapleVector<BBT *> &enclosedBBs = tryEndTryBlock.GetEnclosedBBs();
266 MapleVector<BBT *> &bbsToRelocate = tryEndTryBlock.GetBBsToRelocate();
267 BBT *endTryBB = tryEndTryBlock.GetEndTryBB();
268 StmtNode *tryStmt = tryEndTryBlock.GetTryStmtNode();
269 bool found = false;
270 for (size_t tempIndex = 0; tempIndex < static_cast<TryNode *>(tryStmt)->GetOffsetsCount(); ++tempIndex) {
271 auto id = static_cast<TryNode *>(tryStmt)->GetOffset(tempIndex);
272 /*
273 * if this labeled bb is a catch block,
274 * remove it from the list of blocks enclosed in this try-block'
275 */
276 if (ebbLabel == id) {
277 found = true;
278 enclosedBBs[index] = nullptr;
279 std::vector<BBT *> currBBThread;
280 BBT *lowerBB = &origLowerBB;
281 /* append it to the list of blocks placed after the end try block */
282 currBBThread.emplace_back(lowerBB);
283 while (lowerBB->GetFallthruBranch() != nullptr) {
284 lowerBB = lowerBB->GetFallthruBranch();
285 CHECK_FATAL(!lowerBB->IsTry(), "ebb must not be tryBB");
286 if (lowerBB->IsEndTry()) {
287 CHECK_FATAL(lowerBB == endTryBB, "lowerBB should equal endTryBB");
288 break;
289 }
290 for (uint32 j = 0; j < enclosedBBs.size(); ++j) {
291 if (enclosedBBs[j] == lowerBB) {
292 enclosedBBs[j] = nullptr;
293 break;
294 }
295 }
296 currBBThread.emplace_back(lowerBB);
297 }
298
299 if (!lowerBB->IsEndTry()) {
300 for (auto &e : currBBThread) {
301 bbsToRelocate.emplace_back(e);
302 }
303 } else {
304 /*
305 * We have the following case.
306 * bb_head -> bb_1 -> .. bb_n -> endtry_bb -> succ
307 * For this particular case, we swap endtry bb and curr_bb_thread because the bblock that
308 * contains the endtry statement does not contain any other statements!!
309 */
310 CHECK_FATAL(endTryBB->GetFirstStmt()->GetOpCode() == OP_comment ||
311 endTryBB->GetFirstStmt()->GetOpCode() == OP_endtry,
312 "the opcode of endTryBB's firstStmt should be OP_comment or OP_endtry");
313 CHECK_FATAL(endTryBB->GetLastStmt()->GetOpCode() == OP_endtry,
314 "the opcode of endTryBB's lastStmt should be OP_endtry");
315
316 /* we move endtry_bb before thread_head */
317 BBT *threadHead = currBBThread.front();
318 CHECK_FATAL(threadHead->GetFirstStmt()->GetPrev() != nullptr,
319 "the prev node of threadHead's firstStmt should be not nullptr");
320 CHECK_FATAL(threadHead->GetFirstStmt()->GetOpCode() == OP_comment ||
321 threadHead->GetFirstStmt()->GetOpCode() == OP_label,
322 "the opcode of threadHead's firstStmt should be OP_comment or OP_label");
323 CHECK_FATAL(threadHead->GetFirstStmt()->GetPrev()->GetNext() == threadHead->GetFirstStmt(),
324 "the next of the prev of threadHead's firstStmt should equal threadHead's firstStmt");
325 threadHead->GetFirstStmt()->GetPrev()->SetNext(endTryBB->GetFirstStmt());
326 endTryBB->GetFirstStmt()->SetPrev(threadHead->GetFirstStmt()->GetPrev());
327 BBT *threadTail = currBBThread.back();
328 threadTail->GetLastStmt()->SetNext(endTryBB->GetLastStmt()->GetNext());
329 if (endTryBB->GetLastStmt()->GetNext() != nullptr) {
330 endTryBB->GetLastStmt()->GetNext()->SetPrev(threadTail->GetLastStmt());
331 }
332 endTryBB->GetLastStmt()->SetNext(threadHead->GetFirstStmt());
333
334 CHECK_FATAL(endTryBB->GetCondJumpBranch() == nullptr, "endTryBB's condJumpBranch must be nullptr");
335 if (threadTail->GetFallthruBranch() != nullptr) {
336 threadTail->SetFallthruBranch(firstStmtToBBMap[threadTail->GetLastStmt()->GetNext()]);
337 }
338 endTryBB->SetFallthruBranch(nullptr);
339 if (bodyEndWithEndTry) {
340 body.SetLast(threadTail->GetLastStmt());
341 }
342 }
343 break;
344 }
345 }
346 return found;
347 }
348
349 /* collect catchbb->fallthru(0-n) into currBBThread, when encounter a new catch, return it, else return nullptr */
CollectCatchAndFallthruUntilNextCatchBB(BBT * & lowerBB,uint32 & nextEnclosedIdx,std::vector<BBT * > & currBBThread)350 BBT *TryCatchBlocksLower::CollectCatchAndFallthruUntilNextCatchBB(BBT *&lowerBB, uint32 &nextEnclosedIdx,
351 std::vector<BBT *> &currBBThread)
352 {
353 MapleVector<BBT *> &enclosedBBs = tryEndTryBlock.GetEnclosedBBs();
354 BBT *endTryBB = tryEndTryBlock.GetEndTryBB();
355
356 BBT *nextBBThreadHead = nullptr;
357 while (lowerBB->GetFallthruBranch() != nullptr) {
358 lowerBB = lowerBB->GetFallthruBranch();
359 ++nextEnclosedIdx;
360 if (lowerBB->IsEndTry()) {
361 CHECK_FATAL(lowerBB == endTryBB, "lowerBB should equal endTryBB");
362 break;
363 }
364
365 for (uint32 j = 0; j < enclosedBBs.size(); ++j) {
366 if (enclosedBBs[j] == lowerBB) {
367 enclosedBBs[j] = nullptr;
368 break;
369 }
370 }
371 if (lowerBB->IsCatch()) {
372 nextBBThreadHead = lowerBB;
373 break;
374 }
375 currBBThread.emplace_back(lowerBB);
376 }
377
378 if (nextBBThreadHead == nullptr && lowerBB->GetFallthruBranch() == nullptr && lowerBB != endTryBB &&
379 nextEnclosedIdx < enclosedBBs.size() && enclosedBBs[nextEnclosedIdx]) {
380 /*
381 * Using a loop to find the next_bb_thread_head when it's a catch_BB or a normal_BB which
382 * is after a catch_BB. Other condition, push_back into the curr_bb_thread.
383 */
384 do {
385 lowerBB = enclosedBBs[nextEnclosedIdx];
386 enclosedBBs[nextEnclosedIdx++] = nullptr;
387 BBT *head = currBBThread.front();
388 if (head->IsCatch() || lowerBB->IsCatch()) {
389 nextBBThreadHead = lowerBB;
390 break;
391 }
392 currBBThread.emplace_back(lowerBB);
393 } while (nextEnclosedIdx < enclosedBBs.size());
394 }
395
396 return nextBBThreadHead;
397 }
398
ProcessThreadTail(BBT & threadTail,BBT * const & nextBBThreadHead,bool hasMoveEndTry)399 void TryCatchBlocksLower::ProcessThreadTail(BBT &threadTail, BBT *const &nextBBThreadHead, bool hasMoveEndTry)
400 {
401 BBT *endTryBB = tryEndTryBlock.GetEndTryBB();
402 StmtNode *newEndTry = endTryBB->GetKeyStmt()->CloneTree(mirModule.GetCurFuncCodeMPAllocator());
403 newEndTry->SetPrev(threadTail.GetLastStmt());
404 newEndTry->SetNext(threadTail.GetLastStmt()->GetNext());
405 if (bodyEndWithEndTry && hasMoveEndTry) {
406 if (threadTail.GetLastStmt()->GetNext()) {
407 threadTail.GetLastStmt()->GetNext()->SetPrev(newEndTry);
408 }
409 } else {
410 CHECK_FATAL(threadTail.GetLastStmt()->GetNext() != nullptr,
411 "the next of threadTail's lastStmt should not be nullptr");
412 threadTail.GetLastStmt()->GetNext()->SetPrev(newEndTry);
413 }
414 threadTail.GetLastStmt()->SetNext(newEndTry);
415
416 threadTail.SetLastStmt(*newEndTry);
417 if (hasMoveEndTry && nextBBThreadHead == nullptr) {
418 body.SetLast(threadTail.GetLastStmt());
419 }
420 }
421
422 /* Wrap this catch block with try-endtry block */
WrapCatchWithTryEndTryBlock(std::vector<BBT * > & currBBThread,BBT * & nextBBThreadHead,uint32 & nextEnclosedIdx,bool hasMoveEndTry)423 void TryCatchBlocksLower::WrapCatchWithTryEndTryBlock(std::vector<BBT *> &currBBThread, BBT *&nextBBThreadHead,
424 uint32 &nextEnclosedIdx, bool hasMoveEndTry)
425 {
426 BBT *endTryBB = tryEndTryBlock.GetEndTryBB();
427 StmtNode *tryStmt = tryEndTryBlock.GetTryStmtNode();
428 MapleVector<BBT *> &enclosedBBs = tryEndTryBlock.GetEnclosedBBs();
429 for (auto &e : currBBThread) {
430 CHECK_FATAL(!e->IsTry(), "expect e is not try");
431 }
432 BBT *threadHead = currBBThread.front();
433 if (threadHead->IsCatch()) {
434 StmtNode *jcStmt = threadHead->GetKeyStmt();
435 CHECK_FATAL(jcStmt->GetNext() != nullptr, "jcStmt's next should not be nullptr");
436 TryNode *jtCopy = static_cast<TryNode *>(tryStmt)->CloneTree(mirModule.GetCurFuncCodeMPAllocator());
437 jtCopy->SetNext(jcStmt->GetNext());
438 jtCopy->SetPrev(jcStmt);
439 jcStmt->GetNext()->SetPrev(jtCopy);
440 jcStmt->SetNext(jtCopy);
441
442 BBT *threadTail = currBBThread.back();
443
444 /* for this endtry stmt, we don't need to create a basic block */
445 ProcessThreadTail(*threadTail, static_cast<BBT *const &>(nextBBThreadHead), hasMoveEndTry);
446 } else {
447 /* For cases try->catch->normal_bb->normal_bb->endtry, Combine normal bb first. */
448 while (nextEnclosedIdx < enclosedBBs.size()) {
449 if (nextBBThreadHead != nullptr) {
450 if (nextBBThreadHead->IsCatch()) {
451 break;
452 }
453 }
454 BBT *ebbSecond = enclosedBBs[nextEnclosedIdx];
455 enclosedBBs[nextEnclosedIdx++] = nullptr;
456 CHECK_FATAL(ebbSecond != endTryBB, "ebbSecond should not equal endTryBB");
457 if (ebbSecond->IsCatch()) {
458 nextBBThreadHead = ebbSecond;
459 break;
460 }
461 currBBThread.emplace_back(ebbSecond);
462 }
463 /* normal bb. */
464 StmtNode *stmt = threadHead->GetFirstStmt();
465
466 TryNode *jtCopy = static_cast<TryNode *>(tryStmt)->CloneTree(mirModule.GetCurFuncCodeMPAllocator());
467 jtCopy->SetNext(stmt);
468 jtCopy->SetPrev(stmt->GetPrev());
469 stmt->GetPrev()->SetNext(jtCopy);
470 stmt->SetPrev(jtCopy);
471 threadHead->SetFirstStmt(*jtCopy);
472
473 BBT *threadTail = currBBThread.back();
474
475 /* for this endtry stmt, we don't need to create a basic block */
476 ProcessThreadTail(*threadTail, static_cast<BBT *const &>(nextBBThreadHead), hasMoveEndTry);
477 }
478 }
479
480 /*
481 * We have the following case.
482 * bb_head -> bb_1 -> .. bb_n -> endtry_bb -> succ
483 * For this particular case, we swap EndTry bb and curr_bb_thread, because the bblock that contains the endtry
484 * statement does not contain any other statements!!
485 */
SwapEndTryBBAndCurrBBThread(const std::vector<BBT * > & currBBThread,bool & hasMoveEndTry,const BBT * nextBBThreadHead)486 void TryCatchBlocksLower::SwapEndTryBBAndCurrBBThread(const std::vector<BBT *> &currBBThread, bool &hasMoveEndTry,
487 const BBT *nextBBThreadHead)
488 {
489 BBT *endTryBB = tryEndTryBlock.GetEndTryBB();
490 CHECK_FATAL(endTryBB->GetFirstStmt()->GetOpCode() == OP_comment ||
491 endTryBB->GetFirstStmt()->GetOpCode() == OP_endtry,
492 "the opcode of endTryBB's firstStmt should be OP_comment or OP_endtry");
493 CHECK_FATAL(endTryBB->GetLastStmt()->GetOpCode() == OP_endtry,
494 "the opcode of endTryBB's lastStmt should be OP_endtry");
495
496 /* we move endtry_bb before bb_head */
497 BBT *threadHead = currBBThread.front();
498 CHECK_FATAL(threadHead->GetFirstStmt()->GetPrev() != nullptr,
499 "the prev of threadHead's firstStmt should not nullptr");
500 CHECK_FATAL(threadHead->GetFirstStmt()->GetOpCode() == OP_comment ||
501 threadHead->GetFirstStmt()->GetOpCode() == OP_label,
502 "the opcode of threadHead's firstStmt should be OP_comment or OP_label");
503 CHECK_FATAL(threadHead->GetFirstStmt()->GetPrev()->GetNext() == threadHead->GetFirstStmt(),
504 "the next of the prev of threadHead's firstStmt should equal threadHead's firstStmt");
505
506 endTryBB->GetFirstStmt()->GetPrev()->SetNext(endTryBB->GetLastStmt()->GetNext());
507 if (endTryBB->GetLastStmt()->GetNext() != nullptr) {
508 endTryBB->GetLastStmt()->GetNext()->SetPrev(endTryBB->GetFirstStmt()->GetPrev());
509 }
510
511 threadHead->GetFirstStmt()->GetPrev()->SetNext(endTryBB->GetFirstStmt());
512 endTryBB->GetFirstStmt()->SetPrev(threadHead->GetFirstStmt()->GetPrev());
513
514 endTryBB->GetLastStmt()->SetNext(threadHead->GetFirstStmt());
515 threadHead->GetFirstStmt()->SetPrev(endTryBB->GetLastStmt());
516
517 CHECK_FATAL(endTryBB->GetCondJumpBranch() == nullptr, "endTryBB's condJumpBranch must be nullptr");
518 endTryBB->SetFallthruBranch(nullptr);
519 if (bodyEndWithEndTry) {
520 hasMoveEndTry = true;
521 if (nextBBThreadHead == nullptr) {
522 body.SetLast(currBBThread.back()->GetLastStmt());
523 }
524 }
525 }
526
ProcessEnclosedBBBetweenTryEndTry()527 void TryCatchBlocksLower::ProcessEnclosedBBBetweenTryEndTry()
528 {
529 MapleVector<BBT *> &enclosedBBs = tryEndTryBlock.GetEnclosedBBs();
530 MapleVector<BBT *> &labeledBBsInTry = tryEndTryBlock.GetLabeledBBsInTry();
531
532 for (uint32 i = 0; i < enclosedBBs.size(); ++i) {
533 BBT *lowerBB = enclosedBBs[i];
534 uint32 nextEnclosedIdx = i + 1;
535 if (lowerBB == nullptr) {
536 continue; /* we may have removed the element */
537 }
538 if (!lowerBB->IsLabeled()) {
539 continue;
540 }
541 labeledBBsInTry.emplace_back(lowerBB);
542
543 /*
544 * It seems the way a finally is associated with its try is to put the catch block inside
545 * the java-try-end-try block. So, keep the 'catch(void*)' in it.
546 */
547 LabelIdx ebbLabel = lowerBB->GetLabelIdx();
548 bool found = CheckAndProcessCatchNodeInCurrTryBlock(*lowerBB, ebbLabel, i);
549 /* fill cur_bb_thread until meet the next catch */
550 if (!found && lowerBB->IsCatch()) {
551 enclosedBBs[i] = nullptr;
552 std::vector<BBT *> currBBThread;
553 BBT *nextBBThreadHead = nullptr;
554 bool isFirstTime = true;
555 bool hasMoveEndTry = false;
556 do {
557 if (nextBBThreadHead != nullptr) {
558 isFirstTime = false;
559 }
560 nextBBThreadHead = nullptr;
561 currBBThread.clear();
562 currBBThread.emplace_back(lowerBB);
563 nextBBThreadHead = CollectCatchAndFallthruUntilNextCatchBB(lowerBB, nextEnclosedIdx, currBBThread);
564 WrapCatchWithTryEndTryBlock(currBBThread, nextBBThreadHead, nextEnclosedIdx, hasMoveEndTry);
565 if (isFirstTime) {
566 SwapEndTryBBAndCurrBBThread(currBBThread, hasMoveEndTry, nextBBThreadHead);
567 }
568 } while (nextBBThreadHead != nullptr);
569 }
570 }
571 }
572
ConnectRemainBB()573 void TryCatchBlocksLower::ConnectRemainBB()
574 {
575 MapleVector<BBT *> &enclosedBBs = tryEndTryBlock.GetEnclosedBBs();
576 BBT *startTryBB = tryEndTryBlock.GetStartTryBB();
577 BBT *endTryBB = tryEndTryBlock.GetEndTryBB();
578 size_t nEnclosedBBs = enclosedBBs.size();
579 size_t k = 0;
580 while ((k < nEnclosedBBs) && (enclosedBBs[k] == nullptr)) {
581 ++k;
582 }
583
584 if (k < nEnclosedBBs) {
585 BBT *prevBB = enclosedBBs[k];
586
587 startTryBB->GetLastStmt()->SetNext(prevBB->GetFirstStmt());
588 prevBB->GetFirstStmt()->SetPrev(startTryBB->GetLastStmt());
589
590 for (++k; k < nEnclosedBBs; ++k) {
591 BBT *lowerBB = enclosedBBs[k];
592 if (lowerBB == nullptr) {
593 continue;
594 }
595 prevBB->GetLastStmt()->SetNext(lowerBB->GetFirstStmt());
596 lowerBB->GetFirstStmt()->SetPrev(prevBB->GetLastStmt());
597 prevBB = lowerBB;
598 }
599
600 prevBB->GetLastStmt()->SetNext(endTryBB->GetFirstStmt());
601 endTryBB->GetFirstStmt()->SetPrev(prevBB->GetLastStmt());
602 } else {
603 startTryBB->GetLastStmt()->SetNext(endTryBB->GetFirstStmt());
604 endTryBB->GetFirstStmt()->SetPrev(startTryBB->GetLastStmt());
605 }
606 }
607
FindInsertAfterBB()608 BBT *TryCatchBlocksLower::FindInsertAfterBB()
609 {
610 BBT *insertAfter = tryEndTryBlock.GetEndTryBB();
611 CHECK_FATAL(tryEndTryBlock.GetEndTryBB()->GetLastStmt()->GetOpCode() == OP_endtry, "LowerBB type check");
612 BBT *iaOpenTry = nullptr;
613 while (insertAfter->GetFallthruBranch() != nullptr || iaOpenTry != nullptr) {
614 if (insertAfter->GetFallthruBranch() != nullptr) {
615 insertAfter = insertAfter->GetFallthruBranch();
616 } else {
617 CHECK_FATAL(iaOpenTry != nullptr, "iaOpenTry should not be nullptr");
618 insertAfter = firstStmtToBBMap[insertAfter->GetLastStmt()->GetNext()];
619 CHECK_FATAL(!insertAfter->IsTry(), "insertAfter should not be try");
620 }
621
622 if (insertAfter->IsTry()) {
623 iaOpenTry = insertAfter;
624 } else if (insertAfter->IsEndTry()) {
625 iaOpenTry = nullptr;
626 }
627 }
628 return insertAfter;
629 }
630
PlaceRelocatedBB(BBT & insertAfter)631 void TryCatchBlocksLower::PlaceRelocatedBB(BBT &insertAfter)
632 {
633 StmtNode *iaLast = insertAfter.GetLastStmt();
634 CHECK_FATAL(iaLast != nullptr, "iaLast should not nullptr");
635
636 StmtNode *iaNext = iaLast->GetNext();
637 if (iaNext == nullptr) {
638 CHECK_FATAL(body.GetLast() == iaLast, "body's last should equal iaLast");
639 }
640 BBT *prevBB = &insertAfter;
641 MapleVector<BBT *> &bbsToRelocate = tryEndTryBlock.GetBBsToRelocate();
642 for (auto &rbb : bbsToRelocate) {
643 prevBB->GetLastStmt()->SetNext(rbb->GetFirstStmt());
644 rbb->GetFirstStmt()->SetPrev(prevBB->GetLastStmt());
645 prevBB = rbb;
646 }
647 prevBB->GetLastStmt()->SetNext(iaNext);
648 if (iaNext != nullptr) {
649 iaNext->SetPrev(prevBB->GetLastStmt());
650 } else {
651 /* !ia_next means we started with insert_after that was the last bblock Refer to the above CHECK_FATAL. */
652 body.SetLast(prevBB->GetLastStmt());
653 body.GetLast()->SetNext(nullptr);
654 }
655 }
656
PalceCatchSeenSofar(BBT & insertAfter)657 void TryCatchBlocksLower::PalceCatchSeenSofar(BBT &insertAfter)
658 {
659 TryNode *tryNode = static_cast<TryNode *>(tryEndTryBlock.GetTryStmtNode());
660 DEBUG_ASSERT(tryNode != nullptr, "tryNode should not be nullptr");
661 MapleVector<BBT *> &bbsToRelocate = tryEndTryBlock.GetBBsToRelocate();
662
663 for (size_t offsetIndex = 0; offsetIndex < tryNode->GetOffsetsCount(); ++offsetIndex) {
664 auto id = tryNode->GetOffset(offsetIndex);
665 bool myCatchBlock = false;
666 for (auto &jcb : bbsToRelocate) {
667 if (!jcb->IsLabeled()) {
668 continue;
669 }
670 myCatchBlock = (id == jcb->GetLabelIdx());
671 if (myCatchBlock) {
672 break;
673 }
674 }
675 /*
676 * If the catch block is the one enclosed in this try-endtry block,
677 * we just relocated it above, so we don't need to consider it again
678 */
679 if (myCatchBlock) {
680 continue;
681 }
682
683 CHECK_FATAL(body.GetLast()->GetNext() == nullptr, "the next of body's last should be nullptr");
684 for (auto &jcb : catchesSeenSoFar) {
685 CHECK_FATAL(jcb->IsLabeled(), "jcb should be labeled");
686 if (id == jcb->GetLabelIdx()) {
687 /*
688 * Remove jcb and all of the blocks that are reachable by following fallthruBranch.
689 * If we hit a try block, cut there, append an unconditional jump to it to the preceding bblock,
690 * and relocate them. We may need to insert a label in the try block
691 */
692 BBT *lastBB = jcb;
693 while (lastBB->GetFallthruBranch() != nullptr && !lastBB->GetFallthruBranch()->IsTry()) {
694 lastBB = lastBB->GetFallthruBranch();
695 }
696
697 #if DEBUG
698 BBT::ValidateStmtList(bodyFirst);
699 #endif
700 if (lastBB->GetFallthruBranch() != nullptr) {
701 BBT *jtBB = lastBB->GetFallthruBranch();
702 CHECK_FATAL(jtBB->IsTry(), "jtBB should be try");
703 if (!jtBB->IsLabeled()) {
704 LabelIdx jtLabIdx = mirModule.GetMIRBuilder()->CreateLabIdx(*mirModule.CurFunction());
705 jtBB->SetLabelIdx(jtLabIdx);
706 StmtNode *labelStmt = mirModule.GetMIRBuilder()->CreateStmtLabel(jtLabIdx);
707 bool adjustBBFirstStmt = (jtBB->GetKeyStmt() == jtBB->GetFirstStmt());
708 labelStmt->SetNext(jtBB->GetKeyStmt());
709 labelStmt->SetPrev(jtBB->GetKeyStmt()->GetPrev());
710 CHECK_FATAL(jtBB->GetKeyStmt()->GetPrev() != nullptr,
711 "the prev of jtBB's ketStmt shpould not be nullptr");
712 jtBB->GetKeyStmt()->GetPrev()->SetNext(labelStmt);
713 CHECK_FATAL(jtBB->GetKeyStmt()->GetNext() != nullptr,
714 "the next of jtBB's ketStmt shpould not be nullptr");
715 jtBB->GetKeyStmt()->SetPrev(labelStmt);
716 if (adjustBBFirstStmt) {
717 firstStmtToBBMap.erase(jtBB->GetFirstStmt());
718 jtBB->SetFirstStmt(*labelStmt);
719 firstStmtToBBMap[jtBB->GetFirstStmt()] = jtBB;
720 }
721 }
722 CHECK_FATAL(jtBB->IsLabeled(), "jtBB should be labeled");
723 CHECK_FATAL(lastBB->GetLastStmt()->GetOpCode() != OP_goto,
724 "the opcode of lastBB's lastStmt should not be OP_goto");
725 StmtNode *gotoStmt = mirModule.GetMIRBuilder()->CreateStmtGoto(OP_goto, jtBB->GetLabelIdx());
726
727 StmtNode *lastBBLastStmt = lastBB->GetLastStmt();
728 gotoStmt->SetNext(lastBBLastStmt->GetNext());
729 gotoStmt->SetPrev(lastBBLastStmt);
730 if (lastBBLastStmt->GetNext()) {
731 lastBBLastStmt->GetNext()->SetPrev(gotoStmt);
732 }
733 lastBBLastStmt->SetNext(gotoStmt);
734
735 lastBB->SetLastStmt(*gotoStmt);
736 lastBB->SetFallthruBranch(nullptr);
737
738 #if DEBUG
739 CHECK_FATAL(body.GetLast()->GetNext() == nullptr, "the next of body's last should be nullptr");
740 BBT::ValidateStmtList(bodyFirst);
741 #endif
742 }
743
744 /* we want to remove [jcb .. last_bb], inclusively. */
745 if (jcb->GetFirstStmt() == body.GetFirst()) {
746 body.SetFirst(lastBB->GetLastStmt()->GetNext());
747 body.GetFirst()->SetPrev(nullptr);
748 lastBB->GetLastStmt()->GetNext()->SetPrev(nullptr);
749 bodyFirst = body.GetFirst();
750 } else {
751 CHECK_FATAL(jcb->GetFirstStmt()->GetPrev() != nullptr,
752 "the prev of jcb's firstStmt should not be nullptr");
753 CHECK_FATAL(jcb->GetFirstStmt()->GetPrev()->GetNext() == jcb->GetFirstStmt(),
754 "the next of the prev of jcb's firstStmt should equal jcb's firstStmt");
755 if (lastBB->GetLastStmt()->GetNext() != nullptr) {
756 jcb->GetFirstStmt()->GetPrev()->SetNext(lastBB->GetLastStmt()->GetNext());
757 lastBB->GetLastStmt()->GetNext()->SetPrev(jcb->GetFirstStmt()->GetPrev());
758 } else {
759 CHECK_FATAL(lastBB->GetLastStmt() == body.GetLast(),
760 "lastBB's lastStmt should equal body's last");
761 body.SetLast(jcb->GetFirstStmt()->GetPrev());
762 body.GetLast()->SetNext(nullptr);
763 jcb->GetFirstStmt()->GetPrev()->SetNext(nullptr);
764 }
765 }
766 jcb->GetFirstStmt()->SetPrev(nullptr);
767 lastBB->GetLastStmt()->SetNext(nullptr);
768
769 #if DEBUG
770 CHECK_FATAL(body.GetLast()->GetNext() == nullptr, "the next of body's last should be nullptr");
771 BBT::ValidateStmtList(body.GetFirst(), jcb->GetFirstStmt());
772 #endif
773
774 /* append it (i.e., [jcb->firstStmt .. last_bb->lastStmt]) after insert_after */
775 CHECK_FATAL(insertAfter.GetFallthruBranch() == nullptr,
776 "insertAfter's fallthruBranch should be nullptr");
777 if (insertAfter.GetLastStmt() == body.GetLast()) {
778 CHECK_FATAL(insertAfter.GetLastStmt()->GetNext() == nullptr,
779 "the next of insertAfter's lastStmt should not be nullptr");
780 }
781
782 jcb->GetFirstStmt()->SetPrev(insertAfter.GetLastStmt());
783 lastBB->GetLastStmt()->SetNext(insertAfter.GetLastStmt()->GetNext());
784
785 CHECK_FATAL(body.GetLast()->GetNext() == nullptr, "the next of body's last should be nullptr");
786
787 if (insertAfter.GetLastStmt()->GetNext() != nullptr) {
788 insertAfter.GetLastStmt()->GetNext()->SetPrev(lastBB->GetLastStmt());
789 CHECK_FATAL(body.GetLast()->GetNext() == nullptr, "the next of body's last should be nullptr");
790 } else {
791 /*
792 * note that we have a single BlockNode that contains all the instructions of a method.
793 * What that means is each instruction's next is not nullptr except for the very last instruction.
794 * insert_after->lastStmt->next == nullptr, means insert_after->lastStmt is indeed the last
795 * instruction, and we are moving instructions of 'last_bb' after it. Thus, we need to fix the
796 * BlockNode's last field.
797 */
798 body.SetLast(lastBB->GetLastStmt());
799 CHECK_FATAL(body.GetLast()->GetNext() == nullptr, "the next of body's last should be nullptr");
800 }
801 insertAfter.GetLastStmt()->SetNext(jcb->GetFirstStmt());
802 if (jcb->GetFirstStmt()->GetPrev() != nullptr) {
803 CHECK_FATAL(jcb->GetFirstStmt()->GetPrev()->GetNext() == jcb->GetFirstStmt(),
804 "the next of the prev of jcb's firstStmt should equal jcb's firstStmt");
805 }
806 if (lastBB->GetLastStmt()->GetNext() != nullptr) {
807 CHECK_FATAL(lastBB->GetLastStmt()->GetNext()->GetPrev() == lastBB->GetLastStmt(),
808 "thr prev of the next of lastBB's lastStmt should equal lastBB's lastStmt");
809 }
810
811 CHECK_FATAL(body.GetLast()->GetNext() == nullptr, "the next of body's last should be nullptr");
812 }
813 }
814 }
815 }
816
TraverseBBList()817 void TryCatchBlocksLower::TraverseBBList()
818 {
819 tryEndTryBlock.Init();
820 for (auto &bb : bbList) {
821 if (bb->IsCatch() && tryEndTryBlock.GetStartTryBB() == nullptr) {
822 /* Add to the list of catch blocks seen so far. */
823 catchesSeenSoFar.emplace_back(bb);
824 }
825 bodyEndWithEndTry = false;
826
827 if (tryEndTryBlock.GetStartTryBB() == nullptr) {
828 if (bb->IsTry()) {
829 StmtNode *firstNonCommentStmt = bb->GetFirstStmt();
830 while (firstNonCommentStmt != nullptr && firstNonCommentStmt->GetOpCode() == OP_comment) {
831 firstNonCommentStmt = firstNonCommentStmt->GetNext();
832 }
833 CHECK_FATAL(bb->GetLastStmt()->GetOpCode() != OP_try || bb->GetLastStmt() == firstNonCommentStmt ||
834 !generateEHCode,
835 "make sure the opcode of bb's lastStmt is not OP_try"
836 "or the opcode of bb's lastStmt is OP_try but bb's lastStmt equals firstNonCommentStmt"
837 "or not generate EHCode");
838 /* prepare for processing a java try block */
839 tryEndTryBlock.Reset(*bb);
840 }
841 continue;
842 }
843
844 /* We should have not a try block enclosed in another java try block!! */
845 CHECK_FATAL(!bb->IsTry(), "bb should not be try");
846 if (!bb->IsEndTry()) {
847 tryEndTryBlock.PushToEnclosedBBs(*bb);
848 } else {
849 tryEndTryBlock.SetEndTryBB(bb);
850 if (tryEndTryBlock.GetEndTryBB()->GetLastStmt() == body.GetLast()) {
851 bodyEndWithEndTry = true;
852 }
853 #if DEBUG
854 for (size_t i = 0; i < tryEndTryBlock.GetEnclosedBBsSize(); ++i) {
855 CHECK_FATAL(tryEndTryBlock.GetEnclosedBBsElem(i), "there should not be nullptr in enclosedBBs");
856 }
857 #endif
858 ProcessEnclosedBBBetweenTryEndTry();
859 /* Now, connect the remaining ones again n_enclosed_bbs includes 'nullptr's (i.e., deleted entries) */
860 ConnectRemainBB();
861 BBT *insertAfter = FindInsertAfterBB();
862 PlaceRelocatedBB(*insertAfter);
863
864 #if DEBUG
865 CHECK_FATAL(body.GetLast()->GetNext() == nullptr, "the next of body's last should be nullptr");
866 BBT::ValidateStmtList(bodyFirst);
867 #endif
868 if (prevBBOfTry[tryEndTryBlock.GetStartTryBB()]) {
869 StmtNode *firstStmtMovedIn =
870 MoveCondGotoIntoTry(*tryEndTryBlock.GetStartTryBB(), *prevBBOfTry[tryEndTryBlock.GetStartTryBB()],
871 tryEndTryBlock.GetLabeledBBsInTry());
872 if (firstStmtMovedIn == bodyFirst) {
873 bodyFirst = tryEndTryBlock.GetStartTryBB()->GetFirstStmt();
874 prevBBOfTry[tryEndTryBlock.GetStartTryBB()] = nullptr;
875 }
876 }
877 /*
878 * Now, examine each offset attached to this try and move any catch block
879 * that is not in 'bbs_to_relocate' but in 'catches_seen_so_far'
880 */
881 PalceCatchSeenSofar(*insertAfter);
882
883 /* close the try that is open */
884 tryEndTryBlock.SetStartTryBB(nullptr);
885 }
886 #if DEBUG
887 CHECK_FATAL(body.GetLast()->GetNext() == nullptr, "the next of body's last should be nullptr");
888 BBT::ValidateStmtList(bodyFirst);
889 #endif
890 }
891
892 body.SetFirst(bodyFirst);
893 }
894
CheckTryCatchPattern() const895 void TryCatchBlocksLower::CheckTryCatchPattern() const
896 {
897 StmtNode *openJt = nullptr;
898 for (StmtNode *stmt = body.GetFirst(); stmt; stmt = stmt->GetNext()) {
899 switch (stmt->GetOpCode()) {
900 case OP_try:
901 openJt = stmt;
902 break;
903 case OP_endtry:
904 openJt = nullptr;
905 break;
906 case OP_catch:
907 if (openJt != nullptr) {
908 CatchNode *jcn = static_cast<CatchNode *>(stmt);
909 for (uint32 i = 0; i < jcn->Size(); ++i) {
910 MIRType *type =
911 GlobalTables::GetTypeTable().GetTypeFromTyIdx(jcn->GetExceptionTyIdxVecElement(i));
912 MIRPtrType *ptr = static_cast<MIRPtrType *>(type);
913 type = GlobalTables::GetTypeTable().GetTypeFromTyIdx(ptr->GetPointedTyIdx());
914 CHECK_FATAL(type->GetPrimType() == PTY_void, "type's primType should be PTY_void");
915 }
916 }
917 break;
918 default:
919 break;
920 }
921 }
922 }
923 } /* namespace maplebe */