LLVM 20.0.0git
WebAssemblyLowerEmscriptenEHSjLj.cpp
Go to the documentation of this file.
1//=== WebAssemblyLowerEmscriptenEHSjLj.cpp - Lower exceptions for Emscripten =//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8///
9/// \file
10/// This file lowers exception-related instructions and setjmp/longjmp function
11/// calls to use Emscripten's library functions. The pass uses JavaScript's try
12/// and catch mechanism in case of Emscripten EH/SjLj and Wasm EH intrinsics in
13/// case of Emscripten SjLJ.
14///
15/// * Emscripten exception handling
16/// This pass lowers invokes and landingpads into library functions in JS glue
17/// code. Invokes are lowered into function wrappers called invoke wrappers that
18/// exist in JS side, which wraps the original function call with JS try-catch.
19/// If an exception occurred, cxa_throw() function in JS side sets some
20/// variables (see below) so we can check whether an exception occurred from
21/// wasm code and handle it appropriately.
22///
23/// * Emscripten setjmp-longjmp handling
24/// This pass lowers setjmp to a reasonably-performant approach for emscripten.
25/// The idea is that each block with a setjmp is broken up into two parts: the
26/// part containing setjmp and the part right after the setjmp. The latter part
27/// is either reached from the setjmp, or later from a longjmp. To handle the
28/// longjmp, all calls that might longjmp are also called using invoke wrappers
29/// and thus JS / try-catch. JS longjmp() function also sets some variables so
30/// we can check / whether a longjmp occurred from wasm code. Each block with a
31/// function call that might longjmp is also split up after the longjmp call.
32/// After the longjmp call, we check whether a longjmp occurred, and if it did,
33/// which setjmp it corresponds to, and jump to the right post-setjmp block.
34/// We assume setjmp-longjmp handling always run after EH handling, which means
35/// we don't expect any exception-related instructions when SjLj runs.
36/// FIXME Currently this scheme does not support indirect call of setjmp,
37/// because of the limitation of the scheme itself. fastcomp does not support it
38/// either.
39///
40/// In detail, this pass does following things:
41///
42/// 1) Assumes the existence of global variables: __THREW__, __threwValue
43/// __THREW__ and __threwValue are defined in compiler-rt in Emscripten.
44/// These variables are used for both exceptions and setjmp/longjmps.
45/// __THREW__ indicates whether an exception or a longjmp occurred or not. 0
46/// means nothing occurred, 1 means an exception occurred, and other numbers
47/// mean a longjmp occurred. In the case of longjmp, __THREW__ variable
48/// indicates the corresponding setjmp buffer the longjmp corresponds to.
49/// __threwValue is 0 for exceptions, and the argument to longjmp in case of
50/// longjmp.
51///
52/// * Emscripten exception handling
53///
54/// 2) We assume the existence of setThrew and setTempRet0/getTempRet0 functions
55/// at link time. setThrew exists in Emscripten's compiler-rt:
56///
57/// void setThrew(uintptr_t threw, int value) {
58/// if (__THREW__ == 0) {
59/// __THREW__ = threw;
60/// __threwValue = value;
61/// }
62/// }
63//
64/// setTempRet0 is called from __cxa_find_matching_catch() in JS glue code.
65/// In exception handling, getTempRet0 indicates the type of an exception
66/// caught, and in setjmp/longjmp, it means the second argument to longjmp
67/// function.
68///
69/// 3) Lower
70/// invoke @func(arg1, arg2) to label %invoke.cont unwind label %lpad
71/// into
72/// __THREW__ = 0;
73/// call @__invoke_SIG(func, arg1, arg2)
74/// %__THREW__.val = __THREW__;
75/// __THREW__ = 0;
76/// if (%__THREW__.val == 1)
77/// goto %lpad
78/// else
79/// goto %invoke.cont
80/// SIG is a mangled string generated based on the LLVM IR-level function
81/// signature. After LLVM IR types are lowered to the target wasm types,
82/// the names for these wrappers will change based on wasm types as well,
83/// as in invoke_vi (function takes an int and returns void). The bodies of
84/// these wrappers will be generated in JS glue code, and inside those
85/// wrappers we use JS try-catch to generate actual exception effects. It
86/// also calls the original callee function. An example wrapper in JS code
87/// would look like this:
88/// function invoke_vi(index,a1) {
89/// try {
90/// Module["dynCall_vi"](index,a1); // This calls original callee
91/// } catch(e) {
92/// if (typeof e !== 'number' && e !== 'longjmp') throw e;
93/// _setThrew(1, 0); // setThrew is called here
94/// }
95/// }
96/// If an exception is thrown, __THREW__ will be set to true in a wrapper,
97/// so we can jump to the right BB based on this value.
98///
99/// 4) Lower
100/// %val = landingpad catch c1 catch c2 catch c3 ...
101/// ... use %val ...
102/// into
103/// %fmc = call @__cxa_find_matching_catch_N(c1, c2, c3, ...)
104/// %val = {%fmc, getTempRet0()}
105/// ... use %val ...
106/// Here N is a number calculated based on the number of clauses.
107/// setTempRet0 is called from __cxa_find_matching_catch() in JS glue code.
108///
109/// 5) Lower
110/// resume {%a, %b}
111/// into
112/// call @__resumeException(%a)
113/// where __resumeException() is a function in JS glue code.
114///
115/// 6) Lower
116/// call @llvm.eh.typeid.for(type) (intrinsic)
117/// into
118/// call @llvm_eh_typeid_for(type)
119/// llvm_eh_typeid_for function will be generated in JS glue code.
120///
121/// * Emscripten setjmp / longjmp handling
122///
123/// If there are calls to longjmp()
124///
125/// 1) Lower
126/// longjmp(env, val)
127/// into
128/// emscripten_longjmp(env, val)
129///
130/// If there are calls to setjmp()
131///
132/// 2) In the function entry that calls setjmp, initialize
133/// functionInvocationId as follows:
134///
135/// functionInvocationId = alloca(4)
136///
137/// Note: the alloca size is not important as this pointer is
138/// merely used for pointer comparisions.
139///
140/// 3) Lower
141/// setjmp(env)
142/// into
143/// __wasm_setjmp(env, label, functionInvocationId)
144///
145/// __wasm_setjmp records the necessary info (the label and
146/// functionInvocationId) to the "env".
147/// A BB with setjmp is split into two after setjmp call in order to
148/// make the post-setjmp BB the possible destination of longjmp BB.
149///
150/// 4) Lower every call that might longjmp into
151/// __THREW__ = 0;
152/// call @__invoke_SIG(func, arg1, arg2)
153/// %__THREW__.val = __THREW__;
154/// __THREW__ = 0;
155/// %__threwValue.val = __threwValue;
156/// if (%__THREW__.val != 0 & %__threwValue.val != 0) {
157/// %label = __wasm_setjmp_test(%__THREW__.val, functionInvocationId);
158/// if (%label == 0)
159/// emscripten_longjmp(%__THREW__.val, %__threwValue.val);
160/// setTempRet0(%__threwValue.val);
161/// } else {
162/// %label = -1;
163/// }
164/// longjmp_result = getTempRet0();
165/// switch %label {
166/// label 1: goto post-setjmp BB 1
167/// label 2: goto post-setjmp BB 2
168/// ...
169/// default: goto splitted next BB
170/// }
171///
172/// __wasm_setjmp_test examines the jmp buf to see if it was for a matching
173/// setjmp call. After calling an invoke wrapper, if a longjmp occurred,
174/// __THREW__ will be the address of matching jmp_buf buffer and
175/// __threwValue be the second argument to longjmp.
176/// __wasm_setjmp_test returns a setjmp label, a unique ID to each setjmp
177/// callsite. Label 0 means this longjmp buffer does not correspond to one
178/// of the setjmp callsites in this function, so in this case we just chain
179/// the longjmp to the caller. Label -1 means no longjmp occurred.
180/// Otherwise we jump to the right post-setjmp BB based on the label.
181///
182/// * Wasm setjmp / longjmp handling
183/// This mode still uses some Emscripten library functions but not JavaScript's
184/// try-catch mechanism. It instead uses Wasm exception handling intrinsics,
185/// which will be lowered to exception handling instructions.
186///
187/// If there are calls to longjmp()
188///
189/// 1) Lower
190/// longjmp(env, val)
191/// into
192/// __wasm_longjmp(env, val)
193///
194/// If there are calls to setjmp()
195///
196/// 2) and 3): The same as 2) and 3) in Emscripten SjLj.
197/// (functionInvocationId initialization + setjmp callsite transformation)
198///
199/// 4) Create a catchpad with a wasm.catch() intrinsic, which returns the value
200/// thrown by __wasm_longjmp function. In the runtime library, we have an
201/// equivalent of the following struct:
202///
203/// struct __WasmLongjmpArgs {
204/// void *env;
205/// int val;
206/// };
207///
208/// The thrown value here is a pointer to the struct. We use this struct to
209/// transfer two values by throwing a single value. Wasm throw and catch
210/// instructions are capable of throwing and catching multiple values, but
211/// it also requires multivalue support that is currently not very reliable.
212/// TODO Switch to throwing and catching two values without using the struct
213///
214/// All longjmpable function calls will be converted to an invoke that will
215/// unwind to this catchpad in case a longjmp occurs. Within the catchpad, we
216/// test the thrown values using __wasm_setjmp_test function as we do for
217/// Emscripten SjLj. The main difference is, in Emscripten SjLj, we need to
218/// transform every longjmpable callsite into a sequence of code including
219/// __wasm_setjmp_test() call; in Wasm SjLj we do the testing in only one
220/// place, in this catchpad.
221///
222/// After testing calling __wasm_setjmp_test(), if the longjmp does not
223/// correspond to one of the setjmps within the current function, it rethrows
224/// the longjmp by calling __wasm_longjmp(). If it corresponds to one of
225/// setjmps in the function, we jump to the beginning of the function, which
226/// contains a switch to each post-setjmp BB. Again, in Emscripten SjLj, this
227/// switch is added for every longjmpable callsite; in Wasm SjLj we do this
228/// only once at the top of the function. (after functionInvocationId
229/// initialization)
230///
231/// The below is the pseudocode for what we have described
232///
233/// entry:
234/// Initialize functionInvocationId
235///
236/// setjmp.dispatch:
237/// switch %label {
238/// label 1: goto post-setjmp BB 1
239/// label 2: goto post-setjmp BB 2
240/// ...
241/// default: goto splitted next BB
242/// }
243/// ...
244///
245/// bb:
246/// invoke void @foo() ;; foo is a longjmpable function
247/// to label %next unwind label %catch.dispatch.longjmp
248/// ...
249///
250/// catch.dispatch.longjmp:
251/// %0 = catchswitch within none [label %catch.longjmp] unwind to caller
252///
253/// catch.longjmp:
254/// %longjmp.args = wasm.catch() ;; struct __WasmLongjmpArgs
255/// %env = load 'env' field from __WasmLongjmpArgs
256/// %val = load 'val' field from __WasmLongjmpArgs
257/// %label = __wasm_setjmp_test(%env, functionInvocationId);
258/// if (%label == 0)
259/// __wasm_longjmp(%env, %val)
260/// catchret to %setjmp.dispatch
261///
262///===----------------------------------------------------------------------===//
263
265#include "WebAssembly.h"
271#include "llvm/IR/Dominators.h"
272#include "llvm/IR/IRBuilder.h"
273#include "llvm/IR/IntrinsicsWebAssembly.h"
274#include "llvm/IR/Module.h"
280#include <set>
281
282using namespace llvm;
283
284#define DEBUG_TYPE "wasm-lower-em-ehsjlj"
285
287 EHAllowlist("emscripten-cxx-exceptions-allowed",
288 cl::desc("The list of function names in which Emscripten-style "
289 "exception handling is enabled (see emscripten "
290 "EMSCRIPTEN_CATCHING_ALLOWED options)"),
292
293namespace {
294class WebAssemblyLowerEmscriptenEHSjLj final : public ModulePass {
295 bool EnableEmEH; // Enable Emscripten exception handling
296 bool EnableEmSjLj; // Enable Emscripten setjmp/longjmp handling
297 bool EnableWasmSjLj; // Enable Wasm setjmp/longjmp handling
298 bool DoSjLj; // Whether we actually perform setjmp/longjmp handling
299
300 GlobalVariable *ThrewGV = nullptr; // __THREW__ (Emscripten)
301 GlobalVariable *ThrewValueGV = nullptr; // __threwValue (Emscripten)
302 Function *GetTempRet0F = nullptr; // getTempRet0() (Emscripten)
303 Function *SetTempRet0F = nullptr; // setTempRet0() (Emscripten)
304 Function *ResumeF = nullptr; // __resumeException() (Emscripten)
305 Function *EHTypeIDF = nullptr; // llvm.eh.typeid.for() (intrinsic)
306 Function *EmLongjmpF = nullptr; // emscripten_longjmp() (Emscripten)
307 Function *WasmSetjmpF = nullptr; // __wasm_setjmp() (Emscripten)
308 Function *WasmSetjmpTestF = nullptr; // __wasm_setjmp_test() (Emscripten)
309 Function *WasmLongjmpF = nullptr; // __wasm_longjmp() (Emscripten)
310 Function *CatchF = nullptr; // wasm.catch() (intrinsic)
311
312 // type of 'struct __WasmLongjmpArgs' defined in emscripten
313 Type *LongjmpArgsTy = nullptr;
314
315 // __cxa_find_matching_catch_N functions.
316 // Indexed by the number of clauses in an original landingpad instruction.
317 DenseMap<int, Function *> FindMatchingCatches;
318 // Map of <function signature string, invoke_ wrappers>
319 StringMap<Function *> InvokeWrappers;
320 // Set of allowed function names for exception handling
321 std::set<std::string, std::less<>> EHAllowlistSet;
322 // Functions that contains calls to setjmp
323 SmallPtrSet<Function *, 8> SetjmpUsers;
324
325 StringRef getPassName() const override {
326 return "WebAssembly Lower Emscripten Exceptions";
327 }
328
329 using InstVector = SmallVectorImpl<Instruction *>;
330 bool runEHOnFunction(Function &F);
331 bool runSjLjOnFunction(Function &F);
332 void handleLongjmpableCallsForEmscriptenSjLj(
333 Function &F, Instruction *FunctionInvocationId,
334 SmallVectorImpl<PHINode *> &SetjmpRetPHIs);
335 void
336 handleLongjmpableCallsForWasmSjLj(Function &F,
337 Instruction *FunctionInvocationId,
338 SmallVectorImpl<PHINode *> &SetjmpRetPHIs);
339 Function *getFindMatchingCatch(Module &M, unsigned NumClauses);
340
341 Value *wrapInvoke(CallBase *CI);
342 void wrapTestSetjmp(BasicBlock *BB, DebugLoc DL, Value *Threw,
343 Value *FunctionInvocationId, Value *&Label,
344 Value *&LongjmpResult, BasicBlock *&CallEmLongjmpBB,
345 PHINode *&CallEmLongjmpBBThrewPHI,
346 PHINode *&CallEmLongjmpBBThrewValuePHI,
347 BasicBlock *&EndBB);
348 Function *getInvokeWrapper(CallBase *CI);
349
350 bool areAllExceptionsAllowed() const { return EHAllowlistSet.empty(); }
351 bool supportsException(const Function *F) const {
352 return EnableEmEH &&
353 (areAllExceptionsAllowed() || EHAllowlistSet.count(F->getName()));
354 }
355 void replaceLongjmpWith(Function *LongjmpF, Function *NewF);
356
357 void rebuildSSA(Function &F);
358
359public:
360 static char ID;
361
362 WebAssemblyLowerEmscriptenEHSjLj()
363 : ModulePass(ID), EnableEmEH(WebAssembly::WasmEnableEmEH),
364 EnableEmSjLj(WebAssembly::WasmEnableEmSjLj),
365 EnableWasmSjLj(WebAssembly::WasmEnableSjLj) {
366 assert(!(EnableEmSjLj && EnableWasmSjLj) &&
367 "Two SjLj modes cannot be turned on at the same time");
368 assert(!(EnableEmEH && EnableWasmSjLj) &&
369 "Wasm SjLj should be only used with Wasm EH");
370 EHAllowlistSet.insert(EHAllowlist.begin(), EHAllowlist.end());
371 }
372 bool runOnModule(Module &M) override;
373
374 void getAnalysisUsage(AnalysisUsage &AU) const override {
376 }
377};
378} // End anonymous namespace
379
380char WebAssemblyLowerEmscriptenEHSjLj::ID = 0;
381INITIALIZE_PASS(WebAssemblyLowerEmscriptenEHSjLj, DEBUG_TYPE,
382 "WebAssembly Lower Emscripten Exceptions / Setjmp / Longjmp",
383 false, false)
384
386 return new WebAssemblyLowerEmscriptenEHSjLj();
387}
388
389static bool canThrow(const Value *V) {
390 if (const auto *F = dyn_cast<const Function>(V)) {
391 // Intrinsics cannot throw
392 if (F->isIntrinsic())
393 return false;
394 StringRef Name = F->getName();
395 // leave setjmp and longjmp (mostly) alone, we process them properly later
396 if (Name == "setjmp" || Name == "longjmp" || Name == "emscripten_longjmp")
397 return false;
398 return !F->doesNotThrow();
399 }
400 // not a function, so an indirect call - can throw, we can't tell
401 return true;
402}
403
404// Get a thread-local global variable with the given name. If it doesn't exist
405// declare it, which will generate an import and assume that it will exist at
406// link time.
409 const char *Name) {
410 auto *GV = dyn_cast<GlobalVariable>(M.getOrInsertGlobal(Name, Ty));
411 if (!GV)
412 report_fatal_error(Twine("unable to create global: ") + Name);
413
414 // Variables created by this function are thread local. If the target does not
415 // support TLS, we depend on CoalesceFeaturesAndStripAtomics to downgrade it
416 // to non-thread-local ones, in which case we don't allow this object to be
417 // linked with other objects using shared memory.
418 GV->setThreadLocalMode(GlobalValue::GeneralDynamicTLSModel);
419 return GV;
420}
421
422// Simple function name mangler.
423// This function simply takes LLVM's string representation of parameter types
424// and concatenate them with '_'. There are non-alphanumeric characters but llc
425// is ok with it, and we need to postprocess these names after the lowering
426// phase anyway.
427static std::string getSignature(FunctionType *FTy) {
428 std::string Sig;
430 OS << *FTy->getReturnType();
431 for (Type *ParamTy : FTy->params())
432 OS << "_" << *ParamTy;
433 if (FTy->isVarArg())
434 OS << "_...";
435 Sig = OS.str();
436 erase_if(Sig, isSpace);
437 // When s2wasm parses .s file, a comma means the end of an argument. So a
438 // mangled function name can contain any character but a comma.
439 std::replace(Sig.begin(), Sig.end(), ',', '.');
440 return Sig;
441}
442
444 Module *M) {
446 // Tell the linker that this function is expected to be imported from the
447 // 'env' module.
448 if (!F->hasFnAttribute("wasm-import-module")) {
449 llvm::AttrBuilder B(M->getContext());
450 B.addAttribute("wasm-import-module", "env");
451 F->addFnAttrs(B);
452 }
453 if (!F->hasFnAttribute("wasm-import-name")) {
454 llvm::AttrBuilder B(M->getContext());
455 B.addAttribute("wasm-import-name", F->getName());
456 F->addFnAttrs(B);
457 }
458 return F;
459}
460
461// Returns an integer type for the target architecture's address space.
462// i32 for wasm32 and i64 for wasm64.
464 IRBuilder<> IRB(M->getContext());
465 return IRB.getIntNTy(M->getDataLayout().getPointerSizeInBits());
466}
467
468// Returns an integer pointer type for the target architecture's address space.
469// i32* for wasm32 and i64* for wasm64. With opaque pointers this is just a ptr
470// in address space zero.
472 return PointerType::getUnqual(M->getContext());
473}
474
475// Returns an integer whose type is the integer type for the target's address
476// space. Returns (i32 C) for wasm32 and (i64 C) for wasm64, when C is the
477// integer.
479 IRBuilder<> IRB(M->getContext());
480 return IRB.getIntN(M->getDataLayout().getPointerSizeInBits(), C);
481}
482
483// Returns __cxa_find_matching_catch_N function, where N = NumClauses + 2.
484// This is because a landingpad instruction contains two more arguments, a
485// personality function and a cleanup bit, and __cxa_find_matching_catch_N
486// functions are named after the number of arguments in the original landingpad
487// instruction.
488Function *
489WebAssemblyLowerEmscriptenEHSjLj::getFindMatchingCatch(Module &M,
490 unsigned NumClauses) {
491 if (FindMatchingCatches.count(NumClauses))
492 return FindMatchingCatches[NumClauses];
493 PointerType *Int8PtrTy = PointerType::getUnqual(M.getContext());
494 SmallVector<Type *, 16> Args(NumClauses, Int8PtrTy);
495 FunctionType *FTy = FunctionType::get(Int8PtrTy, Args, false);
497 FTy, "__cxa_find_matching_catch_" + Twine(NumClauses + 2), &M);
498 FindMatchingCatches[NumClauses] = F;
499 return F;
500}
501
502// Generate invoke wrapper seqence with preamble and postamble
503// Preamble:
504// __THREW__ = 0;
505// Postamble:
506// %__THREW__.val = __THREW__; __THREW__ = 0;
507// Returns %__THREW__.val, which indicates whether an exception is thrown (or
508// whether longjmp occurred), for future use.
509Value *WebAssemblyLowerEmscriptenEHSjLj::wrapInvoke(CallBase *CI) {
510 Module *M = CI->getModule();
511 LLVMContext &C = M->getContext();
512
513 IRBuilder<> IRB(C);
514 IRB.SetInsertPoint(CI);
515
516 // Pre-invoke
517 // __THREW__ = 0;
518 IRB.CreateStore(getAddrSizeInt(M, 0), ThrewGV);
519
520 // Invoke function wrapper in JavaScript
522 // Put the pointer to the callee as first argument, so it can be called
523 // within the invoke wrapper later
524 Args.push_back(CI->getCalledOperand());
525 Args.append(CI->arg_begin(), CI->arg_end());
526 CallInst *NewCall = IRB.CreateCall(getInvokeWrapper(CI), Args);
527 NewCall->takeName(CI);
529 NewCall->setDebugLoc(CI->getDebugLoc());
530
531 // Because we added the pointer to the callee as first argument, all
532 // argument attribute indices have to be incremented by one.
533 SmallVector<AttributeSet, 8> ArgAttributes;
534 const AttributeList &InvokeAL = CI->getAttributes();
535
536 // No attributes for the callee pointer.
537 ArgAttributes.push_back(AttributeSet());
538 // Copy the argument attributes from the original
539 for (unsigned I = 0, E = CI->arg_size(); I < E; ++I)
540 ArgAttributes.push_back(InvokeAL.getParamAttrs(I));
541
542 AttrBuilder FnAttrs(CI->getContext(), InvokeAL.getFnAttrs());
543 if (auto Args = FnAttrs.getAllocSizeArgs()) {
544 // The allocsize attribute (if any) referes to parameters by index and needs
545 // to be adjusted.
546 auto [SizeArg, NEltArg] = *Args;
547 SizeArg += 1;
548 if (NEltArg)
549 NEltArg = *NEltArg + 1;
550 FnAttrs.addAllocSizeAttr(SizeArg, NEltArg);
551 }
552 // In case the callee has 'noreturn' attribute, We need to remove it, because
553 // we expect invoke wrappers to return.
554 FnAttrs.removeAttribute(Attribute::NoReturn);
555
556 // Reconstruct the AttributesList based on the vector we constructed.
558 C, AttributeSet::get(C, FnAttrs), InvokeAL.getRetAttrs(), ArgAttributes);
559 NewCall->setAttributes(NewCallAL);
560
561 CI->replaceAllUsesWith(NewCall);
562
563 // Post-invoke
564 // %__THREW__.val = __THREW__; __THREW__ = 0;
565 Value *Threw =
566 IRB.CreateLoad(getAddrIntType(M), ThrewGV, ThrewGV->getName() + ".val");
567 IRB.CreateStore(getAddrSizeInt(M, 0), ThrewGV);
568 return Threw;
569}
570
571// Get matching invoke wrapper based on callee signature
572Function *WebAssemblyLowerEmscriptenEHSjLj::getInvokeWrapper(CallBase *CI) {
573 Module *M = CI->getModule();
575 FunctionType *CalleeFTy = CI->getFunctionType();
576
577 std::string Sig = getSignature(CalleeFTy);
578 auto It = InvokeWrappers.find(Sig);
579 if (It != InvokeWrappers.end())
580 return It->second;
581
582 // Put the pointer to the callee as first argument
583 ArgTys.push_back(PointerType::getUnqual(CalleeFTy));
584 // Add argument types
585 ArgTys.append(CalleeFTy->param_begin(), CalleeFTy->param_end());
586
587 FunctionType *FTy = FunctionType::get(CalleeFTy->getReturnType(), ArgTys,
588 CalleeFTy->isVarArg());
589 Function *F = getEmscriptenFunction(FTy, "__invoke_" + Sig, M);
590 InvokeWrappers[Sig] = F;
591 return F;
592}
593
594static bool canLongjmp(const Value *Callee) {
595 if (auto *CalleeF = dyn_cast<Function>(Callee))
596 if (CalleeF->isIntrinsic())
597 return false;
598
599 // Attempting to transform inline assembly will result in something like:
600 // call void @__invoke_void(void ()* asm ...)
601 // which is invalid because inline assembly blocks do not have addresses
602 // and can't be passed by pointer. The result is a crash with illegal IR.
603 if (isa<InlineAsm>(Callee))
604 return false;
605 StringRef CalleeName = Callee->getName();
606
607 // TODO Include more functions or consider checking with mangled prefixes
608
609 // The reason we include malloc/free here is to exclude the malloc/free
610 // calls generated in setjmp prep / cleanup routines.
611 if (CalleeName == "setjmp" || CalleeName == "malloc" || CalleeName == "free")
612 return false;
613
614 // There are functions in Emscripten's JS glue code or compiler-rt
615 if (CalleeName == "__resumeException" || CalleeName == "llvm_eh_typeid_for" ||
616 CalleeName == "__wasm_setjmp" || CalleeName == "__wasm_setjmp_test" ||
617 CalleeName == "getTempRet0" || CalleeName == "setTempRet0")
618 return false;
619
620 // __cxa_find_matching_catch_N functions cannot longjmp
621 if (Callee->getName().starts_with("__cxa_find_matching_catch_"))
622 return false;
623
624 // Exception-catching related functions
625 //
626 // We intentionally treat __cxa_end_catch longjmpable in Wasm SjLj even though
627 // it surely cannot longjmp, in order to maintain the unwind relationship from
628 // all existing catchpads (and calls within them) to catch.dispatch.longjmp.
629 //
630 // In Wasm EH + Wasm SjLj, we
631 // 1. Make all catchswitch and cleanuppad that unwind to caller unwind to
632 // catch.dispatch.longjmp instead
633 // 2. Convert all longjmpable calls to invokes that unwind to
634 // catch.dispatch.longjmp
635 // But catchswitch BBs are removed in isel, so if an EH catchswitch (generated
636 // from an exception)'s catchpad does not contain any calls that are converted
637 // into invokes unwinding to catch.dispatch.longjmp, this unwind relationship
638 // (EH catchswitch BB -> catch.dispatch.longjmp BB) is lost and
639 // catch.dispatch.longjmp BB can be placed before the EH catchswitch BB in
640 // CFGSort.
641 // int ret = setjmp(buf);
642 // try {
643 // foo(); // longjmps
644 // } catch (...) {
645 // }
646 // Then in this code, if 'foo' longjmps, it first unwinds to 'catch (...)'
647 // catchswitch, and is not caught by that catchswitch because it is a longjmp,
648 // then it should next unwind to catch.dispatch.longjmp BB. But if this 'catch
649 // (...)' catchswitch -> catch.dispatch.longjmp unwind relationship is lost,
650 // it will not unwind to catch.dispatch.longjmp, producing an incorrect
651 // result.
652 //
653 // Every catchpad generated by Wasm C++ contains __cxa_end_catch, so we
654 // intentionally treat it as longjmpable to work around this problem. This is
655 // a hacky fix but an easy one.
656 //
657 // The comment block in findWasmUnwindDestinations() in
658 // SelectionDAGBuilder.cpp is addressing a similar problem.
659 if (CalleeName == "__cxa_end_catch")
661 if (CalleeName == "__cxa_begin_catch" ||
662 CalleeName == "__cxa_allocate_exception" || CalleeName == "__cxa_throw" ||
663 CalleeName == "__clang_call_terminate")
664 return false;
665
666 // std::terminate, which is generated when another exception occurs while
667 // handling an exception, cannot longjmp.
668 if (CalleeName == "_ZSt9terminatev")
669 return false;
670
671 // Otherwise we don't know
672 return true;
673}
674
675static bool isEmAsmCall(const Value *Callee) {
676 StringRef CalleeName = Callee->getName();
677 // This is an exhaustive list from Emscripten's <emscripten/em_asm.h>.
678 return CalleeName == "emscripten_asm_const_int" ||
679 CalleeName == "emscripten_asm_const_double" ||
680 CalleeName == "emscripten_asm_const_int_sync_on_main_thread" ||
681 CalleeName == "emscripten_asm_const_double_sync_on_main_thread" ||
682 CalleeName == "emscripten_asm_const_async_on_main_thread";
683}
684
685// Generate __wasm_setjmp_test function call seqence with preamble and
686// postamble. The code this generates is equivalent to the following
687// JavaScript code:
688// %__threwValue.val = __threwValue;
689// if (%__THREW__.val != 0 & %__threwValue.val != 0) {
690// %label = __wasm_setjmp_test(%__THREW__.val, functionInvocationId);
691// if (%label == 0)
692// emscripten_longjmp(%__THREW__.val, %__threwValue.val);
693// setTempRet0(%__threwValue.val);
694// } else {
695// %label = -1;
696// }
697// %longjmp_result = getTempRet0();
698//
699// As output parameters. returns %label, %longjmp_result, and the BB the last
700// instruction (%longjmp_result = ...) is in.
701void WebAssemblyLowerEmscriptenEHSjLj::wrapTestSetjmp(
702 BasicBlock *BB, DebugLoc DL, Value *Threw, Value *FunctionInvocationId,
703 Value *&Label, Value *&LongjmpResult, BasicBlock *&CallEmLongjmpBB,
704 PHINode *&CallEmLongjmpBBThrewPHI, PHINode *&CallEmLongjmpBBThrewValuePHI,
705 BasicBlock *&EndBB) {
706 Function *F = BB->getParent();
707 Module *M = F->getParent();
708 LLVMContext &C = M->getContext();
709 IRBuilder<> IRB(C);
710 IRB.SetCurrentDebugLocation(DL);
711
712 // if (%__THREW__.val != 0 & %__threwValue.val != 0)
713 IRB.SetInsertPoint(BB);
714 BasicBlock *ThenBB1 = BasicBlock::Create(C, "if.then1", F);
715 BasicBlock *ElseBB1 = BasicBlock::Create(C, "if.else1", F);
716 BasicBlock *EndBB1 = BasicBlock::Create(C, "if.end", F);
717 Value *ThrewCmp = IRB.CreateICmpNE(Threw, getAddrSizeInt(M, 0));
718 Value *ThrewValue = IRB.CreateLoad(IRB.getInt32Ty(), ThrewValueGV,
719 ThrewValueGV->getName() + ".val");
720 Value *ThrewValueCmp = IRB.CreateICmpNE(ThrewValue, IRB.getInt32(0));
721 Value *Cmp1 = IRB.CreateAnd(ThrewCmp, ThrewValueCmp, "cmp1");
722 IRB.CreateCondBr(Cmp1, ThenBB1, ElseBB1);
723
724 // Generate call.em.longjmp BB once and share it within the function
725 if (!CallEmLongjmpBB) {
726 // emscripten_longjmp(%__THREW__.val, %__threwValue.val);
727 CallEmLongjmpBB = BasicBlock::Create(C, "call.em.longjmp", F);
728 IRB.SetInsertPoint(CallEmLongjmpBB);
729 CallEmLongjmpBBThrewPHI = IRB.CreatePHI(getAddrIntType(M), 4, "threw.phi");
730 CallEmLongjmpBBThrewValuePHI =
731 IRB.CreatePHI(IRB.getInt32Ty(), 4, "threwvalue.phi");
732 CallEmLongjmpBBThrewPHI->addIncoming(Threw, ThenBB1);
733 CallEmLongjmpBBThrewValuePHI->addIncoming(ThrewValue, ThenBB1);
734 IRB.CreateCall(EmLongjmpF,
735 {CallEmLongjmpBBThrewPHI, CallEmLongjmpBBThrewValuePHI});
736 IRB.CreateUnreachable();
737 } else {
738 CallEmLongjmpBBThrewPHI->addIncoming(Threw, ThenBB1);
739 CallEmLongjmpBBThrewValuePHI->addIncoming(ThrewValue, ThenBB1);
740 }
741
742 // %label = __wasm_setjmp_test(%__THREW__.val, functionInvocationId);
743 // if (%label == 0)
744 IRB.SetInsertPoint(ThenBB1);
745 BasicBlock *EndBB2 = BasicBlock::Create(C, "if.end2", F);
746 Value *ThrewPtr =
747 IRB.CreateIntToPtr(Threw, getAddrPtrType(M), Threw->getName() + ".p");
748 Value *ThenLabel = IRB.CreateCall(WasmSetjmpTestF,
749 {ThrewPtr, FunctionInvocationId}, "label");
750 Value *Cmp2 = IRB.CreateICmpEQ(ThenLabel, IRB.getInt32(0));
751 IRB.CreateCondBr(Cmp2, CallEmLongjmpBB, EndBB2);
752
753 // setTempRet0(%__threwValue.val);
754 IRB.SetInsertPoint(EndBB2);
755 IRB.CreateCall(SetTempRet0F, ThrewValue);
756 IRB.CreateBr(EndBB1);
757
758 IRB.SetInsertPoint(ElseBB1);
759 IRB.CreateBr(EndBB1);
760
761 // longjmp_result = getTempRet0();
762 IRB.SetInsertPoint(EndBB1);
763 PHINode *LabelPHI = IRB.CreatePHI(IRB.getInt32Ty(), 2, "label");
764 LabelPHI->addIncoming(ThenLabel, EndBB2);
765
766 LabelPHI->addIncoming(IRB.getInt32(-1), ElseBB1);
767
768 // Output parameter assignment
769 Label = LabelPHI;
770 EndBB = EndBB1;
771 LongjmpResult = IRB.CreateCall(GetTempRet0F, {}, "longjmp_result");
772}
773
774void WebAssemblyLowerEmscriptenEHSjLj::rebuildSSA(Function &F) {
775 DominatorTree &DT = getAnalysis<DominatorTreeWrapperPass>(F).getDomTree();
776 DT.recalculate(F); // CFG has been changed
777
779 for (BasicBlock &BB : F) {
780 for (Instruction &I : BB) {
781 if (I.getType()->isVoidTy())
782 continue;
783 unsigned VarID = SSA.AddVariable(I.getName(), I.getType());
784 // If a value is defined by an invoke instruction, it is only available in
785 // its normal destination and not in its unwind destination.
786 if (auto *II = dyn_cast<InvokeInst>(&I))
787 SSA.AddAvailableValue(VarID, II->getNormalDest(), II);
788 else
789 SSA.AddAvailableValue(VarID, &BB, &I);
790 for (auto &U : I.uses()) {
791 auto *User = cast<Instruction>(U.getUser());
792 if (auto *UserPN = dyn_cast<PHINode>(User))
793 if (UserPN->getIncomingBlock(U) == &BB)
794 continue;
795 if (DT.dominates(&I, User))
796 continue;
797 SSA.AddUse(VarID, &U);
798 }
799 }
800 }
801 SSA.RewriteAllUses(&DT);
802}
803
804// Replace uses of longjmp with a new longjmp function in Emscripten library.
805// In Emscripten SjLj, the new function is
806// void emscripten_longjmp(uintptr_t, i32)
807// In Wasm SjLj, the new function is
808// void __wasm_longjmp(i8*, i32)
809// Because the original libc longjmp function takes (jmp_buf*, i32), we need a
810// ptrtoint/bitcast instruction here to make the type match. jmp_buf* will
811// eventually be lowered to i32/i64 in the wasm backend.
812void WebAssemblyLowerEmscriptenEHSjLj::replaceLongjmpWith(Function *LongjmpF,
813 Function *NewF) {
814 assert(NewF == EmLongjmpF || NewF == WasmLongjmpF);
815 Module *M = LongjmpF->getParent();
817 LLVMContext &C = LongjmpF->getParent()->getContext();
818 IRBuilder<> IRB(C);
819
820 // For calls to longjmp, replace it with emscripten_longjmp/__wasm_longjmp and
821 // cast its first argument (jmp_buf*) appropriately
822 for (User *U : LongjmpF->users()) {
823 auto *CI = dyn_cast<CallInst>(U);
824 if (CI && CI->getCalledFunction() == LongjmpF) {
825 IRB.SetInsertPoint(CI);
826 Value *Env = nullptr;
827 if (NewF == EmLongjmpF)
828 Env =
829 IRB.CreatePtrToInt(CI->getArgOperand(0), getAddrIntType(M), "env");
830 else // WasmLongjmpF
831 Env = IRB.CreateBitCast(CI->getArgOperand(0), IRB.getPtrTy(), "env");
832 IRB.CreateCall(NewF, {Env, CI->getArgOperand(1)});
833 ToErase.push_back(CI);
834 }
835 }
836 for (auto *I : ToErase)
837 I->eraseFromParent();
838
839 // If we have any remaining uses of longjmp's function pointer, replace it
840 // with (void(*)(jmp_buf*, int))emscripten_longjmp / __wasm_longjmp.
841 if (!LongjmpF->uses().empty()) {
842 Value *NewLongjmp =
843 IRB.CreateBitCast(NewF, LongjmpF->getType(), "longjmp.cast");
844 LongjmpF->replaceAllUsesWith(NewLongjmp);
845 }
846}
847
849 for (const auto &BB : *F)
850 for (const auto &I : BB)
851 if (const auto *CB = dyn_cast<CallBase>(&I))
852 if (canLongjmp(CB->getCalledOperand()))
853 return true;
854 return false;
855}
856
857// When a function contains a setjmp call but not other calls that can longjmp,
858// we don't do setjmp transformation for that setjmp. But we need to convert the
859// setjmp calls into "i32 0" so they don't cause link time errors. setjmp always
860// returns 0 when called directly.
861static void nullifySetjmp(Function *F) {
862 Module &M = *F->getParent();
863 IRBuilder<> IRB(M.getContext());
864 Function *SetjmpF = M.getFunction("setjmp");
866
867 for (User *U : make_early_inc_range(SetjmpF->users())) {
868 auto *CB = cast<CallBase>(U);
869 BasicBlock *BB = CB->getParent();
870 if (BB->getParent() != F) // in other function
871 continue;
872 CallInst *CI = nullptr;
873 // setjmp cannot throw. So if it is an invoke, lower it to a call
874 if (auto *II = dyn_cast<InvokeInst>(CB))
876 else
877 CI = cast<CallInst>(CB);
878 ToErase.push_back(CI);
879 CI->replaceAllUsesWith(IRB.getInt32(0));
880 }
881 for (auto *I : ToErase)
882 I->eraseFromParent();
883}
884
885bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) {
886 LLVM_DEBUG(dbgs() << "********** Lower Emscripten EH & SjLj **********\n");
887
888 LLVMContext &C = M.getContext();
889 IRBuilder<> IRB(C);
890
891 Function *SetjmpF = M.getFunction("setjmp");
892 Function *LongjmpF = M.getFunction("longjmp");
893
894 // In some platforms _setjmp and _longjmp are used instead. Change these to
895 // use setjmp/longjmp instead, because we later detect these functions by
896 // their names.
897 Function *SetjmpF2 = M.getFunction("_setjmp");
898 Function *LongjmpF2 = M.getFunction("_longjmp");
899 if (SetjmpF2) {
900 if (SetjmpF) {
901 if (SetjmpF->getFunctionType() != SetjmpF2->getFunctionType())
902 report_fatal_error("setjmp and _setjmp have different function types");
903 } else {
904 SetjmpF = Function::Create(SetjmpF2->getFunctionType(),
905 GlobalValue::ExternalLinkage, "setjmp", M);
906 }
907 SetjmpF2->replaceAllUsesWith(SetjmpF);
908 }
909 if (LongjmpF2) {
910 if (LongjmpF) {
911 if (LongjmpF->getFunctionType() != LongjmpF2->getFunctionType())
913 "longjmp and _longjmp have different function types");
914 } else {
915 LongjmpF = Function::Create(LongjmpF2->getFunctionType(),
916 GlobalValue::ExternalLinkage, "setjmp", M);
917 }
918 LongjmpF2->replaceAllUsesWith(LongjmpF);
919 }
920
921 auto *TPC = getAnalysisIfAvailable<TargetPassConfig>();
922 assert(TPC && "Expected a TargetPassConfig");
923 auto &TM = TPC->getTM<WebAssemblyTargetMachine>();
924
925 // Declare (or get) global variables __THREW__, __threwValue, and
926 // getTempRet0/setTempRet0 function which are used in common for both
927 // exception handling and setjmp/longjmp handling
928 ThrewGV = getGlobalVariable(M, getAddrIntType(&M), TM, "__THREW__");
929 ThrewValueGV = getGlobalVariable(M, IRB.getInt32Ty(), TM, "__threwValue");
930 GetTempRet0F = getEmscriptenFunction(
931 FunctionType::get(IRB.getInt32Ty(), false), "getTempRet0", &M);
932 SetTempRet0F = getEmscriptenFunction(
933 FunctionType::get(IRB.getVoidTy(), IRB.getInt32Ty(), false),
934 "setTempRet0", &M);
935 GetTempRet0F->setDoesNotThrow();
936 SetTempRet0F->setDoesNotThrow();
937
938 bool Changed = false;
939
940 // Function registration for exception handling
941 if (EnableEmEH) {
942 // Register __resumeException function
943 FunctionType *ResumeFTy =
944 FunctionType::get(IRB.getVoidTy(), IRB.getPtrTy(), false);
945 ResumeF = getEmscriptenFunction(ResumeFTy, "__resumeException", &M);
946 ResumeF->addFnAttr(Attribute::NoReturn);
947
948 // Register llvm_eh_typeid_for function
949 FunctionType *EHTypeIDTy =
950 FunctionType::get(IRB.getInt32Ty(), IRB.getPtrTy(), false);
951 EHTypeIDF = getEmscriptenFunction(EHTypeIDTy, "llvm_eh_typeid_for", &M);
952 }
953
954 // Functions that contains calls to setjmp but don't have other longjmpable
955 // calls within them.
956 SmallPtrSet<Function *, 4> SetjmpUsersToNullify;
957
958 if ((EnableEmSjLj || EnableWasmSjLj) && SetjmpF) {
959 // Precompute setjmp users
960 for (User *U : SetjmpF->users()) {
961 if (auto *CB = dyn_cast<CallBase>(U)) {
962 auto *UserF = CB->getFunction();
963 // If a function that calls setjmp does not contain any other calls that
964 // can longjmp, we don't need to do any transformation on that function,
965 // so can ignore it
966 if (containsLongjmpableCalls(UserF))
967 SetjmpUsers.insert(UserF);
968 else
969 SetjmpUsersToNullify.insert(UserF);
970 } else {
971 std::string S;
973 SS << *U;
974 report_fatal_error(Twine("Indirect use of setjmp is not supported: ") +
975 SS.str());
976 }
977 }
978 }
979
980 bool SetjmpUsed = SetjmpF && !SetjmpUsers.empty();
981 bool LongjmpUsed = LongjmpF && !LongjmpF->use_empty();
982 DoSjLj = (EnableEmSjLj | EnableWasmSjLj) && (SetjmpUsed || LongjmpUsed);
983
984 // Function registration and data pre-gathering for setjmp/longjmp handling
985 if (DoSjLj) {
986 assert(EnableEmSjLj || EnableWasmSjLj);
987 if (EnableEmSjLj) {
988 // Register emscripten_longjmp function
989 FunctionType *FTy = FunctionType::get(
990 IRB.getVoidTy(), {getAddrIntType(&M), IRB.getInt32Ty()}, false);
991 EmLongjmpF = getEmscriptenFunction(FTy, "emscripten_longjmp", &M);
992 EmLongjmpF->addFnAttr(Attribute::NoReturn);
993 } else { // EnableWasmSjLj
994 Type *Int8PtrTy = IRB.getPtrTy();
995 // Register __wasm_longjmp function, which calls __builtin_wasm_longjmp.
996 FunctionType *FTy = FunctionType::get(
997 IRB.getVoidTy(), {Int8PtrTy, IRB.getInt32Ty()}, false);
998 WasmLongjmpF = getEmscriptenFunction(FTy, "__wasm_longjmp", &M);
999 WasmLongjmpF->addFnAttr(Attribute::NoReturn);
1000 }
1001
1002 if (SetjmpF) {
1003 Type *Int8PtrTy = IRB.getPtrTy();
1004 Type *Int32PtrTy = IRB.getPtrTy();
1005 Type *Int32Ty = IRB.getInt32Ty();
1006
1007 // Register __wasm_setjmp function
1008 FunctionType *SetjmpFTy = SetjmpF->getFunctionType();
1009 FunctionType *FTy = FunctionType::get(
1010 IRB.getVoidTy(), {SetjmpFTy->getParamType(0), Int32Ty, Int32PtrTy},
1011 false);
1012 WasmSetjmpF = getEmscriptenFunction(FTy, "__wasm_setjmp", &M);
1013
1014 // Register __wasm_setjmp_test function
1015 FTy = FunctionType::get(Int32Ty, {Int32PtrTy, Int32PtrTy}, false);
1016 WasmSetjmpTestF = getEmscriptenFunction(FTy, "__wasm_setjmp_test", &M);
1017
1018 // wasm.catch() will be lowered down to wasm 'catch' instruction in
1019 // instruction selection.
1020 CatchF = Intrinsic::getOrInsertDeclaration(&M, Intrinsic::wasm_catch);
1021 // Type for struct __WasmLongjmpArgs
1022 LongjmpArgsTy = StructType::get(Int8PtrTy, // env
1023 Int32Ty // val
1024 );
1025 }
1026 }
1027
1028 // Exception handling transformation
1029 if (EnableEmEH) {
1030 for (Function &F : M) {
1031 if (F.isDeclaration())
1032 continue;
1033 Changed |= runEHOnFunction(F);
1034 }
1035 }
1036
1037 // Setjmp/longjmp handling transformation
1038 if (DoSjLj) {
1039 Changed = true; // We have setjmp or longjmp somewhere
1040 if (LongjmpF)
1041 replaceLongjmpWith(LongjmpF, EnableEmSjLj ? EmLongjmpF : WasmLongjmpF);
1042 // Only traverse functions that uses setjmp in order not to insert
1043 // unnecessary prep / cleanup code in every function
1044 if (SetjmpF)
1045 for (Function *F : SetjmpUsers)
1046 runSjLjOnFunction(*F);
1047 }
1048
1049 // Replace unnecessary setjmp calls with 0
1050 if ((EnableEmSjLj || EnableWasmSjLj) && !SetjmpUsersToNullify.empty()) {
1051 Changed = true;
1052 assert(SetjmpF);
1053 for (Function *F : SetjmpUsersToNullify)
1055 }
1056
1057 // Delete unused global variables and functions
1058 for (auto *V : {ThrewGV, ThrewValueGV})
1059 if (V && V->use_empty())
1060 V->eraseFromParent();
1061 for (auto *V : {GetTempRet0F, SetTempRet0F, ResumeF, EHTypeIDF, EmLongjmpF,
1062 WasmSetjmpF, WasmSetjmpTestF, WasmLongjmpF, CatchF})
1063 if (V && V->use_empty())
1064 V->eraseFromParent();
1065
1066 return Changed;
1067}
1068
1069bool WebAssemblyLowerEmscriptenEHSjLj::runEHOnFunction(Function &F) {
1070 Module &M = *F.getParent();
1071 LLVMContext &C = F.getContext();
1072 IRBuilder<> IRB(C);
1073 bool Changed = false;
1076
1077 // rethrow.longjmp BB that will be shared within the function.
1078 BasicBlock *RethrowLongjmpBB = nullptr;
1079 // PHI node for the loaded value of __THREW__ global variable in
1080 // rethrow.longjmp BB
1081 PHINode *RethrowLongjmpBBThrewPHI = nullptr;
1082
1083 for (BasicBlock &BB : F) {
1084 auto *II = dyn_cast<InvokeInst>(BB.getTerminator());
1085 if (!II)
1086 continue;
1087 Changed = true;
1088 LandingPads.insert(II->getLandingPadInst());
1089 IRB.SetInsertPoint(II);
1090
1091 const Value *Callee = II->getCalledOperand();
1092 bool NeedInvoke = supportsException(&F) && canThrow(Callee);
1093 if (NeedInvoke) {
1094 // Wrap invoke with invoke wrapper and generate preamble/postamble
1095 Value *Threw = wrapInvoke(II);
1096 ToErase.push_back(II);
1097
1098 // If setjmp/longjmp handling is enabled, the thrown value can be not an
1099 // exception but a longjmp. If the current function contains calls to
1100 // setjmp, it will be appropriately handled in runSjLjOnFunction. But even
1101 // if the function does not contain setjmp calls, we shouldn't silently
1102 // ignore longjmps; we should rethrow them so they can be correctly
1103 // handled in somewhere up the call chain where setjmp is. __THREW__'s
1104 // value is 0 when nothing happened, 1 when an exception is thrown, and
1105 // other values when longjmp is thrown.
1106 //
1107 // if (%__THREW__.val == 0 || %__THREW__.val == 1)
1108 // goto %tail
1109 // else
1110 // goto %longjmp.rethrow
1111 //
1112 // rethrow.longjmp: ;; This is longjmp. Rethrow it
1113 // %__threwValue.val = __threwValue
1114 // emscripten_longjmp(%__THREW__.val, %__threwValue.val);
1115 //
1116 // tail: ;; Nothing happened or an exception is thrown
1117 // ... Continue exception handling ...
1118 if (DoSjLj && EnableEmSjLj && !SetjmpUsers.count(&F) &&
1119 canLongjmp(Callee)) {
1120 // Create longjmp.rethrow BB once and share it within the function
1121 if (!RethrowLongjmpBB) {
1122 RethrowLongjmpBB = BasicBlock::Create(C, "rethrow.longjmp", &F);
1123 IRB.SetInsertPoint(RethrowLongjmpBB);
1124 RethrowLongjmpBBThrewPHI =
1125 IRB.CreatePHI(getAddrIntType(&M), 4, "threw.phi");
1126 RethrowLongjmpBBThrewPHI->addIncoming(Threw, &BB);
1127 Value *ThrewValue = IRB.CreateLoad(IRB.getInt32Ty(), ThrewValueGV,
1128 ThrewValueGV->getName() + ".val");
1129 IRB.CreateCall(EmLongjmpF, {RethrowLongjmpBBThrewPHI, ThrewValue});
1130 IRB.CreateUnreachable();
1131 } else {
1132 RethrowLongjmpBBThrewPHI->addIncoming(Threw, &BB);
1133 }
1134
1135 IRB.SetInsertPoint(II); // Restore the insert point back
1136 BasicBlock *Tail = BasicBlock::Create(C, "tail", &F);
1137 Value *CmpEqOne =
1138 IRB.CreateICmpEQ(Threw, getAddrSizeInt(&M, 1), "cmp.eq.one");
1139 Value *CmpEqZero =
1140 IRB.CreateICmpEQ(Threw, getAddrSizeInt(&M, 0), "cmp.eq.zero");
1141 Value *Or = IRB.CreateOr(CmpEqZero, CmpEqOne, "or");
1142 IRB.CreateCondBr(Or, Tail, RethrowLongjmpBB);
1143 IRB.SetInsertPoint(Tail);
1144 BB.replaceSuccessorsPhiUsesWith(&BB, Tail);
1145 }
1146
1147 // Insert a branch based on __THREW__ variable
1148 Value *Cmp = IRB.CreateICmpEQ(Threw, getAddrSizeInt(&M, 1), "cmp");
1149 IRB.CreateCondBr(Cmp, II->getUnwindDest(), II->getNormalDest());
1150
1151 } else {
1152 // This can't throw, and we don't need this invoke, just replace it with a
1153 // call+branch
1155 }
1156 }
1157
1158 // Process resume instructions
1159 for (BasicBlock &BB : F) {
1160 // Scan the body of the basic block for resumes
1161 for (Instruction &I : BB) {
1162 auto *RI = dyn_cast<ResumeInst>(&I);
1163 if (!RI)
1164 continue;
1165 Changed = true;
1166
1167 // Split the input into legal values
1168 Value *Input = RI->getValue();
1169 IRB.SetInsertPoint(RI);
1170 Value *Low = IRB.CreateExtractValue(Input, 0, "low");
1171 // Create a call to __resumeException function
1172 IRB.CreateCall(ResumeF, {Low});
1173 // Add a terminator to the block
1174 IRB.CreateUnreachable();
1175 ToErase.push_back(RI);
1176 }
1177 }
1178
1179 // Process llvm.eh.typeid.for intrinsics
1180 for (BasicBlock &BB : F) {
1181 for (Instruction &I : BB) {
1182 auto *CI = dyn_cast<CallInst>(&I);
1183 if (!CI)
1184 continue;
1185 const Function *Callee = CI->getCalledFunction();
1186 if (!Callee)
1187 continue;
1188 if (Callee->getIntrinsicID() != Intrinsic::eh_typeid_for)
1189 continue;
1190 Changed = true;
1191
1192 IRB.SetInsertPoint(CI);
1193 CallInst *NewCI =
1194 IRB.CreateCall(EHTypeIDF, CI->getArgOperand(0), "typeid");
1195 CI->replaceAllUsesWith(NewCI);
1196 ToErase.push_back(CI);
1197 }
1198 }
1199
1200 // Look for orphan landingpads, can occur in blocks with no predecessors
1201 for (BasicBlock &BB : F) {
1202 Instruction *I = BB.getFirstNonPHI();
1203 if (auto *LPI = dyn_cast<LandingPadInst>(I))
1204 LandingPads.insert(LPI);
1205 }
1206 Changed |= !LandingPads.empty();
1207
1208 // Handle all the landingpad for this function together, as multiple invokes
1209 // may share a single lp
1210 for (LandingPadInst *LPI : LandingPads) {
1211 IRB.SetInsertPoint(LPI);
1213 for (unsigned I = 0, E = LPI->getNumClauses(); I < E; ++I) {
1214 Constant *Clause = LPI->getClause(I);
1215 // TODO Handle filters (= exception specifications).
1216 // https://github.com/llvm/llvm-project/issues/49740
1217 if (LPI->isCatch(I))
1218 FMCArgs.push_back(Clause);
1219 }
1220
1221 // Create a call to __cxa_find_matching_catch_N function
1222 Function *FMCF = getFindMatchingCatch(M, FMCArgs.size());
1223 CallInst *FMCI = IRB.CreateCall(FMCF, FMCArgs, "fmc");
1224 Value *Poison = PoisonValue::get(LPI->getType());
1225 Value *Pair0 = IRB.CreateInsertValue(Poison, FMCI, 0, "pair0");
1226 Value *TempRet0 = IRB.CreateCall(GetTempRet0F, {}, "tempret0");
1227 Value *Pair1 = IRB.CreateInsertValue(Pair0, TempRet0, 1, "pair1");
1228
1229 LPI->replaceAllUsesWith(Pair1);
1230 ToErase.push_back(LPI);
1231 }
1232
1233 // Erase everything we no longer need in this function
1234 for (Instruction *I : ToErase)
1235 I->eraseFromParent();
1236
1237 return Changed;
1238}
1239
1240// This tries to get debug info from the instruction before which a new
1241// instruction will be inserted, and if there's no debug info in that
1242// instruction, tries to get the info instead from the previous instruction (if
1243// any). If none of these has debug info and a DISubprogram is provided, it
1244// creates a dummy debug info with the first line of the function, because IR
1245// verifier requires all inlinable callsites should have debug info when both a
1246// caller and callee have DISubprogram. If none of these conditions are met,
1247// returns empty info.
1248static DebugLoc getOrCreateDebugLoc(const Instruction *InsertBefore,
1249 DISubprogram *SP) {
1250 assert(InsertBefore);
1251 if (InsertBefore->getDebugLoc())
1252 return InsertBefore->getDebugLoc();
1253 const Instruction *Prev = InsertBefore->getPrevNode();
1254 if (Prev && Prev->getDebugLoc())
1255 return Prev->getDebugLoc();
1256 if (SP)
1257 return DILocation::get(SP->getContext(), SP->getLine(), 1, SP);
1258 return DebugLoc();
1259}
1260
1261bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) {
1262 assert(EnableEmSjLj || EnableWasmSjLj);
1263 Module &M = *F.getParent();
1264 LLVMContext &C = F.getContext();
1265 IRBuilder<> IRB(C);
1267
1268 // Setjmp preparation
1269
1270 BasicBlock *Entry = &F.getEntryBlock();
1271 DebugLoc FirstDL = getOrCreateDebugLoc(&*Entry->begin(), F.getSubprogram());
1272 SplitBlock(Entry, &*Entry->getFirstInsertionPt());
1273
1274 IRB.SetInsertPoint(Entry->getTerminator()->getIterator());
1275 // This alloca'ed pointer is used by the runtime to identify function
1276 // invocations. It's just for pointer comparisons. It will never be
1277 // dereferenced.
1278 Instruction *FunctionInvocationId =
1279 IRB.CreateAlloca(IRB.getInt32Ty(), nullptr, "functionInvocationId");
1280 FunctionInvocationId->setDebugLoc(FirstDL);
1281
1282 // Setjmp transformation
1283 SmallVector<PHINode *, 4> SetjmpRetPHIs;
1284 Function *SetjmpF = M.getFunction("setjmp");
1285 for (auto *U : make_early_inc_range(SetjmpF->users())) {
1286 auto *CB = cast<CallBase>(U);
1287 BasicBlock *BB = CB->getParent();
1288 if (BB->getParent() != &F) // in other function
1289 continue;
1290 if (CB->getOperandBundle(LLVMContext::OB_funclet)) {
1291 std::string S;
1293 SS << "In function " + F.getName() +
1294 ": setjmp within a catch clause is not supported in Wasm EH:\n";
1295 SS << *CB;
1297 }
1298
1299 CallInst *CI = nullptr;
1300 // setjmp cannot throw. So if it is an invoke, lower it to a call
1301 if (auto *II = dyn_cast<InvokeInst>(CB))
1302 CI = llvm::changeToCall(II);
1303 else
1304 CI = cast<CallInst>(CB);
1305
1306 // The tail is everything right after the call, and will be reached once
1307 // when setjmp is called, and later when longjmp returns to the setjmp
1308 BasicBlock *Tail = SplitBlock(BB, CI->getNextNode());
1309 // Add a phi to the tail, which will be the output of setjmp, which
1310 // indicates if this is the first call or a longjmp back. The phi directly
1311 // uses the right value based on where we arrive from
1312 IRB.SetInsertPoint(Tail, Tail->getFirstNonPHIIt());
1313 PHINode *SetjmpRet = IRB.CreatePHI(IRB.getInt32Ty(), 2, "setjmp.ret");
1314
1315 // setjmp initial call returns 0
1316 SetjmpRet->addIncoming(IRB.getInt32(0), BB);
1317 // The proper output is now this, not the setjmp call itself
1318 CI->replaceAllUsesWith(SetjmpRet);
1319 // longjmp returns to the setjmp will add themselves to this phi
1320 SetjmpRetPHIs.push_back(SetjmpRet);
1321
1322 // Fix call target
1323 // Our index in the function is our place in the array + 1 to avoid index
1324 // 0, because index 0 means the longjmp is not ours to handle.
1325 IRB.SetInsertPoint(CI);
1326 Value *Args[] = {CI->getArgOperand(0), IRB.getInt32(SetjmpRetPHIs.size()),
1327 FunctionInvocationId};
1328 IRB.CreateCall(WasmSetjmpF, Args);
1329 ToErase.push_back(CI);
1330 }
1331
1332 // Handle longjmpable calls.
1333 if (EnableEmSjLj)
1334 handleLongjmpableCallsForEmscriptenSjLj(F, FunctionInvocationId,
1335 SetjmpRetPHIs);
1336 else // EnableWasmSjLj
1337 handleLongjmpableCallsForWasmSjLj(F, FunctionInvocationId, SetjmpRetPHIs);
1338
1339 // Erase everything we no longer need in this function
1340 for (Instruction *I : ToErase)
1341 I->eraseFromParent();
1342
1343 // Finally, our modifications to the cfg can break dominance of SSA variables.
1344 // For example, in this code,
1345 // if (x()) { .. setjmp() .. }
1346 // if (y()) { .. longjmp() .. }
1347 // We must split the longjmp block, and it can jump into the block splitted
1348 // from setjmp one. But that means that when we split the setjmp block, it's
1349 // first part no longer dominates its second part - there is a theoretically
1350 // possible control flow path where x() is false, then y() is true and we
1351 // reach the second part of the setjmp block, without ever reaching the first
1352 // part. So, we rebuild SSA form here.
1353 rebuildSSA(F);
1354 return true;
1355}
1356
1357// Update each call that can longjmp so it can return to the corresponding
1358// setjmp. Refer to 4) of "Emscripten setjmp/longjmp handling" section in the
1359// comments at top of the file for details.
1360void WebAssemblyLowerEmscriptenEHSjLj::handleLongjmpableCallsForEmscriptenSjLj(
1361 Function &F, Instruction *FunctionInvocationId,
1362 SmallVectorImpl<PHINode *> &SetjmpRetPHIs) {
1363 Module &M = *F.getParent();
1364 LLVMContext &C = F.getContext();
1365 IRBuilder<> IRB(C);
1367
1368 // call.em.longjmp BB that will be shared within the function.
1369 BasicBlock *CallEmLongjmpBB = nullptr;
1370 // PHI node for the loaded value of __THREW__ global variable in
1371 // call.em.longjmp BB
1372 PHINode *CallEmLongjmpBBThrewPHI = nullptr;
1373 // PHI node for the loaded value of __threwValue global variable in
1374 // call.em.longjmp BB
1375 PHINode *CallEmLongjmpBBThrewValuePHI = nullptr;
1376 // rethrow.exn BB that will be shared within the function.
1377 BasicBlock *RethrowExnBB = nullptr;
1378
1379 // Because we are creating new BBs while processing and don't want to make
1380 // all these newly created BBs candidates again for longjmp processing, we
1381 // first make the vector of candidate BBs.
1382 std::vector<BasicBlock *> BBs;
1383 for (BasicBlock &BB : F)
1384 BBs.push_back(&BB);
1385
1386 // BBs.size() will change within the loop, so we query it every time
1387 for (unsigned I = 0; I < BBs.size(); I++) {
1388 BasicBlock *BB = BBs[I];
1389 for (Instruction &I : *BB) {
1390 if (isa<InvokeInst>(&I)) {
1391 std::string S;
1393 SS << "In function " << F.getName()
1394 << ": When using Wasm EH with Emscripten SjLj, there is a "
1395 "restriction that `setjmp` function call and exception cannot be "
1396 "used within the same function:\n";
1397 SS << I;
1399 }
1400 auto *CI = dyn_cast<CallInst>(&I);
1401 if (!CI)
1402 continue;
1403
1404 const Value *Callee = CI->getCalledOperand();
1405 if (!canLongjmp(Callee))
1406 continue;
1407 if (isEmAsmCall(Callee))
1408 report_fatal_error("Cannot use EM_ASM* alongside setjmp/longjmp in " +
1409 F.getName() +
1410 ". Please consider using EM_JS, or move the "
1411 "EM_ASM into another function.",
1412 false);
1413
1414 Value *Threw = nullptr;
1416 if (Callee->getName().starts_with("__invoke_")) {
1417 // If invoke wrapper has already been generated for this call in
1418 // previous EH phase, search for the load instruction
1419 // %__THREW__.val = __THREW__;
1420 // in postamble after the invoke wrapper call
1421 LoadInst *ThrewLI = nullptr;
1422 StoreInst *ThrewResetSI = nullptr;
1423 for (auto I = std::next(BasicBlock::iterator(CI)), IE = BB->end();
1424 I != IE; ++I) {
1425 if (auto *LI = dyn_cast<LoadInst>(I))
1426 if (auto *GV = dyn_cast<GlobalVariable>(LI->getPointerOperand()))
1427 if (GV == ThrewGV) {
1428 Threw = ThrewLI = LI;
1429 break;
1430 }
1431 }
1432 // Search for the store instruction after the load above
1433 // __THREW__ = 0;
1434 for (auto I = std::next(BasicBlock::iterator(ThrewLI)), IE = BB->end();
1435 I != IE; ++I) {
1436 if (auto *SI = dyn_cast<StoreInst>(I)) {
1437 if (auto *GV = dyn_cast<GlobalVariable>(SI->getPointerOperand())) {
1438 if (GV == ThrewGV &&
1439 SI->getValueOperand() == getAddrSizeInt(&M, 0)) {
1440 ThrewResetSI = SI;
1441 break;
1442 }
1443 }
1444 }
1445 }
1446 assert(Threw && ThrewLI && "Cannot find __THREW__ load after invoke");
1447 assert(ThrewResetSI && "Cannot find __THREW__ store after invoke");
1448 Tail = SplitBlock(BB, ThrewResetSI->getNextNode());
1449
1450 } else {
1451 // Wrap call with invoke wrapper and generate preamble/postamble
1452 Threw = wrapInvoke(CI);
1453 ToErase.push_back(CI);
1454 Tail = SplitBlock(BB, CI->getNextNode());
1455
1456 // If exception handling is enabled, the thrown value can be not a
1457 // longjmp but an exception, in which case we shouldn't silently ignore
1458 // exceptions; we should rethrow them.
1459 // __THREW__'s value is 0 when nothing happened, 1 when an exception is
1460 // thrown, other values when longjmp is thrown.
1461 //
1462 // if (%__THREW__.val == 1)
1463 // goto %eh.rethrow
1464 // else
1465 // goto %normal
1466 //
1467 // eh.rethrow: ;; Rethrow exception
1468 // %exn = call @__cxa_find_matching_catch_2() ;; Retrieve thrown ptr
1469 // __resumeException(%exn)
1470 //
1471 // normal:
1472 // <-- Insertion point. Will insert sjlj handling code from here
1473 // goto %tail
1474 //
1475 // tail:
1476 // ...
1477 if (supportsException(&F) && canThrow(Callee)) {
1478 // We will add a new conditional branch. So remove the branch created
1479 // when we split the BB
1480 ToErase.push_back(BB->getTerminator());
1481
1482 // Generate rethrow.exn BB once and share it within the function
1483 if (!RethrowExnBB) {
1484 RethrowExnBB = BasicBlock::Create(C, "rethrow.exn", &F);
1485 IRB.SetInsertPoint(RethrowExnBB);
1486 CallInst *Exn =
1487 IRB.CreateCall(getFindMatchingCatch(M, 0), {}, "exn");
1488 IRB.CreateCall(ResumeF, {Exn});
1489 IRB.CreateUnreachable();
1490 }
1491
1492 IRB.SetInsertPoint(CI);
1493 BasicBlock *NormalBB = BasicBlock::Create(C, "normal", &F);
1494 Value *CmpEqOne =
1495 IRB.CreateICmpEQ(Threw, getAddrSizeInt(&M, 1), "cmp.eq.one");
1496 IRB.CreateCondBr(CmpEqOne, RethrowExnBB, NormalBB);
1497
1498 IRB.SetInsertPoint(NormalBB);
1499 IRB.CreateBr(Tail);
1500 BB = NormalBB; // New insertion point to insert __wasm_setjmp_test()
1501 }
1502 }
1503
1504 // We need to replace the terminator in Tail - SplitBlock makes BB go
1505 // straight to Tail, we need to check if a longjmp occurred, and go to the
1506 // right setjmp-tail if so
1507 ToErase.push_back(BB->getTerminator());
1508
1509 // Generate a function call to __wasm_setjmp_test function and
1510 // preamble/postamble code to figure out (1) whether longjmp
1511 // occurred (2) if longjmp occurred, which setjmp it corresponds to
1512 Value *Label = nullptr;
1513 Value *LongjmpResult = nullptr;
1514 BasicBlock *EndBB = nullptr;
1515 wrapTestSetjmp(BB, CI->getDebugLoc(), Threw, FunctionInvocationId, Label,
1516 LongjmpResult, CallEmLongjmpBB, CallEmLongjmpBBThrewPHI,
1517 CallEmLongjmpBBThrewValuePHI, EndBB);
1518 assert(Label && LongjmpResult && EndBB);
1519
1520 // Create switch instruction
1521 IRB.SetInsertPoint(EndBB);
1522 IRB.SetCurrentDebugLocation(EndBB->back().getDebugLoc());
1523 SwitchInst *SI = IRB.CreateSwitch(Label, Tail, SetjmpRetPHIs.size());
1524 // -1 means no longjmp happened, continue normally (will hit the default
1525 // switch case). 0 means a longjmp that is not ours to handle, needs a
1526 // rethrow. Otherwise the index is the same as the index in P+1 (to avoid
1527 // 0).
1528 for (unsigned I = 0; I < SetjmpRetPHIs.size(); I++) {
1529 SI->addCase(IRB.getInt32(I + 1), SetjmpRetPHIs[I]->getParent());
1530 SetjmpRetPHIs[I]->addIncoming(LongjmpResult, EndBB);
1531 }
1532
1533 // We are splitting the block here, and must continue to find other calls
1534 // in the block - which is now split. so continue to traverse in the Tail
1535 BBs.push_back(Tail);
1536 }
1537 }
1538
1539 for (Instruction *I : ToErase)
1540 I->eraseFromParent();
1541}
1542
1544 for (const User *U : CPI->users())
1545 if (const auto *CRI = dyn_cast<CleanupReturnInst>(U))
1546 return CRI->getUnwindDest();
1547 return nullptr;
1548}
1549
1550// Create a catchpad in which we catch a longjmp's env and val arguments, test
1551// if the longjmp corresponds to one of setjmps in the current function, and if
1552// so, jump to the setjmp dispatch BB from which we go to one of post-setjmp
1553// BBs. Refer to 4) of "Wasm setjmp/longjmp handling" section in the comments at
1554// top of the file for details.
1555void WebAssemblyLowerEmscriptenEHSjLj::handleLongjmpableCallsForWasmSjLj(
1556 Function &F, Instruction *FunctionInvocationId,
1557 SmallVectorImpl<PHINode *> &SetjmpRetPHIs) {
1558 Module &M = *F.getParent();
1559 LLVMContext &C = F.getContext();
1560 IRBuilder<> IRB(C);
1561
1562 // A function with catchswitch/catchpad instruction should have a personality
1563 // function attached to it. Search for the wasm personality function, and if
1564 // it exists, use it, and if it doesn't, create a dummy personality function.
1565 // (SjLj is not going to call it anyway.)
1566 if (!F.hasPersonalityFn()) {
1567 StringRef PersName = getEHPersonalityName(EHPersonality::Wasm_CXX);
1568 FunctionType *PersType =
1569 FunctionType::get(IRB.getInt32Ty(), /* isVarArg */ true);
1570 Value *PersF = M.getOrInsertFunction(PersName, PersType).getCallee();
1571 F.setPersonalityFn(
1572 cast<Constant>(IRB.CreateBitCast(PersF, IRB.getPtrTy())));
1573 }
1574
1575 // Use the entry BB's debugloc as a fallback
1576 BasicBlock *Entry = &F.getEntryBlock();
1577 DebugLoc FirstDL = getOrCreateDebugLoc(&*Entry->begin(), F.getSubprogram());
1578 IRB.SetCurrentDebugLocation(FirstDL);
1579
1580 // Add setjmp.dispatch BB right after the entry block. Because we have
1581 // initialized functionInvocationId in the entry block and split the
1582 // rest into another BB, here 'OrigEntry' is the function's original entry
1583 // block before the transformation.
1584 //
1585 // entry:
1586 // functionInvocationId initialization
1587 // setjmp.dispatch:
1588 // switch will be inserted here later
1589 // entry.split: (OrigEntry)
1590 // the original function starts here
1591 BasicBlock *OrigEntry = Entry->getNextNode();
1592 BasicBlock *SetjmpDispatchBB =
1593 BasicBlock::Create(C, "setjmp.dispatch", &F, OrigEntry);
1594 cast<BranchInst>(Entry->getTerminator())->setSuccessor(0, SetjmpDispatchBB);
1595
1596 // Create catch.dispatch.longjmp BB and a catchswitch instruction
1597 BasicBlock *CatchDispatchLongjmpBB =
1598 BasicBlock::Create(C, "catch.dispatch.longjmp", &F);
1599 IRB.SetInsertPoint(CatchDispatchLongjmpBB);
1600 CatchSwitchInst *CatchSwitchLongjmp =
1601 IRB.CreateCatchSwitch(ConstantTokenNone::get(C), nullptr, 1);
1602
1603 // Create catch.longjmp BB and a catchpad instruction
1604 BasicBlock *CatchLongjmpBB = BasicBlock::Create(C, "catch.longjmp", &F);
1605 CatchSwitchLongjmp->addHandler(CatchLongjmpBB);
1606 IRB.SetInsertPoint(CatchLongjmpBB);
1607 CatchPadInst *CatchPad = IRB.CreateCatchPad(CatchSwitchLongjmp, {});
1608
1609 // Wasm throw and catch instructions can throw and catch multiple values, but
1610 // that requires multivalue support in the toolchain, which is currently not
1611 // very reliable. We instead throw and catch a pointer to a struct value of
1612 // type 'struct __WasmLongjmpArgs', which is defined in Emscripten.
1613 Instruction *LongjmpArgs =
1614 IRB.CreateCall(CatchF, {IRB.getInt32(WebAssembly::C_LONGJMP)}, "thrown");
1615 Value *EnvField =
1616 IRB.CreateConstGEP2_32(LongjmpArgsTy, LongjmpArgs, 0, 0, "env_gep");
1617 Value *ValField =
1618 IRB.CreateConstGEP2_32(LongjmpArgsTy, LongjmpArgs, 0, 1, "val_gep");
1619 // void *env = __wasm_longjmp_args.env;
1620 Instruction *Env = IRB.CreateLoad(IRB.getPtrTy(), EnvField, "env");
1621 // int val = __wasm_longjmp_args.val;
1622 Instruction *Val = IRB.CreateLoad(IRB.getInt32Ty(), ValField, "val");
1623
1624 // %label = __wasm_setjmp_test(%env, functionInvocatinoId);
1625 // if (%label == 0)
1626 // __wasm_longjmp(%env, %val)
1627 // catchret to %setjmp.dispatch
1628 BasicBlock *ThenBB = BasicBlock::Create(C, "if.then", &F);
1629 BasicBlock *EndBB = BasicBlock::Create(C, "if.end", &F);
1630 Value *EnvP = IRB.CreateBitCast(Env, getAddrPtrType(&M), "env.p");
1631 Value *Label = IRB.CreateCall(WasmSetjmpTestF, {EnvP, FunctionInvocationId},
1632 OperandBundleDef("funclet", CatchPad), "label");
1633 Value *Cmp = IRB.CreateICmpEQ(Label, IRB.getInt32(0));
1634 IRB.CreateCondBr(Cmp, ThenBB, EndBB);
1635
1636 IRB.SetInsertPoint(ThenBB);
1637 CallInst *WasmLongjmpCI = IRB.CreateCall(
1638 WasmLongjmpF, {Env, Val}, OperandBundleDef("funclet", CatchPad));
1639 IRB.CreateUnreachable();
1640
1641 IRB.SetInsertPoint(EndBB);
1642 // Jump to setjmp.dispatch block
1643 IRB.CreateCatchRet(CatchPad, SetjmpDispatchBB);
1644
1645 // Go back to setjmp.dispatch BB
1646 // setjmp.dispatch:
1647 // switch %label {
1648 // label 1: goto post-setjmp BB 1
1649 // label 2: goto post-setjmp BB 2
1650 // ...
1651 // default: goto splitted next BB
1652 // }
1653 IRB.SetInsertPoint(SetjmpDispatchBB);
1654 PHINode *LabelPHI = IRB.CreatePHI(IRB.getInt32Ty(), 2, "label.phi");
1655 LabelPHI->addIncoming(Label, EndBB);
1656 LabelPHI->addIncoming(IRB.getInt32(-1), Entry);
1657 SwitchInst *SI = IRB.CreateSwitch(LabelPHI, OrigEntry, SetjmpRetPHIs.size());
1658 // -1 means no longjmp happened, continue normally (will hit the default
1659 // switch case). 0 means a longjmp that is not ours to handle, needs a
1660 // rethrow. Otherwise the index is the same as the index in P+1 (to avoid
1661 // 0).
1662 for (unsigned I = 0; I < SetjmpRetPHIs.size(); I++) {
1663 SI->addCase(IRB.getInt32(I + 1), SetjmpRetPHIs[I]->getParent());
1664 SetjmpRetPHIs[I]->addIncoming(Val, SetjmpDispatchBB);
1665 }
1666
1667 // Convert all longjmpable call instructions to invokes that unwind to the
1668 // newly created catch.dispatch.longjmp BB.
1669 SmallVector<CallInst *, 64> LongjmpableCalls;
1670 for (auto *BB = &*F.begin(); BB; BB = BB->getNextNode()) {
1671 for (auto &I : *BB) {
1672 auto *CI = dyn_cast<CallInst>(&I);
1673 if (!CI)
1674 continue;
1675 const Value *Callee = CI->getCalledOperand();
1676 if (!canLongjmp(Callee))
1677 continue;
1678 if (isEmAsmCall(Callee))
1679 report_fatal_error("Cannot use EM_ASM* alongside setjmp/longjmp in " +
1680 F.getName() +
1681 ". Please consider using EM_JS, or move the "
1682 "EM_ASM into another function.",
1683 false);
1684 // This is __wasm_longjmp() call we inserted in this function, which
1685 // rethrows the longjmp when the longjmp does not correspond to one of
1686 // setjmps in this function. We should not convert this call to an invoke.
1687 if (CI == WasmLongjmpCI)
1688 continue;
1689 LongjmpableCalls.push_back(CI);
1690 }
1691 }
1692
1694 UnwindDestToNewPreds;
1695 for (auto *CI : LongjmpableCalls) {
1696 // Even if the callee function has attribute 'nounwind', which is true for
1697 // all C functions, it can longjmp, which means it can throw a Wasm
1698 // exception now.
1699 CI->removeFnAttr(Attribute::NoUnwind);
1700 if (Function *CalleeF = CI->getCalledFunction())
1701 CalleeF->removeFnAttr(Attribute::NoUnwind);
1702
1703 // Change it to an invoke and make it unwind to the catch.dispatch.longjmp
1704 // BB. If the call is enclosed in another catchpad/cleanuppad scope, unwind
1705 // to its parent pad's unwind destination instead to preserve the scope
1706 // structure. It will eventually unwind to the catch.dispatch.longjmp.
1708 BasicBlock *UnwindDest = nullptr;
1709 if (auto Bundle = CI->getOperandBundle(LLVMContext::OB_funclet)) {
1710 Instruction *FromPad = cast<Instruction>(Bundle->Inputs[0]);
1711 while (!UnwindDest) {
1712 if (auto *CPI = dyn_cast<CatchPadInst>(FromPad)) {
1713 UnwindDest = CPI->getCatchSwitch()->getUnwindDest();
1714 break;
1715 }
1716 if (auto *CPI = dyn_cast<CleanupPadInst>(FromPad)) {
1717 // getCleanupRetUnwindDest() can return nullptr when
1718 // 1. This cleanuppad's matching cleanupret uwninds to caller
1719 // 2. There is no matching cleanupret because it ends with
1720 // unreachable.
1721 // In case of 2, we need to traverse the parent pad chain.
1722 UnwindDest = getCleanupRetUnwindDest(CPI);
1723 Value *ParentPad = CPI->getParentPad();
1724 if (isa<ConstantTokenNone>(ParentPad))
1725 break;
1726 FromPad = cast<Instruction>(ParentPad);
1727 }
1728 }
1729 }
1730 if (!UnwindDest)
1731 UnwindDest = CatchDispatchLongjmpBB;
1732 // Because we are changing a longjmpable call to an invoke, its unwind
1733 // destination can be an existing EH pad that already have phis, and the BB
1734 // with the newly created invoke will become a new predecessor of that EH
1735 // pad. In this case we need to add the new predecessor to those phis.
1736 UnwindDestToNewPreds[UnwindDest].insert(CI->getParent());
1737 changeToInvokeAndSplitBasicBlock(CI, UnwindDest);
1738 }
1739
1741 for (auto &BB : F) {
1742 if (auto *CSI = dyn_cast<CatchSwitchInst>(BB.getFirstNonPHI())) {
1743 if (CSI != CatchSwitchLongjmp && CSI->unwindsToCaller()) {
1744 IRB.SetInsertPoint(CSI);
1745 ToErase.push_back(CSI);
1746 auto *NewCSI = IRB.CreateCatchSwitch(CSI->getParentPad(),
1747 CatchDispatchLongjmpBB, 1);
1748 NewCSI->addHandler(*CSI->handler_begin());
1749 NewCSI->takeName(CSI);
1750 CSI->replaceAllUsesWith(NewCSI);
1751 }
1752 }
1753
1754 if (auto *CRI = dyn_cast<CleanupReturnInst>(BB.getTerminator())) {
1755 if (CRI->unwindsToCaller()) {
1756 IRB.SetInsertPoint(CRI);
1757 ToErase.push_back(CRI);
1758 IRB.CreateCleanupRet(CRI->getCleanupPad(), CatchDispatchLongjmpBB);
1759 }
1760 }
1761 }
1762
1763 for (Instruction *I : ToErase)
1764 I->eraseFromParent();
1765
1766 // Add entries for new predecessors to phis in unwind destinations. We use
1767 // 'undef' as a placeholder value. We should make sure the phis have a valid
1768 // set of predecessors before running SSAUpdater, because SSAUpdater
1769 // internally can use existing phis to gather predecessor info rather than
1770 // scanning the actual CFG (See FindPredecessorBlocks in SSAUpdater.cpp for
1771 // details).
1772 for (auto &[UnwindDest, NewPreds] : UnwindDestToNewPreds) {
1773 for (PHINode &PN : UnwindDest->phis()) {
1774 for (auto *NewPred : NewPreds) {
1775 assert(PN.getBasicBlockIndex(NewPred) == -1);
1776 PN.addIncoming(UndefValue::get(PN.getType()), NewPred);
1777 }
1778 }
1779 }
1780
1781 // For unwind destinations for newly added invokes to longjmpable functions,
1782 // calculate incoming values for the newly added predecessors using
1783 // SSAUpdater. We add existing values in the phis to SSAUpdater as available
1784 // values and let it calculate what the value should be at the end of new
1785 // incoming blocks.
1786 for (auto &[UnwindDest, NewPreds] : UnwindDestToNewPreds) {
1787 for (PHINode &PN : UnwindDest->phis()) {
1789 SSA.Initialize(PN.getType(), PN.getName());
1790 for (unsigned Idx = 0, E = PN.getNumIncomingValues(); Idx != E; ++Idx) {
1791 if (NewPreds.contains(PN.getIncomingBlock(Idx)))
1792 continue;
1793 Value *V = PN.getIncomingValue(Idx);
1794 if (auto *II = dyn_cast<InvokeInst>(V))
1795 SSA.AddAvailableValue(II->getNormalDest(), II);
1796 else if (auto *I = dyn_cast<Instruction>(V))
1797 SSA.AddAvailableValue(I->getParent(), I);
1798 else
1799 SSA.AddAvailableValue(PN.getIncomingBlock(Idx), V);
1800 }
1801 for (auto *NewPred : NewPreds)
1802 PN.setIncomingValueForBlock(NewPred, SSA.GetValueAtEndOfBlock(NewPred));
1803 assert(PN.isComplete());
1804 }
1805 }
1806}
@ Poison
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
Returns the sub type a function will return at a given Idx Should correspond to the result type of an ExtractValue instruction executed with just that one unsigned Idx
#define LLVM_DEBUG(...)
Definition: Debug.h:106
std::string Name
Module.h This file contains the declarations for the Module class.
#define F(x, y, z)
Definition: MD5.cpp:55
#define I(x, y, z)
Definition: MD5.cpp:58
Memory SSA
Definition: MemorySSA.cpp:72
uint64_t IntrinsicInst * II
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis)
Definition: PassSupport.h:38
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
raw_pwrite_stream & OS
This file contains some functions that are useful when dealing with strings.
Target-Independent Code Generator Pass Configuration Options pass.
static void nullifySetjmp(Function *F)
static bool canLongjmp(const Value *Callee)
static cl::list< std::string > EHAllowlist("emscripten-cxx-exceptions-allowed", cl::desc("The list of function names in which Emscripten-style " "exception handling is enabled (see emscripten " "EMSCRIPTEN_CATCHING_ALLOWED options)"), cl::CommaSeparated)
static Type * getAddrPtrType(Module *M)
static std::string getSignature(FunctionType *FTy)
static Type * getAddrIntType(Module *M)
static bool canThrow(const Value *V)
static BasicBlock * getCleanupRetUnwindDest(const CleanupPadInst *CPI)
static DebugLoc getOrCreateDebugLoc(const Instruction *InsertBefore, DISubprogram *SP)
static bool containsLongjmpableCalls(const Function *F)
static Value * getAddrSizeInt(Module *M, uint64_t C)
static Function * getEmscriptenFunction(FunctionType *Ty, const Twine &Name, Module *M)
static GlobalVariable * getGlobalVariable(Module &M, Type *Ty, WebAssemblyTargetMachine &TM, const char *Name)
static bool isEmAsmCall(const Value *Callee)
This file provides WebAssembly-specific target descriptions.
This file declares the WebAssembly-specific subclass of TargetMachine.
This file contains the entry points for global functions defined in the LLVM WebAssembly back-end.
Represent the analysis usage information of a pass.
AnalysisUsage & addRequired()
AttributeSet getFnAttrs() const
The function attributes are returned.
static AttributeList get(LLVMContext &C, ArrayRef< std::pair< unsigned, Attribute > > Attrs)
Create an AttributeList with the specified parameters in it.
AttributeSet getRetAttrs() const
The attributes for the ret value are returned.
AttributeSet getParamAttrs(unsigned ArgNo) const
The attributes for the argument or parameter at the given index are returned.
static AttributeSet get(LLVMContext &C, const AttrBuilder &B)
Definition: Attributes.cpp:897
LLVM Basic Block Representation.
Definition: BasicBlock.h:61
iterator_range< const_phi_iterator > phis() const
Returns a range that iterates over the phis in the basic block.
Definition: BasicBlock.h:517
static BasicBlock * Create(LLVMContext &Context, const Twine &Name="", Function *Parent=nullptr, BasicBlock *InsertBefore=nullptr)
Creates a new BasicBlock.
Definition: BasicBlock.h:212
const Function * getParent() const
Return the enclosing method, or null if none.
Definition: BasicBlock.h:219
InstListType::iterator iterator
Instruction iterators...
Definition: BasicBlock.h:177
const Instruction & back() const
Definition: BasicBlock.h:473
Base class for all callable instructions (InvokeInst and CallInst) Holds everything related to callin...
Definition: InstrTypes.h:1120
void setCallingConv(CallingConv::ID CC)
Definition: InstrTypes.h:1411
std::optional< OperandBundleUse > getOperandBundle(StringRef Name) const
Return an operand bundle by name, if present.
Definition: InstrTypes.h:2051
Function * getCalledFunction() const
Returns the function called, or null if this is an indirect function invocation or the function signa...
Definition: InstrTypes.h:1349
User::op_iterator arg_begin()
Return the iterator pointing to the beginning of the argument list.
Definition: InstrTypes.h:1269
Value * getCalledOperand() const
Definition: InstrTypes.h:1342
void setAttributes(AttributeList A)
Set the attributes for this call.
Definition: InstrTypes.h:1428
Value * getArgOperand(unsigned i) const
Definition: InstrTypes.h:1294
User::op_iterator arg_end()
Return the iterator pointing to the end of the argument list.
Definition: InstrTypes.h:1275
FunctionType * getFunctionType() const
Definition: InstrTypes.h:1207
void removeFnAttr(Attribute::AttrKind Kind)
Removes the attribute from the function.
Definition: InstrTypes.h:1529
unsigned arg_size() const
Definition: InstrTypes.h:1292
AttributeList getAttributes() const
Return the attributes for this call.
Definition: InstrTypes.h:1425
This class represents a function call, abstracting a target machine's calling convention.
void addHandler(BasicBlock *Dest)
Add an entry to the switch instruction... Note: This action invalidates handler_end().
static ConstantTokenNone * get(LLVMContext &Context)
Return the ConstantTokenNone.
Definition: Constants.cpp:1522
This is an important base class in LLVM.
Definition: Constant.h:42
Subprogram description.
A debug info location.
Definition: DebugLoc.h:33
size_type count(const_arg_type_t< KeyT > Val) const
Return 1 if the specified key is in the map, 0 otherwise.
Definition: DenseMap.h:152
std::pair< iterator, bool > insert(const std::pair< KeyT, ValueT > &KV)
Definition: DenseMap.h:211
void recalculate(ParentType &Func)
recalculate - compute a dominator tree for the given function
Legacy analysis pass which computes a DominatorTree.
Definition: Dominators.h:317
Concrete subclass of DominatorTreeBase that is used to compute a normal dominator tree.
Definition: Dominators.h:162
bool dominates(const BasicBlock *BB, const Use &U) const
Return true if the (end of the) basic block BB dominates the use U.
Definition: Dominators.cpp:122
void addFnAttr(Attribute::AttrKind Kind)
Add function attributes to this function.
Definition: Function.cpp:641
static Function * Create(FunctionType *Ty, LinkageTypes Linkage, unsigned AddrSpace, const Twine &N="", Module *M=nullptr)
Definition: Function.h:173
FunctionType * getFunctionType() const
Returns the FunctionType for me.
Definition: Function.h:216
void setDoesNotThrow()
Definition: Function.h:598
Module * getParent()
Get the module that this global value is contained inside of...
Definition: GlobalValue.h:656
PointerType * getType() const
Global values are always pointers.
Definition: GlobalValue.h:294
@ ExternalLinkage
Externally visible function.
Definition: GlobalValue.h:52
IntegerType * getIntNTy(unsigned N)
Fetch the type representing an N-bit integer.
Definition: IRBuilder.h:536
ConstantInt * getInt32(uint32_t C)
Get a constant 32-bit value.
Definition: IRBuilder.h:483
ConstantInt * getIntN(unsigned N, uint64_t C)
Get a constant N-bit value, zero extended or truncated from a 64-bit value.
Definition: IRBuilder.h:494
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
Definition: IRBuilder.h:2697
const DebugLoc & getDebugLoc() const
Return the debug location for this node as a DebugLoc.
Definition: Instruction.h:471
const Module * getModule() const
Return the module owning the function this instruction belongs to or nullptr it the function does not...
Definition: Instruction.cpp:66
void setDebugLoc(DebugLoc Loc)
Set the debug location information for this instruction.
Definition: Instruction.h:468
This is an important class for using LLVM in a threaded context.
Definition: LLVMContext.h:67
The landingpad instruction holds all of the information necessary to generate correct exception handl...
An instruction for reading from memory.
Definition: Instructions.h:176
LLVMContext & getContext() const
Definition: Metadata.h:1233
ModulePass class - This class is used to implement unstructured interprocedural optimizations and ana...
Definition: Pass.h:251
virtual bool runOnModule(Module &M)=0
runOnModule - Virtual method overriden by subclasses to process the module being operated on.
A Module instance is used to store all the information related to an LLVM module.
Definition: Module.h:65
LLVMContext & getContext() const
Get the global data context.
Definition: Module.h:302
void addIncoming(Value *V, BasicBlock *BB)
Add an incoming value to the end of the PHI list.
virtual void getAnalysisUsage(AnalysisUsage &) const
getAnalysisUsage - This function should be overriden by passes that need analysis information to do t...
Definition: Pass.cpp:98
virtual StringRef getPassName() const
getPassName - Return a nice clean name for a pass.
Definition: Pass.cpp:81
static PoisonValue * get(Type *T)
Static factory methods - Return an 'poison' object of the specified type.
Definition: Constants.cpp:1878
Helper class for SSA formation on a set of values defined in multiple blocks.
Helper class for SSA formation on a set of values defined in multiple blocks.
Definition: SSAUpdater.h:40
size_type count(ConstPtrType Ptr) const
count - Return 1 if the specified pointer is in the set, 0 otherwise.
Definition: SmallPtrSet.h:452
std::pair< iterator, bool > insert(PtrType Ptr)
Inserts Ptr if and only if there is no element in the container equal to Ptr.
Definition: SmallPtrSet.h:384
SmallPtrSet - This class implements a set which is optimized for holding SmallSize or less elements.
Definition: SmallPtrSet.h:519
size_t size() const
Definition: SmallVector.h:78
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
Definition: SmallVector.h:573
void append(ItTy in_start, ItTy in_end)
Add the specified range to the end of the SmallVector.
Definition: SmallVector.h:683
void push_back(const T &Elt)
Definition: SmallVector.h:413
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1196
An instruction for storing to memory.
Definition: Instructions.h:292
StringMap - This is an unconventional map that is specialized for handling keys that are "strings",...
Definition: StringMap.h:128
iterator end()
Definition: StringMap.h:220
iterator find(StringRef Key)
Definition: StringMap.h:233
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:51
static StructType * get(LLVMContext &Context, ArrayRef< Type * > Elements, bool isPacked=false)
This static method is the primary way to create a literal StructType.
Definition: Type.cpp:406
Multiway switch.
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition: Twine.h:81
The instances of the Type class are immutable: once they are created, they are never changed.
Definition: Type.h:45
static IntegerType * getInt32Ty(LLVMContext &C)
static UndefValue * get(Type *T)
Static factory methods - Return an 'undef' object of the specified type.
Definition: Constants.cpp:1859
LLVM Value Representation.
Definition: Value.h:74
void replaceAllUsesWith(Value *V)
Change all uses of this to point to a new Value.
Definition: Value.cpp:534
iterator_range< user_iterator > users()
Definition: Value.h:421
bool use_empty() const
Definition: Value.h:344
LLVMContext & getContext() const
All values hold a context through their type.
Definition: Value.cpp:1075
iterator_range< use_iterator > uses()
Definition: Value.h:376
StringRef getName() const
Return a constant reference to the value's name.
Definition: Value.cpp:309
void takeName(Value *V)
Transfer the name from V to this value.
Definition: Value.cpp:383
const ParentTy * getParent() const
Definition: ilist_node.h:32
NodeTy * getNextNode()
Get the next node, or nullptr for the list tail.
Definition: ilist_node.h:353
A raw_ostream that writes to an std::string.
Definition: raw_ostream.h:661
constexpr char Args[]
Key for Kernel::Metadata::mArgs.
@ Entry
Definition: COFF.h:844
@ WASM_EmscriptenInvoke
For emscripten __invoke_* functions.
Definition: CallingConv.h:229
@ Tail
Attemps to make calls as fast as possible while guaranteeing that tail call optimization can always b...
Definition: CallingConv.h:76
@ C
The default llvm calling convention, compatible with C.
Definition: CallingConv.h:34
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
Definition: CallingConv.h:24
Function * getOrInsertDeclaration(Module *M, ID id, ArrayRef< Type * > Tys={})
Look up the Function declaration of the intrinsic id in the Module M.
Definition: Intrinsics.cpp:731
cl::opt< bool > WasmEnableSjLj
cl::opt< bool > WasmEnableEmEH
cl::opt< bool > WasmEnableEmSjLj
@ SS
Definition: X86.h:212
@ CommaSeparated
Definition: CommandLine.h:163
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
@ Low
Lower the current thread's priority such that it does not affect foreground tasks significantly.
StringRef getEHPersonalityName(EHPersonality Pers)
BasicBlock * changeToInvokeAndSplitBasicBlock(CallInst *CI, BasicBlock *UnwindEdge, DomTreeUpdater *DTU=nullptr)
Convert the CallInst to InvokeInst with the specified unwind edge basic block.
Definition: Local.cpp:2992
CallInst * changeToCall(InvokeInst *II, DomTreeUpdater *DTU=nullptr)
This function converts the specified invoke into a normal call.
Definition: Local.cpp:2972
iterator_range< early_inc_iterator_impl< detail::IterOfRange< RangeT > > > make_early_inc_range(RangeT &&Range)
Make a range that does early increment to allow mutation of the underlying range without disrupting i...
Definition: STLExtras.h:657
OperandBundleDefT< Value * > OperandBundleDef
Definition: AutoUpgrade.h:33
raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition: Debug.cpp:163
std::tuple< const DIScope *, const DIScope *, const DILocalVariable * > VarID
A unique key that represents a debug variable.
void report_fatal_error(Error Err, bool gen_crash_diag=true)
Report a serious error, calling any installed error handler.
Definition: Error.cpp:167
ModulePass * createWebAssemblyLowerEmscriptenEHSjLj()
@ Or
Bitwise or logical OR of integers.
BasicBlock * SplitBlock(BasicBlock *Old, BasicBlock::iterator SplitPt, DominatorTree *DT, LoopInfo *LI=nullptr, MemorySSAUpdater *MSSAU=nullptr, const Twine &BBName="", bool Before=false)
Split the specified block at the specified instruction.
void erase_if(Container &C, UnaryPredicate P)
Provide a container algorithm similar to C++ Library Fundamentals v2's erase_if which is equivalent t...
Definition: STLExtras.h:2099