llvm.org GIT mirror llvm / cceb4ae
[WebAssembly] Exception handling: Switch to the new proposal Summary: This switches the EH implementation to the new proposal: https://github.com/WebAssembly/exception-handling/blob/master/proposals/Exceptions.md (The previous proposal was https://github.com/WebAssembly/exception-handling/blob/master/proposals/old/Exceptions.md) - Instruction changes - Now we have one single `catch` instruction that returns a except_ref value - `throw` now can take variable number of operations - `rethrow` does not have 'depth' argument anymore - `br_on_exn` queries an except_ref to see if it matches the tag and branches to the given label if true. - `extract_exception` is a pseudo instruction that simulates popping values from wasm stack. This is to make `br_on_exn`, a very special instruction, work: `br_on_exn` puts values onto the stack only if it is taken, and the # of values can vay depending on the tag. - Now there's only one `catch` per `try`, this patch removes all special handling for terminate pad with a call to `__clang_call_terminate`. Before it was the only case there are two catch clauses (a normal `catch` and `catch_all` per `try`). - Make `rethrow` act as a terminator like `throw`. This splits BB after `rethrow` in WasmEHPrepare, and deletes an unnecessary `unreachable` after `rethrow` in LateEHPrepare. - Now we stop at all catchpads (because we add wasm `catch` instruction that catches all exceptions), this creates new `findWasmUnwindDestinations` function in SelectionDAGBuilder. - Now we use `br_on_exn` instrution to figure out if an except_ref matches the current tag or not, LateEHPrepare generates this sequence for catch pads: ``` catch block i32 br_on_exn $__cpp_exception end_block extract_exception ``` - Branch analysis for `br_on_exn` in WebAssemblyInstrInfo - Other various misc. changes to switch to the new proposal. Reviewers: dschuff Subscribers: sbc100, jgravelle-google, sunfish, llvm-commits Differential Revision: https://reviews.llvm.org/D57134 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@352598 91177308-0d34-0410-b5e6-96231b3b80d8 Heejin Ahn 1 year, 2 months ago
28 changed file(s) with 760 addition(s) and 1351 deletion(s). Raw diff Collapse all Expand all
4848 [IntrHasSideEffects]>;
4949 def int_wasm_get_ehselector : Intrinsic<[llvm_i32_ty], [llvm_token_ty],
5050 [IntrHasSideEffects]>;
51
52 // wasm.catch returns the pointer to the exception object caught by wasm 'catch'
53 // instruction.
54 def int_wasm_catch : Intrinsic<[llvm_ptr_ty], [llvm_i32_ty],
55 [IntrHasSideEffects]>;
51 // This is the same as llvm.wasm.get.exception except that it does not take a
52 // token operand. This is only for instruction selection purpose.
53 // TODO Remove this redundant intrinsic and do custom lowering on
54 // int_wasm_get_exception instead
55 def int_wasm_extract_exception : Intrinsic<[llvm_ptr_ty], [],
56 [IntrHasSideEffects]>;
5657
5758 // WebAssembly EH must maintain the landingpads in the order assigned to them
5859 // by WasmEHPrepare pass to generate landingpad table in EHStreamer. This is
1717 using namespace llvm;
1818
1919 void WasmException::endModule() {
20 // This is the symbol used in 'throw' and 'if_except' instruction to denote
20 // This is the symbol used in 'throw' and 'br_on_exn' instruction to denote
2121 // this is a C++ exception. This symbol has to be emitted somewhere once in
2222 // the module. Check if the symbol has already been created, i.e., we have at
23 // least one 'throw' or 'if_except' instruction in the module, and emit the
23 // least one 'throw' or 'br_on_exn' instruction in the module, and emit the
2424 // symbol only if so.
2525 SmallString<60> NameStr;
2626 Mangler::getNameWithPrefix(NameStr, "__cpp_exception", Asm->getDataLayout());
14551455 }
14561456 }
14571457
1458 // For wasm, there's alwyas a single catch pad attached to a catchswitch, and
1459 // the control flow always stops at the single catch pad, as it does for a
1460 // cleanup pad. In case the exception caught is not of the types the catch pad
1461 // catches, it will be rethrown by a rethrow.
1462 static void findWasmUnwindDestinations(
1463 FunctionLoweringInfo &FuncInfo, const BasicBlock *EHPadBB,
1464 BranchProbability Prob,
1465 SmallVectorImpl>
1466 &UnwindDests) {
1467 while (EHPadBB) {
1468 const Instruction *Pad = EHPadBB->getFirstNonPHI();
1469 if (isa(Pad)) {
1470 // Stop on cleanup pads.
1471 UnwindDests.emplace_back(FuncInfo.MBBMap[EHPadBB], Prob);
1472 UnwindDests.back().first->setIsEHScopeEntry();
1473 break;
1474 } else if (auto *CatchSwitch = dyn_cast(Pad)) {
1475 // Add the catchpad handlers to the possible destinations. We don't
1476 // continue to the unwind destination of the catchswitch for wasm.
1477 for (const BasicBlock *CatchPadBB : CatchSwitch->handlers()) {
1478 UnwindDests.emplace_back(FuncInfo.MBBMap[CatchPadBB], Prob);
1479 UnwindDests.back().first->setIsEHScopeEntry();
1480 }
1481 break;
1482 } else {
1483 continue;
1484 }
1485 }
1486 }
1487
14581488 /// When an invoke or a cleanupret unwinds to the next EH pad, there are
14591489 /// many places it could ultimately go. In the IR, we have a single unwind
14601490 /// destination, but in the machine CFG, we enumerate all the possible blocks.
14751505 bool IsWasmCXX = Personality == EHPersonality::Wasm_CXX;
14761506 bool IsSEH = isAsynchronousEHPersonality(Personality);
14771507
1508 if (IsWasmCXX) {
1509 findWasmUnwindDestinations(FuncInfo, EHPadBB, Prob, UnwindDests);
1510 return;
1511 }
1512
14781513 while (EHPadBB) {
14791514 const Instruction *Pad = EHPadBB->getFirstNonPHI();
14801515 BasicBlock *NewEHPadBB = nullptr;
14871522 // personalities.
14881523 UnwindDests.emplace_back(FuncInfo.MBBMap[EHPadBB], Prob);
14891524 UnwindDests.back().first->setIsEHScopeEntry();
1490 if (!IsWasmCXX)
1491 UnwindDests.back().first->setIsEHFuncletEntry();
1525 UnwindDests.back().first->setIsEHFuncletEntry();
14921526 break;
14931527 } else if (auto *CatchSwitch = dyn_cast(Pad)) {
14941528 // Add the catchpad handlers to the possible destinations.
66 //===----------------------------------------------------------------------===//
77 //
88 // This transformation is designed for use by code generators which use
9 // WebAssembly exception handling scheme.
9 // WebAssembly exception handling scheme. This currently supports C++
10 // exceptions.
1011 //
1112 // WebAssembly exception handling uses Windows exception IR for the middle level
1213 // representation. This pass does the following transformation for every
2122 //
2223 // - After:
2324 // catchpad ...
24 // exn = wasm.catch(0); // 0 is a tag for C++
25 // exn = wasm.extract.exception();
26 // // Only add below in case it's not a single catch (...)
2527 // wasm.landingpad.index(index);
26 // // Only add below in case it's not a single catch (...)
2728 // __wasm_lpad_context.lpad_index = index;
2829 // __wasm_lpad_context.lsda = wasm.lsda();
2930 // _Unwind_CallPersonality(exn);
30 // int selector = __wasm.landingpad_context.selector;
31 // selector = __wasm.landingpad_context.selector;
3132 // ...
32 //
33 // Also, does the following for a cleanuppad block with a call to
34 // __clang_call_terminate():
35 // - Before:
36 // cleanuppad ...
37 // exn = wasm.get.exception();
38 // __clang_call_terminate(exn);
39 //
40 // - After:
41 // cleanuppad ...
42 // exn = wasm.catch(0); // 0 is a tag for C++
43 // __clang_call_terminate(exn);
44 //
45 //
46 // * Background: WebAssembly EH instructions
47 // WebAssembly's try and catch instructions are structured as follows:
48 // try
49 // instruction*
50 // catch (C++ tag)
51 // instruction*
52 // ...
53 // catch_all
54 // instruction*
55 // try_end
56 //
57 // A catch instruction in WebAssembly does not correspond to a C++ catch clause.
58 // In WebAssembly, there is a single catch instruction for all C++ exceptions.
59 // There can be more catch instructions for exceptions in other languages, but
60 // they are not generated for now. catch_all catches all exceptions including
61 // foreign exceptions (e.g. JavaScript). We turn catchpads into catch (C++ tag)
62 // and cleanuppads into catch_all, with one exception: cleanuppad with a call to
63 // __clang_call_terminate should be both in catch (C++ tag) and catch_all.
6433 //
6534 //
6635 // * Background: Direct personality function call
6736 // In WebAssembly EH, the VM is responsible for unwinding the stack once an
6837 // exception is thrown. After the stack is unwound, the control flow is
69 // transfered to WebAssembly 'catch' instruction, which returns a caught
70 // exception object.
38 // transfered to WebAssembly 'catch' instruction.
7139 //
7240 // Unwinding the stack is not done by libunwind but the VM, so the personality
7341 // function in libcxxabi cannot be called from libunwind during the unwinding
136104 Value *SelectorField = nullptr; // selector
137105
138106 Function *ThrowF = nullptr; // wasm.throw() intrinsic
139 Function *CatchF = nullptr; // wasm.catch.extract() intrinsic
107 Function *RethrowF = nullptr; // wasm.rethrow() intrinsic
140108 Function *LPadIndexF = nullptr; // wasm.landingpad.index() intrinsic
141109 Function *LSDAF = nullptr; // wasm.lsda() intrinsic
142110 Function *GetExnF = nullptr; // wasm.get.exception() intrinsic
111 Function *ExtractExnF = nullptr; // wasm.extract.exception() intrinsic
143112 Function *GetSelectorF = nullptr; // wasm.get.ehselector() intrinsic
144113 Function *CallPersonalityF = nullptr; // _Unwind_CallPersonality() wrapper
145 Function *ClangCallTermF = nullptr; // __clang_call_terminate() function
146114
147115 bool prepareEHPads(Function &F);
148116 bool prepareThrows(Function &F);
149117
150 void prepareEHPad(BasicBlock *BB, unsigned Index);
118 void prepareEHPad(BasicBlock *BB, bool NeedLSDA, unsigned Index = 0);
151119 void prepareTerminateCleanupPad(BasicBlock *BB);
152120
153121 public:
207175
208176 // wasm.throw() intinsic, which will be lowered to wasm 'throw' instruction.
209177 ThrowF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_throw);
210
211 // Insert an unreachable instruction after a call to @llvm.wasm.throw and
212 // delete all following instructions within the BB, and delete all the dead
213 // children of the BB as well.
214 for (User *U : ThrowF->users()) {
215 // A call to @llvm.wasm.throw() is only generated from
216 // __builtin_wasm_throw() builtin call within libcxxabi, and cannot be an
217 // InvokeInst.
218 auto *ThrowI = cast(U);
219 if (ThrowI->getFunction() != &F)
220 continue;
221 Changed = true;
222 auto *BB = ThrowI->getParent();
223 SmallVector Succs(succ_begin(BB), succ_end(BB));
224 auto &InstList = BB->getInstList();
225 InstList.erase(std::next(BasicBlock::iterator(ThrowI)), InstList.end());
226 IRB.SetInsertPoint(BB);
227 IRB.CreateUnreachable();
228 eraseDeadBBsAndChildren(Succs);
178 // wasm.rethrow() intinsic, which will be lowered to wasm 'rethrow'
179 // instruction.
180 RethrowF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_rethrow);
181
182 // Insert an unreachable instruction after a call to @llvm.wasm.throw /
183 // @llvm.wasm.rethrow and delete all following instructions within the BB, and
184 // delete all the dead children of the BB as well.
185 for (auto L : {ThrowF->users(), RethrowF->users()}) {
186 for (User *U : L) {
187 // A call to @llvm.wasm.throw() is only generated from __cxa_throw()
188 // builtin call within libcxxabi, and cannot be an InvokeInst.
189 auto *ThrowI = cast(U);
190 if (ThrowI->getFunction() != &F)
191 continue;
192 Changed = true;
193 auto *BB = ThrowI->getParent();
194 SmallVector Succs(succ_begin(BB), succ_end(BB));
195 auto &InstList = BB->getInstList();
196 InstList.erase(std::next(BasicBlock::iterator(ThrowI)), InstList.end());
197 IRB.SetInsertPoint(BB);
198 IRB.CreateUnreachable();
199 eraseDeadBBsAndChildren(Succs);
200 }
229201 }
230202
231203 return Changed;
261233 SelectorField = IRB.CreateConstGEP2_32(LPadContextTy, LPadContextGV, 0, 2,
262234 "selector_gep");
263235
264 // wasm.catch() intinsic, which will be lowered to wasm 'catch' instruction.
265 CatchF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_catch);
266236 // wasm.landingpad.index() intrinsic, which is to specify landingpad index
267237 LPadIndexF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_landingpad_index);
268238 // wasm.lsda() intrinsic. Returns the address of LSDA table for the current
273243 GetExnF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_get_exception);
274244 GetSelectorF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_get_ehselector);
275245
246 // wasm.extract.exception() is the same as wasm.get.exception() but it does
247 // not take a token argument. This will be lowered down to EXTRACT_EXCEPTION
248 // pseudo instruction in instruction selection, which will be expanded using
249 // 'br_on_exn' instruction later.
250 ExtractExnF =
251 Intrinsic::getDeclaration(&M, Intrinsic::wasm_extract_exception);
252
276253 // _Unwind_CallPersonality() wrapper function, which calls the personality
277254 CallPersonalityF = cast(M.getOrInsertFunction(
278255 "_Unwind_CallPersonality", IRB.getInt32Ty(), IRB.getInt8PtrTy()));
279256 CallPersonalityF->setDoesNotThrow();
280
281 // __clang_call_terminate() function, which is inserted by clang in case a
282 // cleanup throws
283 ClangCallTermF = M.getFunction("__clang_call_terminate");
284257
285258 unsigned Index = 0;
286259 for (auto *BB : CatchPads) {
288261 // In case of a single catch (...), we don't need to emit LSDA
289262 if (CPI->getNumArgOperands() == 1 &&
290263 cast(CPI->getArgOperand(0))->isNullValue())
291 prepareEHPad(BB, -1);
264 prepareEHPad(BB, false);
292265 else
293 prepareEHPad(BB, Index++);
294 }
295
296 if (!ClangCallTermF)
297 return !CatchPads.empty();
298
299 // Cleanuppads will turn into catch_all later, but cleanuppads with a call to
300 // __clang_call_terminate() is a special case. __clang_call_terminate() takes
301 // an exception object, so we have to duplicate call in both 'catch '
302 // and 'catch_all' clauses. Here we only insert a call to catch; the
303 // duplication will be done later. In catch_all, the exception object will be
304 // set to null.
266 prepareEHPad(BB, true, Index++);
267 }
268
269 // Cleanup pads don't need LSDA.
305270 for (auto *BB : CleanupPads)
306 for (auto &I : *BB)
307 if (auto *CI = dyn_cast(&I))
308 if (CI->getCalledValue() == ClangCallTermF)
309 prepareEHPad(BB, -1);
271 prepareEHPad(BB, false);
310272
311273 return true;
312274 }
313275
314 void WasmEHPrepare::prepareEHPad(BasicBlock *BB, unsigned Index) {
276 // Prepare an EH pad for Wasm EH handling. If NeedLSDA is false, Index is
277 // ignored.
278 void WasmEHPrepare::prepareEHPad(BasicBlock *BB, bool NeedLSDA,
279 unsigned Index) {
315280 assert(BB->isEHPad() && "BB is not an EHPad!");
316281 IRBuilder<> IRB(BB->getContext());
317
318282 IRB.SetInsertPoint(&*BB->getFirstInsertionPt());
319 // The argument to wasm.catch() is the tag for C++ exceptions, which we set to
320 // 0 for this module.
321 // Pseudocode: void *exn = wasm.catch(0);
322 Instruction *Exn = IRB.CreateCall(CatchF, IRB.getInt32(0), "exn");
323 // Replace the return value of wasm.get.exception() with the return value from
324 // wasm.catch().
283
325284 auto *FPI = cast(BB->getFirstNonPHI());
326285 Instruction *GetExnCI = nullptr, *GetSelectorCI = nullptr;
327286 for (auto &U : FPI->uses()) {
328287 if (auto *CI = dyn_cast(U.getUser())) {
329288 if (CI->getCalledValue() == GetExnF)
330289 GetExnCI = CI;
331 else if (CI->getCalledValue() == GetSelectorF)
290 if (CI->getCalledValue() == GetSelectorF)
332291 GetSelectorCI = CI;
333292 }
334293 }
335294
336 assert(GetExnCI && "wasm.get.exception() call does not exist");
337 GetExnCI->replaceAllUsesWith(Exn);
295 // Cleanup pads w/o __clang_call_terminate call do not have any of
296 // wasm.get.exception() or wasm.get.ehselector() calls. We need to do nothing.
297 if (!GetExnCI) {
298 assert(!GetSelectorCI &&
299 "wasm.get.ehselector() cannot exist w/o wasm.get.exception()");
300 return;
301 }
302
303 Instruction *ExtractExnCI = IRB.CreateCall(ExtractExnF, {}, "exn");
304 GetExnCI->replaceAllUsesWith(ExtractExnCI);
338305 GetExnCI->eraseFromParent();
339306
340307 // In case it is a catchpad with single catch (...) or a cleanuppad, we don't
341308 // need to call personality function because we don't need a selector.
342 if (FPI->getNumArgOperands() == 0 ||
343 (FPI->getNumArgOperands() == 1 &&
344 cast(FPI->getArgOperand(0))->isNullValue())) {
309 if (!NeedLSDA) {
345310 if (GetSelectorCI) {
346311 assert(GetSelectorCI->use_empty() &&
347312 "wasm.get.ehselector() still has uses!");
349314 }
350315 return;
351316 }
352 IRB.SetInsertPoint(Exn->getNextNode());
317 IRB.SetInsertPoint(ExtractExnCI->getNextNode());
353318
354319 // This is to create a map of in
355320 // SelectionDAGISel, which is to be used in EHStreamer to emit LSDA tables.
371336 IRB.CreateStore(IRB.CreateCall(LSDAF), LSDAField);
372337
373338 // Pseudocode: _Unwind_CallPersonality(exn);
374 CallInst *PersCI =
375 IRB.CreateCall(CallPersonalityF, Exn, OperandBundleDef("funclet", CPI));
339 CallInst *PersCI = IRB.CreateCall(CallPersonalityF, ExtractExnCI,
340 OperandBundleDef("funclet", CPI));
376341 PersCI->setDoesNotThrow();
377342
378343 // Pseudocode: int selector = __wasm.landingpad_context.selector;
386351 }
387352
388353 void llvm::calculateWasmEHInfo(const Function *F, WasmEHFuncInfo &EHInfo) {
354 // If an exception is not caught by a catchpad (i.e., it is a foreign
355 // exception), it will unwind to its parent catchswitch's unwind destination.
356 // We don't record an unwind destination for cleanuppads because every
357 // exception should be caught by it.
389358 for (const auto &BB : *F) {
390359 if (!BB.isEHPad())
391360 continue;
392361 const Instruction *Pad = BB.getFirstNonPHI();
393362
394 // If an exception is not caught by a catchpad (i.e., it is a foreign
395 // exception), it will unwind to its parent catchswitch's unwind
396 // destination. We don't record an unwind destination for cleanuppads
397 // because every exception should be caught by it.
398363 if (const auto *CatchPad = dyn_cast(Pad)) {
399364 const auto *UnwindBB = CatchPad->getCatchSwitch()->getUnwindDest();
400365 if (!UnwindBB)
121121 }
122122 break;
123123
124 case WebAssembly::CATCH_I32:
125 case WebAssembly::CATCH_I32_S:
126 case WebAssembly::CATCH_I64:
127 case WebAssembly::CATCH_I64_S:
128 case WebAssembly::CATCH_ALL:
129 case WebAssembly::CATCH_ALL_S:
130 // There can be multiple catch instructions for one try instruction, so we
131 // print a label only for the first 'catch' label.
132 if (LastSeenEHInst != CATCH) {
133 if (EHPadStack.empty()) {
134 printAnnotation(OS, "try-catch mismatch!");
124 case WebAssembly::CATCH:
125 case WebAssembly::CATCH_S:
126 if (EHPadStack.empty()) {
127 printAnnotation(OS, "try-catch mismatch!");
128 } else {
129 printAnnotation(OS, "catch" + utostr(EHPadStack.pop_back_val()) + ':');
130 }
131 break;
132 }
133
134 // Annotate any control flow label references.
135
136 // rethrow instruction does not take any depth argument and rethrows to the
137 // nearest enclosing catch scope, if any. If there's no enclosing catch
138 // scope, it throws up to the caller.
139 if (Opc == WebAssembly::RETHROW || Opc == WebAssembly::RETHROW_S) {
140 if (EHPadStack.empty()) {
141 printAnnotation(OS, "to caller");
142 } else {
143 printAnnotation(OS, "down to catch" + utostr(EHPadStack.back()));
144 }
145
146 } else {
147 unsigned NumFixedOperands = Desc.NumOperands;
148 SmallSet Printed;
149 for (unsigned I = 0, E = MI->getNumOperands(); I < E; ++I) {
150 // See if this operand denotes a basic block target.
151 if (I < NumFixedOperands) {
152 // A non-variable_ops operand, check its type.
153 if (Desc.OpInfo[I].OperandType != WebAssembly::OPERAND_BASIC_BLOCK)
154 continue;
135155 } else {
136 printAnnotation(OS,
137 "catch" + utostr(EHPadStack.pop_back_val()) + ':');
156 // A variable_ops operand, which currently can be immediates (used in
157 // br_table) which are basic block targets, or for call instructions
158 // when using -wasm-keep-registers (in which case they are registers,
159 // and should not be processed).
160 if (!MI->getOperand(I).isImm())
161 continue;
138162 }
139 }
140 LastSeenEHInst = CATCH;
141 break;
142 }
143
144 // Annotate any control flow label references.
145 unsigned NumFixedOperands = Desc.NumOperands;
146 SmallSet Printed;
147 for (unsigned i = 0, e = MI->getNumOperands(); i < e; ++i) {
148 // See if this operand denotes a basic block target.
149 if (i < NumFixedOperands) {
150 // A non-variable_ops operand, check its type.
151 if (Desc.OpInfo[i].OperandType != WebAssembly::OPERAND_BASIC_BLOCK)
163 uint64_t Depth = MI->getOperand(I).getImm();
164 if (!Printed.insert(Depth).second)
152165 continue;
153 } else {
154 // A variable_ops operand, which currently can be immediates (used in
155 // br_table) which are basic block targets, or for call instructions
156 // when using -wasm-keep-registers (in which case they are registers,
157 // and should not be processed).
158 if (!MI->getOperand(i).isImm())
159 continue;
160 }
161 uint64_t Depth = MI->getOperand(i).getImm();
162 if (!Printed.insert(Depth).second)
163 continue;
164
165 if (Opc == WebAssembly::RETHROW || Opc == WebAssembly::RETHROW_S) {
166 if (Depth > EHPadStack.size()) {
167 printAnnotation(OS, "Invalid depth argument!");
168 } else if (Depth == EHPadStack.size()) {
169 // This can happen when rethrow instruction breaks out of all nests
170 // and throws up to the current function's caller.
171 printAnnotation(OS, utostr(Depth) + ": " + "to caller");
172 } else {
173 uint64_t CatchNo = EHPadStack.rbegin()[Depth];
174 printAnnotation(OS, utostr(Depth) + ": " + "down to catch" +
175 utostr(CatchNo));
176 }
177
178 } else {
179166 if (Depth >= ControlFlowStack.size()) {
180167 printAnnotation(OS, "Invalid depth argument!");
181168 } else {
4141 using namespace llvm;
4242
4343 #define DEBUG_TYPE "asm-printer"
44
45 extern cl::opt WasmKeepRegisters;
4446
4547 //===----------------------------------------------------------------------===//
4648 // Helpers.
303305 OutStreamer->AddBlankLine();
304306 }
305307 break;
308 case WebAssembly::EXTRACT_EXCEPTION_I32:
309 case WebAssembly::EXTRACT_EXCEPTION_I32_S:
310 // These are pseudo instructions that simulates popping values from stack.
311 // We print these only when we have -wasm-keep-registers on for assembly
312 // readability.
313 if (!WasmKeepRegisters)
314 break;
315 LLVM_FALLTHROUGH;
306316 default: {
307317 WebAssemblyMCInstLower MCInstLowering(OutContext, *this);
308318 MCInst TmpInst;
3636 #include "llvm/MC/MCAsmInfo.h"
3737 #include "llvm/Support/Debug.h"
3838 #include "llvm/Support/raw_ostream.h"
39 #include
3940 using namespace llvm;
4041
4142 #define DEBUG_TYPE "wasm-cfg-stackify"
109110 static bool ExplicitlyBranchesTo(MachineBasicBlock *Pred,
110111 MachineBasicBlock *MBB) {
111112 for (MachineInstr &MI : Pred->terminators())
112 // Even if a rethrow takes a BB argument, it is not a branch
113 if (!WebAssembly::isRethrow(MI))
114 for (MachineOperand &MO : MI.explicit_operands())
115 if (MO.isMBB() && MO.getMBB() == MBB)
116 return true;
113 for (MachineOperand &MO : MI.explicit_operands())
114 if (MO.isMBB() && MO.getMBB() == MBB)
115 return true;
117116 return false;
118117 }
119118
216215 // which reduces overall stack height.
217216 MachineBasicBlock *Header = nullptr;
218217 bool IsBranchedTo = false;
218 bool IsBrOnExn = false;
219 MachineInstr *BrOnExn = nullptr;
219220 int MBBNumber = MBB.getNumber();
220221 for (MachineBasicBlock *Pred : MBB.predecessors()) {
221222 if (Pred->getNumber() < MBBNumber) {
222223 Header = Header ? MDT.findNearestCommonDominator(Header, Pred) : Pred;
223 if (ExplicitlyBranchesTo(Pred, &MBB))
224 if (ExplicitlyBranchesTo(Pred, &MBB)) {
224225 IsBranchedTo = true;
226 if (Pred->getFirstTerminator()->getOpcode() == WebAssembly::BR_ON_EXN) {
227 IsBrOnExn = true;
228 assert(!BrOnExn && "There should be only one br_on_exn per block");
229 BrOnExn = &*Pred->getFirstTerminator();
230 }
231 }
225232 }
226233 }
227234 if (!Header)
298305 }
299306
300307 // Add the BLOCK.
308
309 // 'br_on_exn' extracts except_ref object and pushes variable number of values
310 // depending on its tag. For C++ exception, its a single i32 value, and the
311 // generated code will be in the form of:
312 // block i32
313 // br_on_exn 0, $__cpp_exception
314 // rethrow
315 // end_block
316 WebAssembly::ExprType ReturnType = WebAssembly::ExprType::Void;
317 if (IsBrOnExn) {
318 const char *TagName = BrOnExn->getOperand(1).getSymbolName();
319 if (std::strcmp(TagName, "__cpp_exception") != 0)
320 llvm_unreachable("Only C++ exception is supported");
321 ReturnType = WebAssembly::ExprType::I32;
322 }
323
301324 auto InsertPos = GetLatestInsertPos(Header, BeforeSet, AfterSet);
302325 MachineInstr *Begin =
303326 BuildMI(*Header, InsertPos, Header->findDebugLoc(InsertPos),
304327 TII.get(WebAssembly::BLOCK))
305 .addImm(int64_t(WebAssembly::ExprType::Void));
328 .addImm(int64_t(ReturnType));
306329
307330 // Decide where in Header to put the END_BLOCK.
308331 BeforeSet.clear();
413436
414437 void WebAssemblyCFGStackify::placeTryMarker(MachineBasicBlock &MBB) {
415438 if (!MBB.isEHPad())
416 return;
417
418 // catch_all terminate pad is grouped together with catch terminate pad and
419 // does not need a separate TRY and END_TRY marker.
420 if (WebAssembly::isCatchAllTerminatePad(MBB))
421439 return;
422440
423441 MachineFunction &MF = *MBB.getParent();
528546 // throw.
529547 if (MBB.isPredecessor(Header)) {
530548 auto TermPos = Header->getFirstTerminator();
531 if (TermPos == Header->end() || !WebAssembly::isRethrow(*TermPos)) {
549 if (TermPos == Header->end() ||
550 TermPos->getOpcode() != WebAssembly::RETHROW) {
532551 for (const auto &MI : reverse(*Header)) {
533552 if (MI.isCall()) {
534553 AfterSet.insert(&MI);
673692
674693 /// Insert LOOP/TRY/BLOCK markers at appropriate places.
675694 void WebAssemblyCFGStackify::placeMarkers(MachineFunction &MF) {
676 const MCAsmInfo *MCAI = MF.getTarget().getMCAsmInfo();
677695 // We allocate one more than the number of blocks in the function to
678696 // accommodate for the possible fake block we may insert at the end.
679697 ScopeTops.resize(MF.getNumBlockIDs() + 1);
681699 for (auto &MBB : MF)
682700 placeLoopMarker(MBB);
683701 // Place the TRY for MBB if MBB is the EH pad of an exception.
702 const MCAsmInfo *MCAI = MF.getTarget().getMCAsmInfo();
684703 if (MCAI->getExceptionHandlingType() == ExceptionHandling::Wasm &&
685704 MF.getFunction().hasPersonalityFn())
686705 for (auto &MBB : MF)
691710 }
692711
693712 void WebAssemblyCFGStackify::rewriteDepthImmediates(MachineFunction &MF) {
694 const auto &TII = *MF.getSubtarget().getInstrInfo();
695713 // Now rewrite references to basic blocks to be depth immediates.
696 // We need two stacks: one for normal scopes and the other for EH pad scopes.
697 // EH pad stack is used to rewrite depths in rethrow instructions.
698714 SmallVector Stack;
699 SmallVector EHPadStack;
700715 for (auto &MBB : reverse(MF)) {
701716 for (auto I = MBB.rbegin(), E = MBB.rend(); I != E; ++I) {
702717 MachineInstr &MI = *I;
713728 MBB.getNumber() &&
714729 "Block/try marker should be balanced");
715730 Stack.pop_back();
716 EHPadStack.pop_back();
717 break;
718
719 case WebAssembly::CATCH_I32:
720 case WebAssembly::CATCH_I64:
721 case WebAssembly::CATCH_ALL:
722 // Currently the only case there are more than one catch for a try is
723 // for catch terminate pad, in the form of
724 // try
725 // catch
726 // call @__clang_call_terminate
727 // unreachable
728 // catch_all
729 // call @std::terminate
730 // unreachable
731 // end
732 // So we shouldn't push the current BB for the second catch_all block
733 // here.
734 if (!WebAssembly::isCatchAllTerminatePad(MBB))
735 EHPadStack.push_back(&MBB);
736731 break;
737732
738733 case WebAssembly::LOOP:
748743 case WebAssembly::END_LOOP:
749744 Stack.push_back(EndToBegin[&MI]->getParent());
750745 break;
751
752 case WebAssembly::RETHROW: {
753 // Rewrite MBB operands to be depth immediates.
754 unsigned EHPadDepth = GetDepth(EHPadStack, MI.getOperand(0).getMBB());
755 MI.RemoveOperand(0);
756 MI.addOperand(MF, MachineOperand::CreateImm(EHPadDepth));
757 break;
758 }
759
760 case WebAssembly::RETHROW_TO_CALLER: {
761 MachineInstr *Rethrow =
762 BuildMI(MBB, MI, MI.getDebugLoc(), TII.get(WebAssembly::RETHROW))
763 .addImm(EHPadStack.size());
764 MI.eraseFromParent();
765 I = MachineBasicBlock::reverse_iterator(Rethrow);
766 break;
767 }
768746
769747 default:
770748 if (MI.isTerminator()) {
7676 // function uses the red zone, but that only happens with leaf functions,
7777 // and we don't restore __stack_pointer in leaf functions anyway.
7878 auto InsertPos = MBB.begin();
79 if (WebAssembly::isCatch(*MBB.begin()))
79 if (MBB.begin()->getOpcode() == WebAssembly::CATCH)
8080 InsertPos++;
8181 FrameLowering->writeSPToGlobal(WebAssembly::SP32, MF, MBB, InsertPos,
8282 MBB.begin()->getDebugLoc());
4949 MachineBasicBlock *EHPad = DomNode->getBlock();
5050 if (!EHPad->isEHPad())
5151 continue;
52 // We group catch & catch-all terminate pads together, so skip the second
53 // one
54 if (WebAssembly::isCatchAllTerminatePad(*EHPad))
55 continue;
5652 auto *WE = new WebAssemblyException(EHPad);
5753 discoverAndMapException(WE, MDT, MDF);
5854 Exceptions.push_back(WE);
10399
104100 // Map blocks that belong to a catchpad / cleanuppad
105101 MachineBasicBlock *EHPad = WE->getEHPad();
106
107 // We group catch & catch-all terminate pads together within an exception
108 if (WebAssembly::isCatchTerminatePad(*EHPad)) {
109 assert(EHPad->succ_size() == 1 &&
110 "Catch terminate pad has more than one successors");
111 changeExceptionFor(EHPad, WE);
112 changeExceptionFor(*(EHPad->succ_begin()), WE);
113 return;
114 }
115
116102 SmallVector WL;
117103 WL.push_back(EHPad);
118104 while (!WL.empty()) {
883883 return LowerFRAMEADDR(Op, DAG);
884884 case ISD::CopyToReg:
885885 return LowerCopyToReg(Op, DAG);
886 case ISD::INTRINSIC_WO_CHAIN:
887 return LowerINTRINSIC_WO_CHAIN(Op, DAG);
888886 case ISD::EXTRACT_VECTOR_ELT:
889887 case ISD::INSERT_VECTOR_ELT:
890888 return LowerAccessVectorElement(Op, DAG);
891889 case ISD::INTRINSIC_VOID:
892 return LowerINTRINSIC_VOID(Op, DAG);
890 case ISD::INTRINSIC_WO_CHAIN:
891 case ISD::INTRINSIC_W_CHAIN:
892 return LowerIntrinsic(Op, DAG);
893893 case ISD::SIGN_EXTEND_INREG:
894894 return LowerSIGN_EXTEND_INREG(Op, DAG);
895895 case ISD::BUILD_VECTOR:
10341034 MachinePointerInfo(SV), 0);
10351035 }
10361036
1037 SDValue
1038 WebAssemblyTargetLowering::LowerINTRINSIC_WO_CHAIN(SDValue Op,
1039 SelectionDAG &DAG) const {
1040 unsigned IntNo = cast(Op.getOperand(0))->getZExtValue();
1037 SDValue WebAssemblyTargetLowering::LowerIntrinsic(SDValue Op,
1038 SelectionDAG &DAG) const {
1039 MachineFunction &MF = DAG.getMachineFunction();
1040 unsigned IntNo;
1041 switch (Op.getOpcode()) {
1042 case ISD::INTRINSIC_VOID:
1043 case ISD::INTRINSIC_W_CHAIN:
1044 IntNo = cast(Op.getOperand(1))->getZExtValue();
1045 break;
1046 case ISD::INTRINSIC_WO_CHAIN:
1047 IntNo = cast(Op.getOperand(0))->getZExtValue();
1048 break;
1049 default:
1050 llvm_unreachable("Invalid intrinsic");
1051 }
10411052 SDLoc DL(Op);
1053
10421054 switch (IntNo) {
10431055 default:
10441056 return {}; // Don't custom lower most intrinsics.
10451057
10461058 case Intrinsic::wasm_lsda: {
1047 MachineFunction &MF = DAG.getMachineFunction();
10481059 EVT VT = Op.getValueType();
10491060 const TargetLowering &TLI = DAG.getTargetLoweringInfo();
10501061 MVT PtrVT = TLI.getPointerTy(DAG.getDataLayout());
10541065 return DAG.getNode(WebAssemblyISD::Wrapper, DL, VT,
10551066 DAG.getMCSymbol(S, PtrVT));
10561067 }
1057 }
1058 }
1059
1060 SDValue
1061 WebAssemblyTargetLowering::LowerINTRINSIC_VOID(SDValue Op,
1062 SelectionDAG &DAG) const {
1063 MachineFunction &MF = DAG.getMachineFunction();
1064 unsigned IntNo = cast(Op.getOperand(1))->getZExtValue();
1065 SDLoc DL(Op);
1066
1067 switch (IntNo) {
1068 default:
1069 return {}; // Don't custom lower most intrinsics.
10701068
10711069 case Intrinsic::wasm_throw: {
1070 // We only support C++ exceptions for now
10721071 int Tag = cast(Op.getOperand(2).getNode())->getZExtValue();
1073 switch (Tag) {
1074 case CPP_EXCEPTION: {
1075 const TargetLowering &TLI = DAG.getTargetLoweringInfo();
1076 MVT PtrVT = TLI.getPointerTy(DAG.getDataLayout());
1077 const char *SymName = MF.createExternalSymbolName("__cpp_exception");
1078 SDValue SymNode =
1079 DAG.getNode(WebAssemblyISD::Wrapper, DL, PtrVT,
1080 DAG.getTargetExternalSymbol(
1081 SymName, PtrVT, WebAssemblyII::MO_SYMBOL_EVENT));
1082 return DAG.getNode(WebAssemblyISD::THROW, DL,
1083 MVT::Other, // outchain type
1084 {
1085 Op.getOperand(0), // inchain
1086 SymNode, // exception symbol
1087 Op.getOperand(3) // thrown value
1088 });
1089 }
1090 default:
1072 if (Tag != CPP_EXCEPTION)
10911073 llvm_unreachable("Invalid tag!");
1092 }
1093 break;
1074 const TargetLowering &TLI = DAG.getTargetLoweringInfo();
1075 MVT PtrVT = TLI.getPointerTy(DAG.getDataLayout());
1076 const char *SymName = MF.createExternalSymbolName("__cpp_exception");
1077 SDValue SymNode =
1078 DAG.getNode(WebAssemblyISD::Wrapper, DL, PtrVT,
1079 DAG.getTargetExternalSymbol(
1080 SymName, PtrVT, WebAssemblyII::MO_SYMBOL_EVENT));
1081 return DAG.getNode(WebAssemblyISD::THROW, DL,
1082 MVT::Other, // outchain type
1083 {
1084 Op.getOperand(0), // inchain
1085 SymNode, // exception symbol
1086 Op.getOperand(3) // thrown value
1087 });
10941088 }
10951089 }
10961090 }
9595 SDValue LowerJumpTable(SDValue Op, SelectionDAG &DAG) const;
9696 SDValue LowerVASTART(SDValue Op, SelectionDAG &DAG) const;
9797 SDValue LowerCopyToReg(SDValue Op, SelectionDAG &DAG) const;
98 SDValue LowerINTRINSIC_WO_CHAIN(SDValue Op, SelectionDAG &DAG) const;
99 SDValue LowerINTRINSIC_VOID(SDValue Op, SelectionDAG &DAG) const;
98 SDValue LowerIntrinsic(SDValue Op, SelectionDAG &DAG) const;
10099 SDValue LowerSIGN_EXTEND_INREG(SDValue Op, SelectionDAG &DAG) const;
101100 SDValue LowerBUILD_VECTOR(SDValue Op, SelectionDAG &DAG) const;
102101 SDValue LowerVECTOR_SHUFFLE(SDValue Op, SelectionDAG &DAG) const;
140140
141141 // Throwing an exception: throw / rethrow
142142 let isTerminator = 1, hasCtrlDep = 1, isBarrier = 1 in {
143 defm THROW_I32 : I<(outs), (ins event_op:$tag, I32:$val),
144 (outs), (ins event_op:$tag),
145 [(WebAssemblythrow (WebAssemblywrapper texternalsym:$tag),
146 I32:$val)],
147 "throw \t$tag, $val", "throw \t$tag",
148 0x08>;
149 defm THROW_I64 : I<(outs), (ins event_op:$tag, I64:$val),
150 (outs), (ins event_op:$tag),
151 [(WebAssemblythrow (WebAssemblywrapper texternalsym:$tag),
152 I64:$val)],
153 "throw \t$tag, $val", "throw \t$tag",
154 0x08>;
155 defm RETHROW : NRI<(outs), (ins bb_op:$dst), [], "rethrow \t$dst", 0x09>;
156 let isCodeGenOnly = 1 in
157 // This is used when the destination for rethrow is the caller function. This
158 // will be converted to a rethrow in CFGStackify.
159 defm RETHROW_TO_CALLER : NRI<(outs), (ins), [], "rethrow">;
143 defm THROW : I<(outs), (ins event_op:$tag, variable_ops),
144 (outs), (ins event_op:$tag),
145 [(WebAssemblythrow (WebAssemblywrapper texternalsym:$tag))],
146 "throw \t$tag", "throw \t$tag", 0x08>;
147 defm RETHROW : NRI<(outs), (ins), [(int_wasm_rethrow)], "rethrow", 0x09>;
160148 } // isTerminator = 1, hasCtrlDep = 1, isBarrier = 1
161149
162150 // Region within which an exception is caught: try / end_try
165153 defm END_TRY : NRI<(outs), (ins), [], "end_try", 0x0b>;
166154 } // Uses = [VALUE_STACK], Defs = [VALUE_STACK]
167155
168 // Catching an exception: catch / catch_all
169 let hasCtrlDep = 1, hasSideEffects = 1 in {
170 defm CATCH_I32 : I<(outs I32:$dst), (ins i32imm:$tag),
171 (outs), (ins i32imm:$tag),
172 [(set I32:$dst, (int_wasm_catch imm:$tag))],
173 "i32.catch \t$dst, $tag", "i32.catch \t$tag", 0x07>;
174 defm CATCH_I64 : I<(outs I64:$dst), (ins i32imm:$tag),
175 (outs), (ins i32imm:$tag),
176 [(set I64:$dst, (int_wasm_catch imm:$tag))],
177 "i64.catch \t$dst, $tag", "i64.catch \t$tag", 0x07>;
178 defm CATCH_ALL : NRI<(outs), (ins), [], "catch_all", 0x05>;
179 }
156 // Catching an exception: catch / extract_exception
157 let hasCtrlDep = 1, hasSideEffects = 1 in
158 defm CATCH : I<(outs EXCEPT_REF:$dst), (ins), (outs), (ins), [],
159 "catch \t$dst", "catch", 0x07>;
160
161 // Querying / extracing exception: br_on_exn
162 // br_on_exn queries an except_ref to see if it matches the corresponding
163 // exception tag index. If true it branches to the given label and pushes the
164 // corresponding argument values of the exception onto the stack.
165 let isBranch = 1, isTerminator = 1, hasCtrlDep = 1 in
166 defm BR_ON_EXN : I<(outs), (ins bb_op:$dst, event_op:$tag, EXCEPT_REF:$exn),
167 (outs), (ins bb_op:$dst, event_op:$tag), [],
168 "br_on_exn \t$dst, $tag, $exn", "br_on_exn \t$dst, $tag",
169 0x0a>;
170 // This is a pseudo instruction that simulates popping a value from stack, which
171 // has been pushed by br_on_exn
172 let isCodeGenOnly = 1, hasSideEffects = 1 in
173 defm EXTRACT_EXCEPTION_I32 : NRI<(outs I32:$dst), (ins),
174 [(set I32:$dst, (int_wasm_extract_exception))],
175 "extract_exception\t$dst">;
180176
181177 // Pseudo instructions: cleanupret / catchret
182178 let isTerminator = 1, hasSideEffects = 1, isBarrier = 1, hasCtrlDep = 1,
183 isCodeGenOnly = 1, isEHScopeReturn = 1 in {
184 defm CLEANUPRET : NRI<(outs), (ins), [(cleanupret)], "", 0>;
179 isPseudo = 1, isEHScopeReturn = 1 in {
180 defm CLEANUPRET : NRI<(outs), (ins), [(cleanupret)], "cleanupret", 0>;
185181 defm CATCHRET : NRI<(outs), (ins bb_op:$dst, bb_op:$from),
186 [(catchret bb:$dst, bb:$from)], "", 0>;
182 [(catchret bb:$dst, bb:$from)], "catchret", 0>;
183 } // isTerminator = 1, hasSideEffects = 1, isBarrier = 1, hasCtrlDep = 1,
184 // isPseudo = 1, isEHScopeReturn = 1
187185 }
188 }
133133 else
134134 FBB = MI.getOperand(0).getMBB();
135135 break;
136 case WebAssembly::BR_ON_EXN:
137 if (HaveCond)
138 return true;
139 // If we're running after CFGStackify, we can't optimize further.
140 if (!MI.getOperand(0).isMBB())
141 return true;
142 Cond.push_back(MachineOperand::CreateImm(true));
143 Cond.push_back(MI.getOperand(2));
144 TBB = MI.getOperand(0).getMBB();
145 HaveCond = true;
146 break;
136147 }
137148 if (MI.isBarrier())
138149 break;
178189
179190 assert(Cond.size() == 2 && "Expected a flag and a successor block");
180191
192 MachineFunction &MF = *MBB.getParent();
193 auto &MRI = MF.getRegInfo();
194 bool IsBrOnExn = Cond[1].isReg() && MRI.getRegClass(Cond[1].getReg()) ==
195 &WebAssembly::EXCEPT_REFRegClass;
196
181197 if (Cond[0].getImm()) {
182 BuildMI(&MBB, DL, get(WebAssembly::BR_IF)).addMBB(TBB).add(Cond[1]);
198 if (IsBrOnExn) {
199 const char *CPPExnSymbol = MF.createExternalSymbolName("__cpp_exception");
200 BuildMI(&MBB, DL, get(WebAssembly::BR_ON_EXN))
201 .addMBB(TBB)
202 .addExternalSymbol(CPPExnSymbol, WebAssemblyII::MO_SYMBOL_EVENT)
203 .add(Cond[1]);
204 } else
205 BuildMI(&MBB, DL, get(WebAssembly::BR_IF)).addMBB(TBB).add(Cond[1]);
183206 } else {
207 assert(!IsBrOnExn && "br_on_exn does not have a reversed condition");
184208 BuildMI(&MBB, DL, get(WebAssembly::BR_UNLESS)).addMBB(TBB).add(Cond[1]);
185209 }
186210 if (!FBB)
192216
193217 bool WebAssemblyInstrInfo::reverseBranchCondition(
194218 SmallVectorImpl &Cond) const {
195 assert(Cond.size() == 2 && "Expected a flag and a successor block");
219 assert(Cond.size() == 2 && "Expected a flag and a condition expression");
220
221 // br_on_exn's condition cannot be reversed
222 MachineFunction &MF = *Cond[1].getParent()->getParent()->getParent();
223 auto &MRI = MF.getRegInfo();
224 if (Cond[1].isReg() &&
225 MRI.getRegClass(Cond[1].getReg()) == &WebAssembly::EXCEPT_REFRegClass)
226 return true;
227
196228 Cond.front() = MachineOperand::CreateImm(!Cond.front().getImm());
197229 return false;
198230 }
6565 def SDT_WebAssemblyReturn : SDTypeProfile<0, -1, []>;
6666 def SDT_WebAssemblyWrapper : SDTypeProfile<1, 1, [SDTCisSameAs<0, 1>,
6767 SDTCisPtrTy<0>]>;
68 def SDT_WebAssemblyThrow : SDTypeProfile<0, 2, [SDTCisPtrTy<0>]>;
68 def SDT_WebAssemblyThrow : SDTypeProfile<0, -1, [SDTCisPtrTy<0>]>;
6969
7070 //===----------------------------------------------------------------------===//
7171 // WebAssembly-specific DAG Nodes.
9393 def WebAssemblywrapper : SDNode<"WebAssemblyISD::Wrapper",
9494 SDT_WebAssemblyWrapper>;
9595 def WebAssemblythrow : SDNode<"WebAssemblyISD::THROW", SDT_WebAssemblyThrow,
96 [SDNPHasChain]>;
96 [SDNPHasChain, SDNPVariadic]>;
9797
9898 //===----------------------------------------------------------------------===//
9999 // WebAssembly-specific Operands.
1414 #include "WebAssembly.h"
1515 #include "WebAssemblySubtarget.h"
1616 #include "WebAssemblyUtilities.h"
17 #include "llvm/ADT/SmallSet.h"
1718 #include "llvm/CodeGen/MachineInstrBuilder.h"
1819 #include "llvm/CodeGen/WasmEHFuncInfo.h"
1920 #include "llvm/MC/MCAsmInfo.h"
2425 namespace {
2526 class WebAssemblyLateEHPrepare final : public MachineFunctionPass {
2627 StringRef getPassName() const override {
27 return "WebAssembly Prepare Exception";
28 return "WebAssembly Late Prepare Exception";
2829 }
2930
3031 bool runOnMachineFunction(MachineFunction &MF) override;
31
3232 bool removeUnnecessaryUnreachables(MachineFunction &MF);
3333 bool replaceFuncletReturns(MachineFunction &MF);
34 bool hoistCatches(MachineFunction &MF);
35 bool addCatchAlls(MachineFunction &MF);
36 bool addRethrows(MachineFunction &MF);
37 bool ensureSingleBBTermPads(MachineFunction &MF);
38 bool mergeTerminatePads(MachineFunction &MF);
39 bool addCatchAllTerminatePads(MachineFunction &MF);
34 bool addCatches(MachineFunction &MF);
35 bool addExceptionExtraction(MachineFunction &MF);
4036
4137 public:
4238 static char ID; // Pass identification, replacement for typeid
111107
112108 bool Changed = false;
113109 Changed |= removeUnnecessaryUnreachables(MF);
114 Changed |= addRethrows(MF);
115110 if (!MF.getFunction().hasPersonalityFn())
116111 return Changed;
117112 Changed |= replaceFuncletReturns(MF);
118 Changed |= hoistCatches(MF);
119 Changed |= addCatchAlls(MF);
120 Changed |= ensureSingleBBTermPads(MF);
121 Changed |= mergeTerminatePads(MF);
122 Changed |= addCatchAllTerminatePads(MF);
113 Changed |= addCatches(MF);
114 Changed |= addExceptionExtraction(MF);
123115 return Changed;
124116 }
125117
128120 bool Changed = false;
129121 for (auto &MBB : MF) {
130122 for (auto &MI : MBB) {
131 if (!WebAssembly::isThrow(MI))
123 if (MI.getOpcode() != WebAssembly::THROW &&
124 MI.getOpcode() != WebAssembly::RETHROW)
132125 continue;
133126 Changed = true;
134127
151144 bool WebAssemblyLateEHPrepare::replaceFuncletReturns(MachineFunction &MF) {
152145 bool Changed = false;
153146 const auto &TII = *MF.getSubtarget().getInstrInfo();
154 auto *EHInfo = MF.getWasmEHFuncInfo();
155147
156148 for (auto &MBB : MF) {
157149 auto Pos = MBB.getFirstTerminator();
172164 }
173165 case WebAssembly::CLEANUPRET: {
174166 // Replace a cleanupret with a rethrow
175 if (EHInfo->hasThrowUnwindDest(&MBB))
176 BuildMI(MBB, TI, TI->getDebugLoc(), TII.get(WebAssembly::RETHROW))
177 .addMBB(EHInfo->getThrowUnwindDest(&MBB));
178 else
179 BuildMI(MBB, TI, TI->getDebugLoc(),
180 TII.get(WebAssembly::RETHROW_TO_CALLER));
181
167 BuildMI(MBB, TI, TI->getDebugLoc(), TII.get(WebAssembly::RETHROW));
182168 TI->eraseFromParent();
183169 Changed = true;
184170 break;
188174 return Changed;
189175 }
190176
191 // Hoist catch instructions to the beginning of their matching EH pad BBs in
192 // case,
193 // (1) catch instruction is not the first instruction in EH pad.
194 // ehpad:
195 // some_other_instruction
196 // ...
197 // %exn = catch 0
198 // (2) catch instruction is in a non-EH pad BB. For example,
199 // ehpad:
200 // br bb0
201 // bb0:
202 // %exn = catch 0
203 bool WebAssemblyLateEHPrepare::hoistCatches(MachineFunction &MF) {
204 bool Changed = false;
205 SmallVector Catches;
206 for (auto &MBB : MF)
207 for (auto &MI : MBB)
208 if (WebAssembly::isCatch(MI))
209 Catches.push_back(&MI);
210
211 for (auto *Catch : Catches) {
212 MachineBasicBlock *EHPad = getMatchingEHPad(Catch);
213 assert(EHPad && "No matching EH pad for catch");
214 if (EHPad->begin() == Catch)
215 continue;
216 Changed = true;
217 EHPad->insert(EHPad->begin(), Catch->removeFromParent());
218 }
219 return Changed;
220 }
221
222 // Add catch_all to beginning of cleanup pads.
223 bool WebAssemblyLateEHPrepare::addCatchAlls(MachineFunction &MF) {
177 // Add catch instruction to beginning of catchpads and cleanuppads.
178 bool WebAssemblyLateEHPrepare::addCatches(MachineFunction &MF) {
224179 bool Changed = false;
225180 const auto &TII = *MF.getSubtarget().getInstrInfo();
226
227 for (auto &MBB : MF) {
228 if (!MBB.isEHPad())
229 continue;
230 // This runs after hoistCatches(), so we assume that if there is a catch,
231 // that should be the first instruction in an EH pad.
232 if (!WebAssembly::isCatch(*MBB.begin())) {
181 MachineRegisterInfo &MRI = MF.getRegInfo();
182 for (auto &MBB : MF) {
183 if (MBB.isEHPad()) {
233184 Changed = true;
185 unsigned DstReg =
186 MRI.createVirtualRegister(&WebAssembly::EXCEPT_REFRegClass);
234187 BuildMI(MBB, MBB.begin(), MBB.begin()->getDebugLoc(),
235 TII.get(WebAssembly::CATCH_ALL));
188 TII.get(WebAssembly::CATCH), DstReg);
236189 }
237190 }
238191 return Changed;
239192 }
240193
241 // Add a 'rethrow' instruction after __cxa_rethrow() call
242 bool WebAssemblyLateEHPrepare::addRethrows(MachineFunction &MF) {
243 bool Changed = false;
194 // Wasm uses 'br_on_exn' instruction to check the tag of an exception. It takes
195 // except_ref type object returned by 'catch', and branches to the destination
196 // if it matches a given tag. We currently use __cpp_exception symbol to
197 // represent the tag for all C++ exceptions.
198 //
199 // block $l (result i32)
200 // ...
201 // ;; except_ref $e is on the stack at this point
202 // br_on_exn $l $e ;; branch to $l with $e's arguments
203 // ...
204 // end
205 // ;; Here we expect the extracted values are on top of the wasm value stack
206 // ... Handle exception using values ...
207 //
208 // br_on_exn takes an except_ref object and branches if it matches the given
209 // tag. There can be multiple br_on_exn instructions if we want to match for
210 // another tag, but for now we only test for __cpp_exception tag, and if it does
211 // not match, i.e., it is a foreign exception, we rethrow it.
212 //
213 // In the destination BB that's the target of br_on_exn, extracted exception
214 // values (in C++'s case a single i32, which represents an exception pointer)
215 // are placed on top of the wasm stack. Because we can't model wasm stack in
216 // LLVM instruction, we use 'extract_exception' pseudo instruction to retrieve
217 // it. The pseudo instruction will be deleted later.
218 bool WebAssemblyLateEHPrepare::addExceptionExtraction(MachineFunction &MF) {
244219 const auto &TII = *MF.getSubtarget().getInstrInfo();
245220 auto *EHInfo = MF.getWasmEHFuncInfo();
246
247 for (auto &MBB : MF)
221 SmallVector ExtractInstrs;
222 for (auto &MBB : MF) {
248223 for (auto &MI : MBB) {
249 // Check if it is a call to __cxa_rethrow()
250 if (!MI.isCall())
251 continue;
252 MachineOperand &CalleeOp = MI.getOperand(0);
253 if (!CalleeOp.isGlobal() ||
254 CalleeOp.getGlobal()->getName() != WebAssembly::CxaRethrowFn)
255 continue;
256
257 // Now we have __cxa_rethrow() call
258 Changed = true;
259 auto InsertPt = std::next(MachineBasicBlock::iterator(MI));
260 while (InsertPt != MBB.end() && InsertPt->isLabel()) // Skip EH_LABELs
261 ++InsertPt;
262 MachineInstr *Rethrow = nullptr;
263 if (EHInfo->hasThrowUnwindDest(&MBB))
264 Rethrow = BuildMI(MBB, InsertPt, MI.getDebugLoc(),
265 TII.get(WebAssembly::RETHROW))
266 .addMBB(EHInfo->getThrowUnwindDest(&MBB));
267 else
268 Rethrow = BuildMI(MBB, InsertPt, MI.getDebugLoc(),
269 TII.get(WebAssembly::RETHROW_TO_CALLER));
270
271 // Because __cxa_rethrow does not return, the instruction after the
272 // rethrow should be an unreachable or a branch to another BB that should
273 // eventually lead to an unreachable. Delete it because rethrow itself is
274 // a terminator, and also delete non-EH pad successors if any.
275 MBB.erase(std::next(MachineBasicBlock::iterator(Rethrow)), MBB.end());
276 SmallVector NonPadSuccessors;
277 for (auto *Succ : MBB.successors())
278 if (!Succ->isEHPad())
279 NonPadSuccessors.push_back(Succ);
280 for (auto *Succ : NonPadSuccessors)
281 MBB.removeSuccessor(Succ);
282 eraseDeadBBsAndChildren(NonPadSuccessors);
283 }
284 return Changed;
285 }
286
287 // Terminate pads are an single-BB EH pad in the form of
288 // termpad:
289 // %exn = catch 0
290 // call @__clang_call_terminate(%exn)
291 // unreachable
292 // (There can be local.set and local.gets before the call if we didn't run
293 // RegStackify)
294 // But code transformations can change or add more control flow, so the call to
295 // __clang_call_terminate() function may not be in the original EH pad anymore.
296 // This ensures every terminate pad is a single BB in the form illustrated
297 // above.
298 bool WebAssemblyLateEHPrepare::ensureSingleBBTermPads(MachineFunction &MF) {
299 const auto &TII = *MF.getSubtarget().getInstrInfo();
300
301 // Find calls to __clang_call_terminate()
302 SmallVector ClangCallTerminateCalls;
303 for (auto &MBB : MF)
304 for (auto &MI : MBB)
224 if (MI.getOpcode() == WebAssembly::EXTRACT_EXCEPTION_I32) {
225 if (MI.getOperand(0).isDead())
226 MI.eraseFromParent();
227 else
228 ExtractInstrs.push_back(&MI);
229 }
230 }
231 }
232 if (ExtractInstrs.empty())
233 return false;
234
235 // Find terminate pads.
236 SmallSet TerminatePads;
237 for (auto &MBB : MF) {
238 for (auto &MI : MBB) {
305239 if (MI.isCall()) {
306240 const MachineOperand &CalleeOp = MI.getOperand(0);
307241 if (CalleeOp.isGlobal() && CalleeOp.getGlobal()->getName() ==
308242 WebAssembly::ClangCallTerminateFn)
309 ClangCallTerminateCalls.push_back(&MI);
243 TerminatePads.insert(getMatchingEHPad(&MI));
310244 }
311
312 bool Changed = false;
313 for (auto *Call : ClangCallTerminateCalls) {
314 MachineBasicBlock *EHPad = getMatchingEHPad(Call);
315 assert(EHPad && "No matching EH pad for catch");
316
317 // If it is already the form we want, skip it
318 if (Call->getParent() == EHPad &&
319 Call->getNextNode()->getOpcode() == WebAssembly::UNREACHABLE)
320 continue;
321
322 // In case the __clang_call_terminate() call is not in its matching EH pad,
323 // move the call to the end of EH pad and add an unreachable instruction
324 // after that. Delete all successors and their children if any, because here
325 // the program terminates.
326 Changed = true;
245 }
246 }
247
248 for (auto *Extract : ExtractInstrs) {
249 MachineBasicBlock *EHPad = getMatchingEHPad(Extract);
250 assert(EHPad && "No matching EH pad for extract_exception");
327251 MachineInstr *Catch = &*EHPad->begin();
328 // This runs after hoistCatches(), so catch instruction should be at the top
329 assert(WebAssembly::isCatch(*Catch));
330 // Takes the result register of the catch instruction as argument. There may
331 // have been some other local.set/local.gets in between, but at this point
332 // we don't care.
333 Call->getOperand(1).setReg(Catch->getOperand(0).getReg());
334 auto InsertPos = std::next(MachineBasicBlock::iterator(Catch));
335 EHPad->insert(InsertPos, Call->removeFromParent());
336 BuildMI(*EHPad, InsertPos, Call->getDebugLoc(),
337 TII.get(WebAssembly::UNREACHABLE));
338 EHPad->erase(InsertPos, EHPad->end());
339 SmallVector Succs(EHPad->succ_begin(),
340 EHPad->succ_end());
341 for (auto *Succ : Succs)
342 EHPad->removeSuccessor(Succ);
343 eraseDeadBBsAndChildren(Succs);
344 }
345 return Changed;
346 }
347
348 // In case there are multiple terminate pads, merge them into one for code size.
349 // This runs after ensureSingleBBTermPads() and assumes every terminate pad is a
350 // single BB.
351 // In principle this violates EH scope relationship because it can merge
352 // multiple inner EH scopes, each of which is in different outer EH scope. But
353 // getEHScopeMembership() function will not be called after this, so it is fine.
354 bool WebAssemblyLateEHPrepare::mergeTerminatePads(MachineFunction &MF) {
355 SmallVector TermPads;
356 for (auto &MBB : MF)
357 if (WebAssembly::isCatchTerminatePad(MBB))
358 TermPads.push_back(&MBB);
359 if (TermPads.empty())
360 return false;
361
362 MachineBasicBlock *UniqueTermPad = TermPads.front();
363 for (auto *TermPad :
364 llvm::make_range(std::next(TermPads.begin()), TermPads.end())) {
365 SmallVector Preds(TermPad->pred_begin(),
366 TermPad->pred_end());
367 for (auto *Pred : Preds)
368 Pred->replaceSuccessor(TermPad, UniqueTermPad);
369 TermPad->eraseFromParent();
370 }
252 if (Catch->getNextNode() != Extract)
253 EHPad->insert(Catch->getNextNode(), Extract->removeFromParent());
254
255 // - Before:
256 // ehpad:
257 // %exnref:except_ref = catch
258 // %exn:i32 = extract_exception
259 // ... use exn ...
260 //
261 // - After:
262 // ehpad:
263 // %exnref:except_ref = catch
264 // br_on_exn %thenbb, $__cpp_exception, %exnref
265 // br %elsebb
266 // elsebb:
267 // rethrow
268 // thenbb:
269 // %exn:i32 = extract_exception
270 // ... use exn ...
271 unsigned ExnRefReg = Catch->getOperand(0).getReg();
272 auto *ThenMBB = MF.CreateMachineBasicBlock();
273 auto *ElseMBB = MF.CreateMachineBasicBlock();
274 MF.insert(std::next(MachineFunction::iterator(EHPad)), ElseMBB);
275 MF.insert(std::next(MachineFunction::iterator(ElseMBB)), ThenMBB);
276 ThenMBB->splice(ThenMBB->end(), EHPad, Extract, EHPad->end());
277 ThenMBB->transferSuccessors(EHPad);
278 EHPad->addSuccessor(ThenMBB);
279 EHPad->addSuccessor(ElseMBB);
280
281 DebugLoc DL = Extract->getDebugLoc();
282 const char *CPPExnSymbol = MF.createExternalSymbolName("__cpp_exception");
283 BuildMI(EHPad, DL, TII.get(WebAssembly::BR_ON_EXN))
284 .addMBB(ThenMBB)
285 .addExternalSymbol(CPPExnSymbol, WebAssemblyII::MO_SYMBOL_EVENT)
286 .addReg(ExnRefReg);
287 BuildMI(EHPad, DL, TII.get(WebAssembly::BR)).addMBB(ElseMBB);
288
289 // When this is a terminate pad with __clang_call_terminate() call, we don't
290 // rethrow it anymore and call __clang_call_terminate() with a nullptr
291 // argument, which will call std::terminate().
292 //
293 // - Before:
294 // ehpad:
295 // %exnref:except_ref = catch
296 // %exn:i32 = extract_exception
297 // call @__clang_call_terminate(%exn)
298 // unreachable
299 //
300 // - After:
301 // ehpad:
302 // %exnref:except_ref = catch
303 // br_on_exn %thenbb, $__cpp_exception, %exnref
304 // br %elsebb
305 // elsebb:
306 // call @__clang_call_terminate(0)
307 // unreachable
308 // thenbb:
309 // %exn:i32 = extract_exception
310 // call @__clang_call_terminate(%exn)
311 // unreachable
312 if (TerminatePads.count(EHPad)) {
313 Function *ClangCallTerminateFn =
314 MF.getFunction().getParent()->getFunction(
315 WebAssembly::ClangCallTerminateFn);
316 assert(ClangCallTerminateFn &&
317 "There is no __clang_call_terminate() function");
318 BuildMI(ElseMBB, DL, TII.get(WebAssembly::CALL_VOID))
319 .addGlobalAddress(ClangCallTerminateFn)
320 .addImm(0);
321 BuildMI(ElseMBB, DL, TII.get(WebAssembly::UNREACHABLE));
322
323 } else {
324 BuildMI(ElseMBB, DL, TII.get(WebAssembly::RETHROW));
325 if (EHInfo->hasEHPadUnwindDest(EHPad))
326 EHInfo->setThrowUnwindDest(ElseMBB, EHInfo->getEHPadUnwindDest(EHPad));
327 }
328 }
329
371330 return true;
372331 }
373
374 // Terminate pads are cleanup pads, so they should start with a 'catch_all'
375 // instruction. But in the Itanium model, when we have a C++ exception object,
376 // we pass them to __clang_call_terminate function, which calls __cxa_end_catch
377 // with the passed exception pointer and then std::terminate. This is the reason
378 // that terminate pads are generated with not a catch_all but a catch
379 // instruction in clang and earlier llvm passes. Here we append a terminate pad
380 // with a catch_all after each existing terminate pad so we can also catch
381 // foreign exceptions. For every terminate pad:
382 // %exn = catch 0
383 // call @__clang_call_terminate(%exn)
384 // unreachable
385 // We append this BB right after that:
386 // catch_all
387 // call @std::terminate()
388 // unreachable
389 bool WebAssemblyLateEHPrepare::addCatchAllTerminatePads(MachineFunction &MF) {
390 const auto &TII = *MF.getSubtarget().getInstrInfo();
391 SmallVector TermPads;
392 for (auto &MBB : MF)
393 if (WebAssembly::isCatchTerminatePad(MBB))
394 TermPads.push_back(&MBB);
395 if (TermPads.empty())
396 return false;
397
398 Function *StdTerminateFn =
399 MF.getFunction().getParent()->getFunction(WebAssembly::StdTerminateFn);
400 assert(StdTerminateFn && "There is no std::terminate() function");
401 for (auto *CatchTermPad : TermPads) {
402 DebugLoc DL = CatchTermPad->findDebugLoc(CatchTermPad->begin());
403 auto *CatchAllTermPad = MF.CreateMachineBasicBlock();
404 MF.insert(std::next(MachineFunction::iterator(CatchTermPad)),
405 CatchAllTermPad);
406 CatchAllTermPad->setIsEHPad();
407 BuildMI(CatchAllTermPad, DL, TII.get(WebAssembly::CATCH_ALL));
408 BuildMI(CatchAllTermPad, DL, TII.get(WebAssembly::CALL_VOID))
409 .addGlobalAddress(StdTerminateFn);
410 BuildMI(CatchAllTermPad, DL, TII.get(WebAssembly::UNREACHABLE));
411
412 // Actually this CatchAllTermPad (new terminate pad with a catch_all) is not
413 // a successor of an existing terminate pad. CatchAllTermPad should have all
414 // predecessors CatchTermPad has instead. This is a hack to force
415 // CatchAllTermPad be always sorted right after CatchTermPad; the correct
416 // predecessor-successor relationships will be restored in CFGStackify pass.
417 CatchTermPad->addSuccessor(CatchAllTermPad);
418 }
419 return true;
420 }
3535
3636 // This disables the removal of registers when lowering into MC, as required
3737 // by some current tests.
38 static cl::opt
38 cl::opt
3939 WasmKeepRegisters("wasm-keep-registers", cl::Hidden,
4040 cl::desc("WebAssembly: output stack registers in"
4141 " instruction output for test purposes only."),
316316 AliasAnalysis &AA, const MachineRegisterInfo &MRI) {
317317 assert(Def->getParent() == Insert->getParent());
318318
319 // 'catch' and 'extract_exception' should be the first instruction of a BB and
320 // cannot move.
321 if (Def->getOpcode() == WebAssembly::CATCH ||
322 Def->getOpcode() == WebAssembly::EXTRACT_EXCEPTION_I32) {
323 const MachineBasicBlock *MBB = Def->getParent();
324 auto NextI = std::next(MachineBasicBlock::const_iterator(Def));
325 for (auto E = MBB->end(); NextI != E && NextI->isDebugInstr(); ++NextI)
326 ;
327 if (NextI != Insert)
328 return false;
329 }
330
319331 // Check for register dependencies.
320332 SmallVector MutableRegisters;
321333 for (const MachineOperand &MO : Def->operands()) {
818830 if (WebAssembly::isArgument(*Def))
819831 continue;
820832
833 // Currently catch's return value register cannot be stackified, because
834 // the wasm LLVM backend currently does not support live-in values
835 // entering blocks, which is a part of multi-value proposal.
836 //
837 // Once we support live-in values of wasm blocks, this can be:
838 // catch ; push except_ref value onto stack
839 // block except_ref -> i32
840 // br_on_exn $__cpp_exception ; pop the except_ref value
841 // end_block
842 //
843 // But because we don't support it yet, the catch instruction's dst
844 // register should be assigned to a local to be propagated across
845 // 'block' boundary now.
846 //
847 // TODO Fix this once we support the multi-value proposal.
848 if (Def->getOpcode() == WebAssembly::CATCH)
849 continue;
850
821851 // Decide which strategy to take. Prefer to move a single-use value
822852 // over cloning it, and prefer cloning over introducing a tee.
823853 // For moving, we require the def to be in the same block as the use;
300300 addPass(createWebAssemblyFixIrreducibleControlFlow());
301301
302302 // Do various transformations for exception handling.
303 // Every CFG-changing optimizations should come before this.
303304 addPass(createWebAssemblyLateEHPrepare());
304305
306 // Preparations and optimizations related to register stackification.
305307 if (getOptLevel() != CodeGenOpt::None) {
306308 // LiveIntervals isn't commonly run this late. Re-establish preconditions.
307309 addPass(createWebAssemblyPrepareForLiveIntervals());
241241 }
242242 }
243243
244 bool WebAssembly::isThrow(const MachineInstr &MI) {
245 switch (MI.getOpcode()) {
246 case WebAssembly::THROW_I32:
247 case WebAssembly::THROW_I32_S:
248 case WebAssembly::THROW_I64:
249 case WebAssembly::THROW_I64_S:
250 return true;
251 default:
252 return false;
253 }
254 }
255
256 bool WebAssembly::isRethrow(const MachineInstr &MI) {
257 switch (MI.getOpcode()) {
258 case WebAssembly::RETHROW:
259 case WebAssembly::RETHROW_S:
260 case WebAssembly::RETHROW_TO_CALLER:
261 case WebAssembly::RETHROW_TO_CALLER_S:
262 return true;
263 default:
264 return false;
265 }
266 }
267
268 bool WebAssembly::isCatch(const MachineInstr &MI) {
269 switch (MI.getOpcode()) {
270 case WebAssembly::CATCH_I32:
271 case WebAssembly::CATCH_I32_S:
272 case WebAssembly::CATCH_I64:
273 case WebAssembly::CATCH_I64_S:
274 case WebAssembly::CATCH_ALL:
275 case WebAssembly::CATCH_ALL_S:
276 return true;
277 default:
278 return false;
279 }
280 }
281
282244 bool WebAssembly::mayThrow(const MachineInstr &MI) {
283245 switch (MI.getOpcode()) {
284 case WebAssembly::THROW_I32:
285 case WebAssembly::THROW_I32_S:
286 case WebAssembly::THROW_I64:
287 case WebAssembly::THROW_I64_S:
246 case WebAssembly::THROW:
247 case WebAssembly::THROW_S:
288248 case WebAssembly::RETHROW:
289249 case WebAssembly::RETHROW_S:
290250 return true;
307267 return false;
308268 return true;
309269 }
310
311 bool WebAssembly::isCatchTerminatePad(const MachineBasicBlock &MBB) {
312 if (!MBB.isEHPad())
313 return false;
314 bool SeenCatch = false;
315 for (auto &MI : MBB) {
316 if (MI.getOpcode() == WebAssembly::CATCH_I32 ||
317 MI.getOpcode() == WebAssembly::CATCH_I64 ||
318 MI.getOpcode() == WebAssembly::CATCH_I32_S ||
319 MI.getOpcode() == WebAssembly::CATCH_I64_S)
320 SeenCatch = true;
321 if (SeenCatch && MI.isCall()) {
322 const MachineOperand &CalleeOp = MI.getOperand(getCalleeOpNo(MI));
323 if (CalleeOp.isGlobal() &&
324 CalleeOp.getGlobal()->getName() == ClangCallTerminateFn)
325 return true;
326 }
327 }
328 return false;
329 }
330
331 bool WebAssembly::isCatchAllTerminatePad(const MachineBasicBlock &MBB) {
332 if (!MBB.isEHPad())
333 return false;
334 bool SeenCatchAll = false;
335 for (auto &MI : MBB) {
336 if (MI.getOpcode() == WebAssembly::CATCH_ALL ||
337 MI.getOpcode() == WebAssembly::CATCH_ALL_S)
338 SeenCatchAll = true;
339 if (SeenCatchAll && MI.isCall()) {
340 const MachineOperand &CalleeOp = MI.getOperand(getCalleeOpNo(MI));
341 if (CalleeOp.isGlobal() &&
342 CalleeOp.getGlobal()->getName() == StdTerminateFn)
343 return true;
344 }
345 }
346 return false;
347 }
2929 bool isCallDirect(const MachineInstr &MI);
3030 bool isCallIndirect(const MachineInstr &MI);
3131 bool isMarker(const MachineInstr &MI);
32 bool isThrow(const MachineInstr &MI);
33 bool isRethrow(const MachineInstr &MI);
34 bool isCatch(const MachineInstr &MI);
3532 bool mayThrow(const MachineInstr &MI);
3633
3734 /// Returns the operand number of a callee, assuming the argument is a call
3835 /// instruction.
3936 unsigned getCalleeOpNo(const MachineInstr &MI);
40
41 /// Returns if the given BB is a single BB terminate pad which starts with a
42 /// 'catch' instruction.
43 bool isCatchTerminatePad(const MachineBasicBlock &MBB);
44 /// Returns if the given BB is a single BB terminate pad which starts with a
45 /// 'catch_all' insrtruction.
46 bool isCatchAllTerminatePad(const MachineBasicBlock &MBB);
4737
4838 // Exception-related function names
4939 extern const char *const ClangCallTerminateFn;
+0
-94
test/CodeGen/WebAssembly/annotations.mir less more
None # RUN: llc -mtriple=wasm32-unknown-unknown -start-after xray-instrumentation -wasm-keep-registers %s -o - | FileCheck %s
1
2 ---
3 # Tests if block/loop/try/catch/end instructions are correctly printed with
4 # their annotations.
5
6 # CHECK: test0:
7 # CHECK: block
8 # CHECK: try
9 # CHECK: br 0 # 0: down to label1
10 # CHECK: catch_all # catch0:
11 # CHECK: block
12 # CHECK: br_if 0, 1 # 0: down to label2
13 # CHECK: loop # label3:
14 # CHECK: br_if 0, 1 # 0: up to label3
15 # CHECK: end_loop
16 # CHECK: end_block # label2:
17 # CHECK: try
18 # CHECK: rethrow 0 # 0: down to catch1
19 # CHECK: catch_all # catch1:
20 # CHECK: block
21 # CHECK: try
22 # CHECK: br 0 # 0: down to label6
23 # CHECK: catch_all # catch2:
24 # CHECK: unreachable
25 # CHECK: end_try # label6:
26 # CHECK: end_block # label5:
27 # CHECK: rethrow 0 # 0: to caller
28 # CHECK: end_try # label4:
29 # CHECK: end_try # label1:
30 # CHECK: end_block # label0:
31
32 name: test0
33 liveins:
34 - { reg: '$arguments', reg: '$value_stack' }
35 body: |
36 bb.0:
37 successors: %bb.7, %bb.1
38 BLOCK 64, implicit-def $value_stack, implicit $value_stack
39 TRY 64, implicit-def $value_stack, implicit $value_stack
40 BR 0, implicit-def $arguments
41
42 bb.1 (landing-pad):
43 ; predecessors: %bb.0
44 successors: %bb.2, %bb.3
45
46 CATCH_ALL implicit-def $arguments
47 BLOCK 64, implicit-def $value_stack, implicit $value_stack
48 BR_IF 0, 1, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
49
50 bb.2:
51 ; predecessors: %bb.1, %bb.2
52 successors: %bb.2, %bb.3
53
54 LOOP 64, implicit-def $value_stack, implicit $value_stack
55 BR_IF 0, 1, implicit-def $arguments
56
57 bb.3:
58 ; predecessors: %bb.1, %bb.2
59 successors: %bb.4
60
61 END_LOOP implicit-def $value_stack, implicit $value_stack
62 END_BLOCK implicit-def $value_stack, implicit $value_stack
63 TRY 64, implicit-def $value_stack, implicit $value_stack
64 RETHROW 0, implicit-def $arguments
65
66 bb.4 (landing-pad):
67 ; predecessors: %bb.3
68 successors: %bb.6, %bb.5
69
70 CATCH_ALL implicit-def $arguments
71 BLOCK 64, implicit-def $value_stack, implicit $value_stack
72 TRY 64, implicit-def $value_stack, implicit $value_stack
73 BR 0, implicit-def $arguments
74
75 bb.5 (landing-pad):
76 ; predecessors: %bb.4
77 CATCH_ALL implicit-def $arguments
78 UNREACHABLE implicit-def dead $arguments
79
80 bb.6:
81 ; predecessors: %bb.4
82 END_TRY implicit-def $value_stack, implicit $value_stack
83 END_BLOCK implicit-def $value_stack, implicit $value_stack
84 RETHROW 0, implicit-def $arguments
85
86 bb.7:
87 ; predecessors: %bb.0
88 END_TRY implicit-def $value_stack, implicit $value_stack
89 END_TRY implicit-def $value_stack, implicit $value_stack
90 END_BLOCK implicit-def $value_stack, implicit $value_stack
91 FALLTHROUGH_RETURN_VOID implicit-def dead $arguments
92 END_FUNCTION implicit-def $value_stack, implicit $value_stack
93 ...
66 @_ZTId = external constant i8*
77
88 ; Simple test case with two catch clauses
9 ; void test0() {
10 ; try {
11 ; foo();
12 ; } catch (int n) {
13 ; bar();
14 ; } catch (double d) {
15 ; }
16 ; }
917
1018 ; CHECK-LABEL: test0
11 ; CHECK: call foo@FUNCTION
12 ; CHECK: .LBB0_1:
13 ; CHECK: i32.catch
19 ; CHECK: try
20 ; CHECK: call foo@FUNCTION
21 ; CHECK: catch $[[EXCEPT_REF:[0-9]+]]=
22 ; CHECK: block i32
23 ; CHECK: br_on_exn 0, __cpp_exception@EVENT, $[[EXCEPT_REF]]
24 ; CHECK: rethrow
25 ; CHECK: end_block
1426 ; CHECK: i32.call $drop=, _Unwind_CallPersonality@FUNCTION
15 ; CHECK: i32.call $drop=, __cxa_begin_catch@FUNCTION
16 ; CHECK: call bar@FUNCTION
17 ; CHECK: call __cxa_end_catch@FUNCTION
18 ; CHECK: .LBB0_3:
19 ; CHECK: i32.call $drop=, __cxa_begin_catch@FUNCTION
20 ; CHECK: call __cxa_end_catch@FUNCTION
21 ; CHECK: .LBB0_5:
22 ; CHECK: call __cxa_rethrow@FUNCTION
23 ; CHECK: .LBB0_6:
27 ; CHECK: end_try
2428 ; CHECK: return
2529 define void @test0() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
2630 entry:
6771 }
6872
6973 ; Nested try-catches within a catch
74 ; void test1() {
75 ; try {
76 ; foo();
77 ; } catch (int n) {
78 ; try {
79 ; foo();
80 ; } catch (int n) {
81 ; foo();
82 ; }
83 ; }
84 ; }
7085
7186 ; CHECK-LABEL: test1
72 ; CHECK: call foo@FUNCTION
73 ; CHECK: .LBB1_1:
74 ; CHECK: i32.catch $0=, 0
75 ; CHECK: i32.call $drop=, _Unwind_CallPersonality@FUNCTION, $0
76 ; CHECK: i32.call $drop=, __cxa_begin_catch@FUNCTION, $0
77 ; CHECK: call foo@FUNCTION
78 ; CHECK: .LBB1_3:
79 ; CHECK: i32.catch $0=, 0
80 ; CHECK: i32.call $drop=, _Unwind_CallPersonality@FUNCTION, $0
81 ; CHECK: i32.call $drop=, __cxa_begin_catch@FUNCTION, $0
82 ; CHECK: call foo@FUNCTION
83 ; CHECK: .LBB1_5:
84 ; CHECK: catch_all
85 ; CHECK: call __cxa_end_catch@FUNCTION
86 ; CHECK: rethrow
87 ; CHECK: .LBB1_6:
88 ; CHECK: call __cxa_rethrow@FUNCTION
89 ; CHECK: rethrow
90 ; CHECK: .LBB1_7:
91 ; CHECK: call __cxa_end_catch@FUNCTION
92 ; CHECK: .LBB1_8:
93 ; CHECK: catch_all
94 ; CHECK: call __cxa_end_catch@FUNCTION
95 ; CHECK: .LBB1_9:
96 ; CHECK: call __cxa_rethrow@FUNCTION
97 ; CHECK: rethrow
98 ; CHECK: .LBB1_10:
99 ; CHECK: call __cxa_end_catch@FUNCTION
100 ; CHECK: .LBB1_11:
87 ; CHECK: try
88 ; CHECK: call foo@FUNCTION
89 ; CHECK: catch
90 ; CHECK: br_on_exn 0, __cpp_exception@EVENT
91 ; CHECK: rethrow
92 ; CHECK: i32.call $drop=, _Unwind_CallPersonality@FUNCTION
93 ; CHECK: try
94 ; CHECK: call foo@FUNCTION
95 ; CHECK: catch
96 ; CHECK: br_on_exn 0, __cpp_exception@EVENT
97 ; CHECK: rethrow
98 ; CHECK: i32.call $drop=, _Unwind_CallPersonality@FUNCTION
99 ; CHECK: try
100 ; CHECK: i32.call $drop=, __cxa_begin_catch@FUNCTION
101 ; CHECK: try
102 ; CHECK: call foo@FUNCTION
103 ; CHECK: catch $drop=
104 ; CHECK: rethrow
105 ; CHECK: end_try
106 ; CHECK: catch $drop=
107 ; CHECK: rethrow
108 ; CHECK: end_try
109 ; CHECK: end_try
110 ; CHECK: end_try
101111 ; CHECK: return
102 define hidden void @test1() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
112 define void @test1() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
103113 entry:
104114 invoke void @foo()
105115 to label %try.cont11 unwind label %catch.dispatch
174184 }
175185
176186 ; Nested loop within a catch clause
187 ; void test2() {
188 ; try {
189 ; foo();
190 ; } catch (...) {
191 ; for (int i = 0; i < 50; i++)
192 ; foo();
193 ; }
194 ; }
177195
178196 ; CHECK-LABEL: test2
179 ; CHECK: call foo@FUNCTION
180 ; CHECK: .LBB2_1:
181 ; CHECK: i32.catch
182 ; CHECK: i32.call $drop=, __cxa_begin_catch@FUNCTION
183 ; CHECK: .LBB2_2:
184 ; CHECK: call foo@FUNCTION
185 ; CHECK: .LBB2_4:
186 ; CHECK: catch_all
197 ; CHECK: try
198 ; CHECK: call foo@FUNCTION
199 ; CHECK: catch
200 ; CHECK: br_on_exn 0, __cpp_exception@EVENT
201 ; CHECK: rethrow
202 ; CHECK: loop
203 ; CHECK: try
204 ; CHECK: call foo@FUNCTION
205 ; CHECK: catch $drop=
206 ; CHECK: try
187207 ; CHECK: call __cxa_end_catch@FUNCTION
188 ; CHECK: .LBB2_5:
189 ; CHECK: i32.catch
208 ; CHECK: catch
209 ; CHECK: br_on_exn 0, __cpp_exception@EVENT
210 ; CHECK: call __clang_call_terminate@FUNCTION, 0
211 ; CHECK: unreachable
190212 ; CHECK: call __clang_call_terminate@FUNCTION
191213 ; CHECK: unreachable
192 ; CHECK: .LBB2_6:
193 ; CHECK: catch_all
194 ; CHECK: call _ZSt9terminatev@FUNCTION
195 ; CHECK: unreachable
196 ; CHECK: .LBB2_7:
197 ; CHECK: rethrow
198 ; CHECK: .LBB2_8:
199 ; CHECK: call __cxa_end_catch@FUNCTION
200 ; CHECK: .LBB2_10:
214 ; CHECK: end_try
215 ; CHECK: rethrow
216 ; CHECK: end_try
217 ; CHECK: end_loop
218 ; CHECK: end_try
201219 ; CHECK: return
202220 define void @test2() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
203221 entry:
+0
-322
test/CodeGen/WebAssembly/cfg-stackify-eh.mir less more
None # RUN: llc -mtriple=wasm32-unknown-unknown -exception-model=wasm -run-pass wasm-cfg-stackify %s -o - | FileCheck %s
1
2 --- |
3 target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
4 target triple = "wasm32-unknown-unknown"
5
6 @__wasm_lpad_context = external global { i32, i8*, i32 }
7
8 declare void @may_throw()
9 ; Function Attrs: nounwind
10 declare void @dont_throw() #0
11 declare i8* @__cxa_begin_catch(i8*)
12 declare void @__cxa_end_catch()
13 declare void @__cxa_rethrow()
14 ; Function Attrs: nounwind
15 declare i32 @__gxx_wasm_personality_v0(...)
16 declare i32 @_Unwind_CallPersonality(i8*) #0
17
18 define void @test0() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
19 unreachable
20 }
21 define void @test1() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
22 unreachable
23 }
24 define void @test2() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
25 unreachable
26 }
27 define void @test3() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
28 unreachable
29 }
30
31 attributes #0 = { nounwind }
32
33 ---
34 # Simplest try-catch
35 # try {
36 # may_throw();
37 # } catch (...) {
38 # }
39 name: test0
40 # CHECK-LABEL: name: test0
41 liveins:
42 - { reg: '$arguments', reg: '$value_stack' }
43 body: |
44 bb.0:
45 successors: %bb.2, %bb.1
46
47 CALL_VOID @may_throw, implicit-def dead $arguments, implicit $sp32, implicit $sp64
48 BR %bb.2, implicit-def $arguments
49 ; CHECK-LABEL: bb.0:
50 ; CHECK: TRY
51 ; CHECK-NEXT: CALL_VOID @may_throw
52
53 bb.1 (landing-pad):
54 ; predecessors: %bb.0
55 successors: %bb.2
56
57 %2:i32 = CATCH_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
58 %3:i32 = CALL_I32 @__cxa_begin_catch, %2:i32, implicit-def dead $arguments, implicit $sp32, implicit $sp64, implicit-def $value_stack, implicit $value_stack
59 DROP_I32 killed %3:i32, implicit-def $arguments
60 CALL_VOID @__cxa_end_catch, implicit-def dead $arguments, implicit $sp32, implicit $sp64
61
62 bb.2:
63 ; predecessors: %bb.0, %bb.1
64
65 RETURN_VOID implicit-def dead $arguments
66 ; CHECK-LABEL: bb.2:
67 ; CHECK-NEXT: END_TRY
68 ; CHECK: RETURN_VOID
69 ...
70 ---
71
72 # Nested try-catch inside another catch
73 # try {
74 # may_throw();
75 # } catch (int n) {
76 # try {
77 # may_throw();
78 # } catch (int n) {
79 # }
80 # }
81 name: test1
82 # CHECK-LABEL: name: test1
83 liveins:
84 - { reg: '$arguments', reg: '$value_stack' }
85 body: |
86 bb.0:
87 successors: %bb.9, %bb.1
88
89 CALL_VOID @may_throw, implicit-def dead $arguments, implicit $sp32, implicit $sp64
90 BR %bb.9, implicit-def $arguments
91 ; CHECK-LABEL: bb.0:
92 ; CHECK: TRY
93 ; CHECK-NEXT: CALL_VOID @may_throw
94
95 bb.1 (landing-pad):
96 ; predecessors: %bb.0
97 successors: %bb.2, %bb.7
98
99 %30:i32 = CATCH_I32 0, implicit-def dead $arguments
100 LOCAL_SET_I32 0, %30:i32, implicit-def $arguments
101 %16:i32 = CONST_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
102 %27:i32 = CONST_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
103 STORE_I32 2, @__wasm_lpad_context + 4, %16:i32, %27:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store 4 into `i8** getelementptr inbounds ({ i32, i8*, i32 }, { i32, i8*, i32 }* @__wasm_lpad_context, i32 0, i32 1)`)
104 %26:i32 = CONST_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
105 %25:i32 = CONST_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
106 STORE_I32 2, @__wasm_lpad_context, %26:i32, %25:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store 4 into `i32* getelementptr inbounds ({ i32, i8*, i32 }, { i32, i8*, i32 }* @__wasm_lpad_context, i32 0, i32 0)`)
107 %32:i32 = LOCAL_GET_I32 0, implicit-def $arguments
108 %31:i32 = CALL_I32 @_Unwind_CallPersonality, %32:i32, implicit-def dead $arguments, implicit $sp32, implicit $sp64
109 DROP_I32 killed %31:i32, implicit-def $arguments
110 %24:i32 = CONST_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
111 %17:i32 = LOAD_I32 2, @__wasm_lpad_context + 8, %24:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (dereferenceable load 4 from `i32* getelementptr inbounds ({ i32, i8*, i32 }, { i32, i8*, i32 }* @__wasm_lpad_context, i32 0, i32 2)`)
112 %18:i32 = CONST_I32 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
113 %19:i32 = NE_I32 %17:i32, %18:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
114 BR_IF %bb.7, %19:i32, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
115
116 bb.2:
117 ; predecessors: %bb.1
118 successors: %bb.8, %bb.3, %bb.6
119
120 %34:i32 = LOCAL_GET_I32 0, implicit-def $arguments
121 %33:i32 = CALL_I32 @__cxa_begin_catch, %34:i32, implicit-def dead $arguments, implicit $sp32, implicit $sp64
122 DROP_I32 killed %33:i32, implicit-def $arguments
123 CALL_VOID @may_throw, implicit-def dead $arguments, implicit $sp32, implicit $sp64
124 BR %bb.8, implicit-def $arguments
125 ; CHECK-LABEL: bb.2:
126 ; CHECK: DROP_I32
127 ; CHECK-NEXT: TRY
128 ; CHECK-NEXT: TRY
129 ; CHECK-NEXT: CALL_VOID @may_throw
130
131 bb.3 (landing-pad):
132 ; predecessors: %bb.2
133 successors: %bb.4, %bb.5
134
135 %35:i32 = CATCH_I32 0, implicit-def dead $arguments
136 LOCAL_SET_I32 0, %35:i32, implicit-def $arguments
137 %21:i32 = CONST_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
138 %20:i32 = CONST_I32 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
139 STORE_I32 2, @__wasm_lpad_context, %21:i32, %20:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store 4 into `i32* getelementptr inbounds ({ i32, i8*, i32 }, { i32, i8*, i32 }* @__wasm_lpad_context, i32 0, i32 0)`)
140 %37:i32 = LOCAL_GET_I32 0, implicit-def $arguments
141 %36:i32 = CALL_I32 @_Unwind_CallPersonality, %37:i32, implicit-def dead $arguments, implicit $sp32, implicit $sp64
142 DROP_I32 killed %36:i32, implicit-def $arguments
143 %29:i32 = CONST_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
144 %22:i32 = LOAD_I32 2, @__wasm_lpad_context + 8, %29:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (dereferenceable load 4 from `i32* getelementptr inbounds ({ i32, i8*, i32 }, { i32, i8*, i32 }* @__wasm_lpad_context, i32 0, i32 2)`)
145 %28:i32 = CONST_I32 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
146 %23:i32 = NE_I32 %22:i32, %28:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
147 BR_IF %bb.5, %23:i32, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
148
149 bb.4:
150 ; predecessors: %bb.3
151 successors: %bb.8
152
153 %39:i32 = LOCAL_GET_I32 0, implicit-def $arguments
154 %38:i32 = CALL_I32 @__cxa_begin_catch, %39:i32, implicit-def dead $arguments, implicit $sp32, implicit $sp64
155 DROP_I32 killed %38:i32, implicit-def $arguments
156 CALL_VOID @__cxa_end_catch, implicit-def dead $arguments, implicit $sp32, implicit $sp64
157 BR %bb.8, implicit-def $arguments
158
159 bb.5:
160 ; predecessors: %bb.3
161 successors: %bb.6
162
163 CALL_VOID @__cxa_rethrow, implicit-def dead $arguments, implicit $sp32, implicit $sp64
164 RETHROW %bb.6, implicit-def $arguments
165
166 bb.6 (landing-pad):
167 ; predecessors: %bb.2, %bb.5
168
169 CATCH_ALL implicit-def $arguments
170 CALL_VOID @__cxa_end_catch, implicit-def dead $arguments, implicit $sp32, implicit $sp64
171 RETHROW_TO_CALLER implicit-def $arguments
172 ; CHECK-LABEL: bb.6 (landing-pad):
173 ; CHECK-NEXT: END_TRY
174
175 bb.7:
176 ; predecessors: %bb.1
177
178 CALL_VOID @__cxa_rethrow, implicit-def dead $arguments, implicit $sp32, implicit $sp64
179 RETHROW_TO_CALLER implicit-def $arguments
180 ; CHECK-LABEL: bb.7:
181 ; CHECK-NEXT: END_TRY
182 ; CHECK: RETHROW 0
183
184 bb.8:
185 ; predecessors: %bb.2, %bb.4
186 successors: %bb.9
187
188 CALL_VOID @__cxa_end_catch, implicit-def dead $arguments, implicit $sp32, implicit $sp64
189
190 bb.9:
191 ; predecessors: %bb.0, %bb.8
192
193 RETURN_VOID implicit-def dead $arguments
194 ; CHECK-LABEL: bb.9:
195 ; CHECK-NEXT: END_TRY
196 ...
197 ---
198
199 # A loop within a try.
200 # try {
201 # for (int i = 0; i < n; ++i)
202 # may_throw();
203 # } catch (...) {
204 # }
205 name: test2
206 # CHECK-LABEL: name: test2
207 liveins:
208 - { reg: '$arguments', reg: '$value_stack' }
209 body: |
210 bb.0:
211 successors: %bb.1, %bb.4
212
213 %18:i32 = CONST_I32 0, implicit-def dead $arguments
214 LOCAL_SET_I32 1, %18:i32, implicit-def $arguments
215 %14:i32 = CONST_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
216 %19:i32 = LOCAL_GET_I32 0, implicit-def $arguments
217 %9:i32 = GE_S_I32 %14:i32, %19:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
218 BR_IF %bb.4, %9:i32, implicit-def $arguments
219
220 bb.1:
221 ; predecessors: %bb.0, %bb.3
222 successors: %bb.3, %bb.2
223
224 CALL_VOID @may_throw, implicit-def dead $arguments, implicit $sp32, implicit $sp64
225 BR %bb.3, implicit-def $arguments
226 ; CHECK-LABEL: bb.1:
227 ; CHECK: LOOP
228 ; CHECK: TRY
229 ; CHECK-NEXT: CALL_VOID @may_throw
230
231 bb.2 (landing-pad):
232 ; predecessors: %bb.1
233 successors: %bb.4
234
235 %11:i32 = CATCH_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
236 %22:i32 = CALL_I32 @__cxa_begin_catch, %11:i32, implicit-def dead $arguments, implicit $sp32, implicit $sp64, implicit-def $value_stack, implicit $value_stack
237 DROP_I32 killed %22:i32, implicit-def $arguments
238 CALL_VOID @__cxa_end_catch, implicit-def dead $arguments, implicit $sp32, implicit $sp64
239 BR %bb.4, implicit-def $arguments
240
241 bb.3:
242 ; predecessors: %bb.1
243 successors: %bb.1, %bb.4
244
245 %20:i32 = LOCAL_GET_I32 1, implicit-def $arguments
246 %17:i32 = CONST_I32 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
247 %16:i32 = ADD_I32 %20:i32, %17:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
248 %15:i32 = LOCAL_TEE_I32 1, %16:i32, implicit-def $arguments
249 %21:i32 = LOCAL_GET_I32 0, implicit-def $arguments
250 %10:i32 = GE_S_I32 %15:i32, %21:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
251 BR_UNLESS %bb.1, %10:i32, implicit-def $arguments
252 ; CHECK-LABEL: bb.3:
253 ; CHECK: END_TRY
254
255 bb.4:
256 ; predecessors: %bb.2, %bb.0, %bb.3
257
258 RETURN_VOID implicit-def dead $arguments
259 ...
260 ---
261
262 # A loop within a catch
263 # try {
264 # may_throw();
265 # } catch (...) {
266 # for (int i = 0; i < n; ++i)
267 # dont_throw();
268 # }
269 name: test3
270 # CHECK-LABEL: name: test3
271 liveins:
272 - { reg: '$arguments', reg: '$value_stack' }
273 body: |
274 bb.0:
275 successors: %bb.4, %bb.1
276
277 CALL_VOID @may_throw, implicit-def dead $arguments, implicit $sp32, implicit $sp64
278 BR %bb.4, implicit-def $arguments
279 ; CHECK-LABEL: bb.0:
280 ; CHECK: TRY
281 ; CHECK-NEXT: CALL_VOID @may_throw
282
283 bb.1 (landing-pad):
284 ; predecessors: %bb.0
285 successors: %bb.2, %bb.3
286
287 %9:i32 = CATCH_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
288 %18:i32 = CALL_I32 @__cxa_begin_catch, %9:i32, implicit-def dead $arguments, implicit $sp32, implicit $sp64, implicit-def $value_stack, implicit $value_stack
289 DROP_I32 killed %18:i32, implicit-def $arguments
290 %19:i32 = CONST_I32 0, implicit-def dead $arguments
291 LOCAL_SET_I32 1, %19:i32, implicit-def $arguments
292 %14:i32 = CONST_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
293 %20:i32 = LOCAL_GET_I32 0, implicit-def $arguments
294 %10:i32 = GE_S_I32 %14:i32, %20:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
295 BR_IF %bb.3, %10:i32, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
296
297 bb.2:
298 ; predecessors: %bb.1, %bb.2
299 successors: %bb.2, %bb.3
300
301 CALL_VOID @dont_throw, implicit-def dead $arguments, implicit $sp32, implicit $sp64
302 %21:i32 = LOCAL_GET_I32 1, implicit-def $arguments
303 %17:i32 = CONST_I32 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
304 %16:i32 = ADD_I32 %21:i32, %17:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
305 %15:i32 = LOCAL_TEE_I32 1, %16:i32, implicit-def $arguments
306 %22:i32 = LOCAL_GET_I32 0, implicit-def $arguments
307 %11:i32 = GE_S_I32 %15:i32, %22:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
308 BR_UNLESS %bb.2, %11:i32, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
309
310 bb.3:
311 ; predecessors: %bb.1, %bb.2
312 successors: %bb.4
313
314 CALL_VOID @__cxa_end_catch, implicit-def dead $arguments, implicit $sp32, implicit $sp64
315
316 bb.4:
317 ; predecessors: %bb.0, %bb.3
318
319 RETURN_VOID implicit-def dead $arguments
320 ; CHECK-LABEL: bb.4:
321 ; CHECK: END_TRY
0 ; RUN: not llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-keep-registers -exception-model=wasm
1 ; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-keep-registers -exception-model=wasm -mattr=+exception-handling -verify-machineinstrs | FileCheck -allow-deprecated-dag-overlap %s
1 ; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -exception-model=wasm -mattr=+exception-handling -verify-machineinstrs | FileCheck -allow-deprecated-dag-overlap %s
22 ; RUN: llc < %s -disable-wasm-fallthrough-return-opt -wasm-keep-registers -exception-model=wasm -mattr=+exception-handling
33
44 target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
88
99 @_ZTIi = external constant i8*
1010
11 declare void @llvm.wasm.throw(i32, i8*)
12
1311 ; CHECK-LABEL: test_throw:
14 ; CHECK: local.get $push0=, 0
15 ; CHECK-NEXT: throw __cpp_exception@EVENT, $pop0
12 ; CHECK: throw __cpp_exception@EVENT, $0
1613 ; CHECK-NOT: unreachable
1714 define void @test_throw(i8* %p) {
1815 call void @llvm.wasm.throw(i32 0, i8* %p)
1916 ret void
2017 }
2118
19 ; CHECK-LABEL: test_rethrow:
20 ; CHECK: rethrow
21 ; CHECK-NOT: unreachable
22 define void @test_rethrow(i8* %p) {
23 call void @llvm.wasm.rethrow()
24 ret void
25 }
26
2227 ; CHECK-LABEL: test_catch_rethrow:
23 ; CHECK: global.get $push{{.+}}=, __stack_pointer@GLOBAL
28 ; CHECK: global.get ${{.+}}=, __stack_pointer@GLOBAL
2429 ; CHECK: try
2530 ; CHECK: call foo@FUNCTION
26 ; CHECK: i32.catch $push{{.+}}=, 0
31 ; CHECK: catch $[[EXCEPT_REF:[0-9]+]]=
32 ; CHECK: block i32
33 ; CHECK: br_on_exn 0, __cpp_exception@EVENT, $[[EXCEPT_REF]]
34 ; CHECK: rethrow
35 ; CHECK: end_block
36 ; CHECK: extract_exception $[[EXN:[0-9]+]]=
2737 ; CHECK: global.set __stack_pointer@GLOBAL
2838 ; CHECK-DAG: i32.store __wasm_lpad_context
2939 ; CHECK-DAG: i32.store __wasm_lpad_context+4
30 ; CHECK: i32.call $push{{.+}}=, _Unwind_CallPersonality@FUNCTION
31 ; CHECK: i32.call $push{{.+}}=, __cxa_begin_catch@FUNCTION
40 ; CHECK: i32.call $drop=, _Unwind_CallPersonality@FUNCTION, $[[EXN]]
41 ; CHECK: i32.call $drop=, __cxa_begin_catch@FUNCTION
3242 ; CHECK: call __cxa_end_catch@FUNCTION
3343 ; CHECK: call __cxa_rethrow@FUNCTION
34 ; CHECK-NEXT: rethrow
3544 ; CHECK: end_try
3645 define void @test_catch_rethrow() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
3746 entry:
6574 ; CHECK-LABEL: test_cleanup:
6675 ; CHECK: try
6776 ; CHECK: call foo@FUNCTION
68 ; CHECK: catch_all
77 ; CHECK: catch
6978 ; CHECK: global.set __stack_pointer@GLOBAL
70 ; CHECK: i32.call $push{{.+}}=, _ZN7CleanupD1Ev@FUNCTION
79 ; CHECK: i32.call $drop=, _ZN7CleanupD1Ev@FUNCTION
7180 ; CHECK: rethrow
7281 ; CHECK: end_try
7382 define void @test_cleanup() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
8695 cleanupret from %0 unwind to caller
8796 }
8897
89 ; - Tests multple terminate pads are merged into one
90 ; - Tests a catch_all terminate pad is created after a catch terminate pad
91
9298 ; CHECK-LABEL: test_terminatepad
93 ; CHECK: i32.catch
94 ; CHECK: call __clang_call_terminate@FUNCTION
95 ; CHECK: unreachable
96 ; CHECK: catch_all
97 ; CHECK: call _ZSt9terminatev@FUNCTION
98 ; CHECK-NOT: call __clang_call_terminate@FUNCTION
99 define hidden i32 @test_terminatepad() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
100 entry:
101 %c = alloca %struct.Cleanup, align 1
102 %c1 = alloca %struct.Cleanup, align 1
103 invoke void @foo()
104 to label %invoke.cont unwind label %ehcleanup
105
106 invoke.cont: ; preds = %entry
107 %call = invoke %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %c1)
108 to label %try.cont unwind label %catch.dispatch
109
110 ehcleanup: ; preds = %entry
111 %0 = cleanuppad within none []
112 %call4 = invoke %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %c1) [ "funclet"(token %0) ]
113 to label %invoke.cont3 unwind label %terminate
114
115 invoke.cont3: ; preds = %ehcleanup
116 cleanupret from %0 unwind label %catch.dispatch
117
118 catch.dispatch: ; preds = %invoke.cont3, %invoke.cont
119 %1 = catchswitch within none [label %catch.start] unwind label %ehcleanup7
120
121 catch.start: ; preds = %catch.dispatch
122 %2 = catchpad within %1 [i8* null]
123 %3 = call i8* @llvm.wasm.get.exception(token %2)
124 %4 = call i32 @llvm.wasm.get.ehselector(token %2)
125 %5 = call i8* @__cxa_begin_catch(i8* %3) [ "funclet"(token %2) ]
126 invoke void @__cxa_end_catch() [ "funclet"(token %2) ]
127 to label %invoke.cont5 unwind label %ehcleanup7
128
129 invoke.cont5: ; preds = %catch.start
130 catchret from %2 to label %try.cont
131
132 try.cont: ; preds = %invoke.cont5, %invoke.cont
133 %call6 = call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %c)
134 ret i32 0
135
136 ehcleanup7: ; preds = %catch.start, %catch.dispatch
137 %6 = cleanuppad within none []
138 %call9 = invoke %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %c) [ "funclet"(token %6) ]
139 to label %invoke.cont8 unwind label %terminate10
140
141 invoke.cont8: ; preds = %ehcleanup7
142 cleanupret from %6 unwind to caller
99 ; CHECK: catch
100 ; CHECK: block i32
101 ; CHECK: br_on_exn 0, __cpp_exception@EVENT
102 ; CHECK: call __clang_call_terminate@FUNCTION, 0
103 ; CHECK: unreachable
104 ; CHECK: end_block
105 ; CHECK: extract_exception
106 ; CHECK: call __clang_call_terminate@FUNCTION
107 ; CHECK: unreachable
108 define void @test_terminatepad() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
109 entry:
110 invoke void @foo()
111 to label %try.cont unwind label %catch.dispatch
112
113 catch.dispatch: ; preds = %entry
114 %0 = catchswitch within none [label %catch.start] unwind to caller
115
116 catch.start: ; preds = %catch.dispatch
117 %1 = catchpad within %0 [i8* null]
118 %2 = call i8* @llvm.wasm.get.exception(token %1)
119 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
120 %4 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ]
121 invoke void @foo() [ "funclet"(token %1) ]
122 to label %invoke.cont1 unwind label %ehcleanup
123
124 invoke.cont1: ; preds = %catch.start
125 call void @__cxa_end_catch() [ "funclet"(token %1) ]
126 catchret from %1 to label %try.cont
127
128 try.cont: ; preds = %entry, %invoke.cont1
129 ret void
130
131 ehcleanup: ; preds = %catch.start
132 %5 = cleanuppad within %1 []
133 invoke void @__cxa_end_catch() [ "funclet"(token %5) ]
134 to label %invoke.cont2 unwind label %terminate
135
136 invoke.cont2: ; preds = %ehcleanup
137 cleanupret from %5 unwind to caller
143138
144139 terminate: ; preds = %ehcleanup
145 %7 = cleanuppad within %0 []
146 %8 = call i8* @llvm.wasm.get.exception(token %7)
147 call void @__clang_call_terminate(i8* %8) [ "funclet"(token %7) ]
148 unreachable
149
150 terminate10: ; preds = %ehcleanup7
151 %9 = cleanuppad within %6 []
152 %10 = call i8* @llvm.wasm.get.exception(token %9)
153 call void @__clang_call_terminate(i8* %10) [ "funclet"(token %9) ]
140 %6 = cleanuppad within %5 []
141 %7 = call i8* @llvm.wasm.get.exception(token %6)
142 call void @__clang_call_terminate(i8* %7) [ "funclet"(token %6) ]
154143 unreachable
155144 }
156145
163152 ; CHECK-LABEL: test_no_prolog_epilog_in_ehpad
164153 ; CHECK: try
165154 ; CHECK: call foo@FUNCTION
166 ; CHECK: i32.catch
155 ; CHECK: catch
167156 ; CHECK-NOT: global.get $push{{.+}}=, __stack_pointer@GLOBAL
168157 ; CHECK: global.set __stack_pointer@GLOBAL
169158 ; CHECK: try
170159 ; CHECK: call foo@FUNCTION
171 ; CHECK: catch_all
160 ; CHECK: catch
172161 ; CHECK-NOT: global.get $push{{.+}}=, __stack_pointer@GLOBAL
173162 ; CHECK: global.set __stack_pointer@GLOBAL
174163 ; CHECK: call __cxa_end_catch@FUNCTION
250239 declare void @foo()
251240 declare void @bar(i32*)
252241 declare i32 @__gxx_wasm_personality_v0(...)
242 declare void @llvm.wasm.throw(i32, i8*)
243 declare void @llvm.wasm.rethrow()
253244 declare i8* @llvm.wasm.get.exception(token)
254245 declare i32 @llvm.wasm.get.ehselector(token)
255246 declare i32 @llvm.eh.typeid.for(i8*)
257248 declare void @__cxa_end_catch()
258249 declare void @__cxa_rethrow()
259250 declare void @__clang_call_terminate(i8*)
260 declare void @_ZSt9terminatev()
261251 declare %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* returned)
262252
263253 ; CHECK: __cpp_exception:
2828 br i1 %matches, label %catch, label %rethrow
2929 ; CHECK: catch.start:
3030 ; CHECK-NEXT: %[[CATCHPAD:.*]] = catchpad
31 ; CHECK-NEXT: %[[EXN:.*]] = call i8* @llvm.wasm.catch(i32 0)
31 ; CHECK-NEXT: %[[EXN:.*]] = call i8* @llvm.wasm.extract.exception()
3232 ; CHECK-NEXT: call void @llvm.wasm.landingpad.index(token %[[CATCHPAD]], i32 0)
3333 ; CHECK-NEXT: store i32 0, i32* getelementptr inbounds ({ i32, i8*, i32 }, { i32, i8*, i32 }* @__wasm_lpad_context, i32 0, i32 0)
3434 ; CHECK-NEXT: %[[LSDA:.*]] = call i8* @llvm.wasm.lsda()
7575 catchret from %1 to label %try.cont
7676 ; CHECK: catch.start:
7777 ; CHECK-NEXT: catchpad within %0 [i8* null]
78 ; CHECK-NEXT: call i8* @llvm.wasm.catch(i32 0)
7978 ; CHECK-NOT: call void @llvm.wasm.landingpad.index
8079 ; CHECK-NOT: store {{.*}} @__wasm_lpad_context
8180 ; CHECK-NOT: call i8* @llvm.wasm.lsda()
177176 cleanupret from %12 unwind to caller
178177 ; CHECK: ehcleanup:
179178 ; CHECK-NEXT: cleanuppad
180 ; CHECK-NOT: call i8* @llvm.wasm.catch(i32 0)
181179 ; CHECK-NOT: call void @llvm.wasm.landingpad.index
182180 ; CHECK-NOT: store {{.*}} @__wasm_lpad_context
183181 ; CHECK-NOT: call i8* @llvm.wasm.lsda()
190188
191189 ; A cleanuppad with a call to __clang_call_terminate().
192190 ; A call to wasm.catch() should be generated after the cleanuppad.
193 define hidden void @test3() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
191 define void @test3() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
194192 ; CHECK-LABEL: @test3
195193 entry:
196194 invoke void @foo()
229227 unreachable
230228 ; CHECK: terminate:
231229 ; CHECK-NEXT: cleanuppad
232 ; CHECK-NEXT: %[[EXN:.*]] = call i8* @llvm.wasm.catch(i32 0)
230 ; CHECK-NEXT: %[[EXN:.*]] = call i8* @llvm.wasm.extract.exception
233231 ; CHECK-NEXT: call void @__clang_call_terminate(i8* %[[EXN]])
234232 }
235233
236234 ; PHI demotion test. Only the phi before catchswitch should be demoted; the phi
237235 ; before cleanuppad should NOT.
238 define void @test5() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
239 ; CHECK-LABEL: @test5
236 define void @test4() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
237 ; CHECK-LABEL: @test4
240238 entry:
241239 %c = alloca %struct.Cleanup, align 1
242240 invoke void @foo()
300298 ; Tests if instructions after a call to @llvm.wasm.throw are deleted and the
301299 ; BB's dead children are deleted.
302300
303 ; CHECK-LABEL: @test6
304 define i32 @test6(i1 %b, i8* %p) {
301 ; CHECK-LABEL: @test5
302 define i32 @test5(i1 %b, i8* %p) {
305303 entry:
306304 br i1 %b, label %bb.true, label %bb.false
307305
310308 ; CHECK-NEXT: unreachable
311309 bb.true: ; preds = %entry
312310 call void @llvm.wasm.throw(i32 0, i8* %p)
311 br label %bb.true.0
312
313 ; CHECK-NOT: bb.true.0
314 bb.true.0: ; preds = %bb.true
315 br label %merge
316
317 ; CHECK: bb.false
318 bb.false: ; preds = %entry
319 br label %merge
320
321 ; CHECK: merge
322 merge: ; preds = %bb.true.0, %bb.false
323 ret i32 0
324 }
325
326 ; Tests if instructions after a call to @llvm.wasm.rethrow are deleted and the
327 ; BB's dead children are deleted.
328
329 ; CHECK-LABEL: @test6
330 define i32 @test6(i1 %b, i8* %p) {
331 entry:
332 br i1 %b, label %bb.true, label %bb.false
333
334 ; CHECK: bb.true:
335 ; CHECK-NEXT: call void @llvm.wasm.rethrow()
336 ; CHECK-NEXT: unreachable
337 bb.true: ; preds = %entry
338 call void @llvm.wasm.rethrow()
313339 br label %bb.true.0
314340
315341 ; CHECK-NOT: bb.true.0
333359 declare i32 @llvm.wasm.get.ehselector(token)
334360 declare i32 @llvm.eh.typeid.for(i8*)
335361 declare void @llvm.wasm.throw(i32, i8*)
362 declare void @llvm.wasm.rethrow()
336363 declare i8* @__cxa_begin_catch(i8*)
337364 declare void @__cxa_end_catch()
338365 declare void @__cxa_rethrow()
339366 declare void @__clang_call_terminate(i8*)
340367
341 ; CHECK-DAG: declare i8* @llvm.wasm.catch(i32)
342368 ; CHECK-DAG: declare void @llvm.wasm.landingpad.index(token, i32)
343369 ; CHECK-DAG: declare i8* @llvm.wasm.lsda()
344370 ; CHECK-DAG: declare i32 @_Unwind_CallPersonality(i8*)
0 # RUN: llvm-mc -triple=wasm32-unknown-unknown -mattr=+exception-handling < %s | FileCheck %s
1
2 # Tests if block/loop/try/catch/end/branch/rethrow instructions are correctly
3 # printed with their annotations.
4
5 .text
6 .section .text.test_annotation,"",@
7 .type test_annotation,@function
8 test_annotation:
9 .functype test_annotation () -> ()
10 .eventtype __cpp_exception i32
11 try
12 br 0
13 catch
14 block
15 br_if 0
16 loop
17 br_if 1
18 end_loop
19 end_block
20 try
21 rethrow
22 catch
23 block
24 try
25 br 0
26 catch
27 local.set 0
28 block i32
29 local.get 0
30 br_on_exn 0, __cpp_exception@EVENT
31 rethrow
32 end_block
33 end_try
34 end_block
35 rethrow
36 end_try
37 end_try
38 end_function
39
40
41 # CHECK: test_annotation:
42 # CHECK: try
43 # CHECK-NEXT: br 0 # 0: down to label0
44 # CHECK-NEXT: catch # catch0:
45 # CHECK-NEXT: block
46 # CHECK-NEXT: br_if 0 # 0: down to label1
47 # CHECK-NEXT: loop # label2:
48 # CHECK-NEXT: br_if 1 # 1: down to label1
49 # CHECK-NEXT: end_loop
50 # CHECK-NEXT: end_block # label1:
51 # CHECK-NEXT: try
52 # CHECK-NEXT: rethrow # down to catch1
53 # CHECK-NEXT: catch # catch1:
54 # CHECK-NEXT: block
55 # CHECK-NEXT: try
56 # CHECK-NEXT: br 0 # 0: down to label5
57 # CHECK-NEXT: catch # catch2:
58 # CHECK-NEXT: local.set 0
59 # CHECK-NEXT: block i32
60 # CHECK-NEXT: local.get 0
61 # CHECK-NEXT: br_on_exn 0, __cpp_exception@EVENT # 0: down to label6
62 # CHECK-NEXT: rethrow # to caller
63 # CHECK-NEXT: end_block # label6:
64 # CHECK-NEXT: end_try # label5:
65 # CHECK-NEXT: end_block # label4:
66 # CHECK-NEXT: rethrow # to caller
67 # CHECK-NEXT: end_try # label3:
68 # CHECK-NEXT: end_try # label0:
69 # CHECK-NEXT: end_function
70
7070 i32.trunc_f32_s
7171 try except_ref
7272 .LBB0_3:
73 i32.catch 0
73 catch
74 local.set 0
75 block i32
76 local.get 0
77 br_on_exn 0, __cpp_exception@EVENT
78 rethrow
7479 .LBB0_4:
75 catch_all
80 end_block
81 end_try
82 i32.const 0
83 throw 0
7684 .LBB0_5:
77 end_try
7885 #i32.trunc_sat_f32_s
7986 global.get __stack_pointer@GLOBAL
8087 end_function
142149 # CHECK-NEXT: i32.trunc_f32_s
143150 # CHECK-NEXT: try except_ref
144151 # CHECK-NEXT: .LBB0_3:
145 # CHECK-NEXT: i32.catch 0
152 # CHECK-NEXT: catch
153 # CHECK-NEXT: local.set 0
154 # CHECK-NEXT: block i32
155 # CHECK-NEXT: local.get 0
156 # CHECK-NEXT: br_on_exn 0, __cpp_exception@EVENT
157 # CHECK-NEXT: rethrow
146158 # CHECK-NEXT: .LBB0_4:
147 # CHECK-NEXT: catch_all
159 # CHECK-NEXT: end_block
160 # CHECK-NEXT: end_try
161 # CHECK-NEXT: i32.const 0
162 # CHECK-NEXT: throw 0
148163 # CHECK-NEXT: .LBB0_5:
149 # CHECK-NEXT: end_try
150164 # CHECK-NEXT: global.get __stack_pointer@GLOBAL
151165 # CHECK-NEXT: end_function
152166
7373
7474 declare i32 @__gxx_wasm_personality_v0(...)
7575
76 define hidden void @test0() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
76 define void @test0() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
7777 unreachable
7878 }
7979
9999 ; predecessors: %bb.0
100100 successors: %bb.3, %bb.9
101101 liveins: $value_stack
102 CATCH_ALL implicit-def $arguments
102 %0:except_ref = CATCH implicit-def $arguments
103103 CLEANUPRET implicit-def dead $arguments
104104
105105 bb.3 (landing-pad):
106106 ; predecessors: %bb.2
107107 successors: %bb.4, %bb.6
108108 liveins: $value_stack
109 CATCH_ALL implicit-def $arguments
109 %1:except_ref = CATCH implicit-def $arguments
110110 BR_IF %bb.4, %58:i32, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
111111 BR %bb.6, implicit-def $arguments
112112
137137 ; predecessors: %bb.4
138138 successors: %bb.9
139139 liveins: $value_stack
140 CATCH_ALL implicit-def $arguments
140 %2:except_ref = CATCH implicit-def $arguments
141141 CLEANUPRET implicit-def dead $arguments
142142
143143 bb.9 (landing-pad):
144144 ; predecessors: %bb.2, %bb.6, %bb.8
145145 liveins: $value_stack
146 CATCH_ALL implicit-def $arguments
146 %3:except_ref = CATCH implicit-def $arguments
147147 CLEANUPRET implicit-def dead $arguments
148148
149149 bb.10:
236236
237237 declare i32 @__gxx_wasm_personality_v0(...)
238238
239 define hidden void @test1() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
239 define void @test1() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
240240 unreachable
241241 }
242242
256256 ; predecessors: %bb.0
257257 successors: %bb.2, %bb.8
258258 liveins: $value_stack
259 %52:i32 = CATCH_I32 0, implicit-def dead $arguments
259 %0:except_ref = CATCH implicit-def $arguments
260260 BR_IF %bb.2, %32:i32, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
261261 BR %bb.8, implicit-def $arguments
262262
270270 ; predecessors: %bb.2
271271 successors: %bb.4, %bb.6
272272 liveins: $value_stack
273 CATCH_ALL implicit-def $arguments
273 %1:except_ref = CATCH implicit-def $arguments
274274 BR_IF %bb.4, %43:i32, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
275275 BR %bb.6, implicit-def $arguments
276276
312312 ; predecessors: %bb.4
313313 successors: %bb.11
314314 liveins: $value_stack
315 CATCH_ALL implicit-def $arguments
315 %2:except_ref = CATCH implicit-def $arguments
316316 CLEANUPRET implicit-def dead $arguments
317317
318318 bb.11 (landing-pad):
319319 ; predecessors: %bb.2, %bb.6, %bb.10
320320 liveins: $value_stack
321 CATCH_ALL implicit-def $arguments
321 %3:except_ref = CATCH implicit-def $arguments
322322 CLEANUPRET implicit-def dead $arguments
323323
324324 bb.12:
414414 EXPECT_EQ(WE0_1->getParentException(), WE0);
415415 EXPECT_EQ(WE0_1->getExceptionDepth(), (unsigned)2);
416416 }
417
418 // Terminate pad test
419 TEST(WebAssemblyExceptionInfoTest, TEST2) {
420 std::unique_ptr TM = createTargetMachine();
421 ASSERT_TRUE(TM);
422
423 StringRef MIRString = R"MIR(
424 --- |
425 target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
426 target triple = "wasm32-unknown-unknown"
427
428 declare i32 @__gxx_wasm_personality_v0(...)
429 declare void @_ZSt9terminatev()
430 declare void @__clang_call_terminate(i8*)
431
432 define hidden void @test2() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
433 unreachable
434 }
435
436 ...
437 ---
438 name: test2
439 liveins:
440 - { reg: '$arguments' }
441 - { reg: '$value_stack' }
442 body: |
443 bb.0:
444 successors: %bb.3, %bb.1
445 BR %bb.3, implicit-def dead $arguments
446
447 bb.1 (landing-pad):
448 ; predecessors: %bb.0
449 successors: %bb.2, %bb.4
450 %3:i32 = CATCH_I32 0, implicit-def dead $arguments
451 BR %bb.2, implicit-def dead $arguments
452
453 bb.2:
454 ; predecessors: %bb.1
455 successors: %bb.3(0x80000000); %bb.3(200.00%)
456 CATCHRET %bb.3, %bb.0, implicit-def dead $arguments
457
458 bb.3:
459 ; predecessors: %bb.0, %bb.2
460 RETURN_VOID implicit-def $arguments
461
462 bb.4 (landing-pad):
463 ; predecessors: %bb.1
464 successors: %bb.5, %bb.6
465 CATCH_ALL implicit-def $arguments
466 BR %bb.5, implicit-def dead $arguments
467
468 bb.5:
469 ; predecessors: %bb.4
470 CLEANUPRET implicit-def dead $arguments
471
472 bb.6 (landing-pad):
473 ; predecessors: %bb.4
474 successors: %bb.7(0x80000000); %bb.7(200.00%)
475 %6:i32 = CATCH_I32 0, implicit-def dead $arguments
476 CALL_VOID @__clang_call_terminate, %7:i32, implicit-def $arguments
477 UNREACHABLE implicit-def $arguments
478
479 bb.7 (landing-pad):
480 ; predecessors: %bb.6
481 CATCH_ALL implicit-def $arguments
482 CALL_VOID @_ZSt9terminatev, implicit-def $arguments
483 UNREACHABLE implicit-def $arguments
484 )MIR";
485
486 LLVMContext Context;
487 std::unique_ptr MIR;
488 MachineModuleInfo MMI(TM.get());
489 std::unique_ptr M =
490 parseMIR(Context, MIR, *TM, MIRString, "test2", MMI);
491 ASSERT_TRUE(M);
492
493 Function *F = M->getFunction("test2");
494 auto *MF = MMI.getMachineFunction(*F);
495 ASSERT_TRUE(MF);
496
497 WebAssemblyExceptionInfo WEI;
498 MachineDominatorTree MDT;
499 MachineDominanceFrontier MDF;
500 MDT.runOnMachineFunction(*MF);
501 MDF.getBase().analyze(MDT.getBase());
502 WEI.recalculate(MDT, MDF);
503
504 // Exception info structure:
505 // |- bb1 (ehpad), bb2, bb4, bb5, bb6, bb7
506 // |- bb4 (ehpad), bb5, bb6, bb7
507 // |- bb6 (ehpad), bb7
508 //
509 // Here, bb6 is a terminate pad with a 'catch' instruction, and bb7 is a
510 // terminate pad with a 'catch_all' instruction, In this case we put bb6 and
511 // bb7 into one exception.
512
513 auto *MBB1 = MF->getBlockNumbered(1);
514 auto *WE0 = WEI.getExceptionFor(MBB1);
515 ASSERT_TRUE(WE0);
516 EXPECT_EQ(WE0->getEHPad(), MBB1);
517 EXPECT_EQ(WE0->getParentException(), nullptr);
518 EXPECT_EQ(WE0->getExceptionDepth(), (unsigned)1);
519
520 auto *MBB2 = MF->getBlockNumbered(2);
521 WE0 = WEI.getExceptionFor(MBB2);
522 ASSERT_TRUE(WE0);
523 EXPECT_EQ(WE0->getEHPad(), MBB1);
524
525 auto *MBB4 = MF->getBlockNumbered(4);
526 auto *WE0_0 = WEI.getExceptionFor(MBB4);
527 ASSERT_TRUE(WE0_0);
528 EXPECT_EQ(WE0_0->getEHPad(), MBB4);
529 EXPECT_EQ(WE0_0->getParentException(), WE0);
530 EXPECT_EQ(WE0_0->getExceptionDepth(), (unsigned)2);
531
532 auto *MBB5 = MF->getBlockNumbered(5);
533 WE0_0 = WEI.getExceptionFor(MBB5);
534 ASSERT_TRUE(WE0_0);
535 EXPECT_EQ(WE0_0->getEHPad(), MBB4);
536
537 auto *MBB6 = MF->getBlockNumbered(6);
538 auto *WE0_0_0 = WEI.getExceptionFor(MBB6);
539 ASSERT_TRUE(WE0_0_0);
540 EXPECT_EQ(WE0_0_0->getEHPad(), MBB6);
541 EXPECT_EQ(WE0_0_0->getParentException(), WE0_0);
542 EXPECT_EQ(WE0_0_0->getExceptionDepth(), (unsigned)3);
543
544 auto *MBB7 = MF->getBlockNumbered(7);
545 WE0_0_0 = WEI.getExceptionFor(MBB7);
546 ASSERT_TRUE(WE0_0_0);
547 EXPECT_EQ(WE0_0_0->getEHPad(), MBB6);
548 }