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> 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 && (areAllExceptionsAllowed() ||
353 EHAllowlistSet.count(std::string(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 if (InvokeWrappers.contains(Sig))
579 return InvokeWrappers[Sig];
580
581 // Put the pointer to the callee as first argument
582 ArgTys.push_back(PointerType::getUnqual(CalleeFTy));
583 // Add argument types
584 ArgTys.append(CalleeFTy->param_begin(), CalleeFTy->param_end());
585
586 FunctionType *FTy = FunctionType::get(CalleeFTy->getReturnType(), ArgTys,
587 CalleeFTy->isVarArg());
588 Function *F = getEmscriptenFunction(FTy, "__invoke_" + Sig, M);
589 InvokeWrappers[Sig] = F;
590 return F;
591}
592
593static bool canLongjmp(const Value *Callee) {
594 if (auto *CalleeF = dyn_cast<Function>(Callee))
595 if (CalleeF->isIntrinsic())
596 return false;
597
598 // Attempting to transform inline assembly will result in something like:
599 // call void @__invoke_void(void ()* asm ...)
600 // which is invalid because inline assembly blocks do not have addresses
601 // and can't be passed by pointer. The result is a crash with illegal IR.
602 if (isa<InlineAsm>(Callee))
603 return false;
604 StringRef CalleeName = Callee->getName();
605
606 // TODO Include more functions or consider checking with mangled prefixes
607
608 // The reason we include malloc/free here is to exclude the malloc/free
609 // calls generated in setjmp prep / cleanup routines.
610 if (CalleeName == "setjmp" || CalleeName == "malloc" || CalleeName == "free")
611 return false;
612
613 // There are functions in Emscripten's JS glue code or compiler-rt
614 if (CalleeName == "__resumeException" || CalleeName == "llvm_eh_typeid_for" ||
615 CalleeName == "__wasm_setjmp" || CalleeName == "__wasm_setjmp_test" ||
616 CalleeName == "getTempRet0" || CalleeName == "setTempRet0")
617 return false;
618
619 // __cxa_find_matching_catch_N functions cannot longjmp
620 if (Callee->getName().starts_with("__cxa_find_matching_catch_"))
621 return false;
622
623 // Exception-catching related functions
624 //
625 // We intentionally treat __cxa_end_catch longjmpable in Wasm SjLj even though
626 // it surely cannot longjmp, in order to maintain the unwind relationship from
627 // all existing catchpads (and calls within them) to catch.dispatch.longjmp.
628 //
629 // In Wasm EH + Wasm SjLj, we
630 // 1. Make all catchswitch and cleanuppad that unwind to caller unwind to
631 // catch.dispatch.longjmp instead
632 // 2. Convert all longjmpable calls to invokes that unwind to
633 // catch.dispatch.longjmp
634 // But catchswitch BBs are removed in isel, so if an EH catchswitch (generated
635 // from an exception)'s catchpad does not contain any calls that are converted
636 // into invokes unwinding to catch.dispatch.longjmp, this unwind relationship
637 // (EH catchswitch BB -> catch.dispatch.longjmp BB) is lost and
638 // catch.dispatch.longjmp BB can be placed before the EH catchswitch BB in
639 // CFGSort.
640 // int ret = setjmp(buf);
641 // try {
642 // foo(); // longjmps
643 // } catch (...) {
644 // }
645 // Then in this code, if 'foo' longjmps, it first unwinds to 'catch (...)'
646 // catchswitch, and is not caught by that catchswitch because it is a longjmp,
647 // then it should next unwind to catch.dispatch.longjmp BB. But if this 'catch
648 // (...)' catchswitch -> catch.dispatch.longjmp unwind relationship is lost,
649 // it will not unwind to catch.dispatch.longjmp, producing an incorrect
650 // result.
651 //
652 // Every catchpad generated by Wasm C++ contains __cxa_end_catch, so we
653 // intentionally treat it as longjmpable to work around this problem. This is
654 // a hacky fix but an easy one.
655 //
656 // The comment block in findWasmUnwindDestinations() in
657 // SelectionDAGBuilder.cpp is addressing a similar problem.
658 if (CalleeName == "__cxa_end_catch")
660 if (CalleeName == "__cxa_begin_catch" ||
661 CalleeName == "__cxa_allocate_exception" || CalleeName == "__cxa_throw" ||
662 CalleeName == "__clang_call_terminate")
663 return false;
664
665 // std::terminate, which is generated when another exception occurs while
666 // handling an exception, cannot longjmp.
667 if (CalleeName == "_ZSt9terminatev")
668 return false;
669
670 // Otherwise we don't know
671 return true;
672}
673
674static bool isEmAsmCall(const Value *Callee) {
675 StringRef CalleeName = Callee->getName();
676 // This is an exhaustive list from Emscripten's <emscripten/em_asm.h>.
677 return CalleeName == "emscripten_asm_const_int" ||
678 CalleeName == "emscripten_asm_const_double" ||
679 CalleeName == "emscripten_asm_const_int_sync_on_main_thread" ||
680 CalleeName == "emscripten_asm_const_double_sync_on_main_thread" ||
681 CalleeName == "emscripten_asm_const_async_on_main_thread";
682}
683
684// Generate __wasm_setjmp_test function call seqence with preamble and
685// postamble. The code this generates is equivalent to the following
686// JavaScript code:
687// %__threwValue.val = __threwValue;
688// if (%__THREW__.val != 0 & %__threwValue.val != 0) {
689// %label = __wasm_setjmp_test(%__THREW__.val, functionInvocationId);
690// if (%label == 0)
691// emscripten_longjmp(%__THREW__.val, %__threwValue.val);
692// setTempRet0(%__threwValue.val);
693// } else {
694// %label = -1;
695// }
696// %longjmp_result = getTempRet0();
697//
698// As output parameters. returns %label, %longjmp_result, and the BB the last
699// instruction (%longjmp_result = ...) is in.
700void WebAssemblyLowerEmscriptenEHSjLj::wrapTestSetjmp(
701 BasicBlock *BB, DebugLoc DL, Value *Threw, Value *FunctionInvocationId,
702 Value *&Label, Value *&LongjmpResult, BasicBlock *&CallEmLongjmpBB,
703 PHINode *&CallEmLongjmpBBThrewPHI, PHINode *&CallEmLongjmpBBThrewValuePHI,
704 BasicBlock *&EndBB) {
705 Function *F = BB->getParent();
706 Module *M = F->getParent();
707 LLVMContext &C = M->getContext();
708 IRBuilder<> IRB(C);
709 IRB.SetCurrentDebugLocation(DL);
710
711 // if (%__THREW__.val != 0 & %__threwValue.val != 0)
712 IRB.SetInsertPoint(BB);
713 BasicBlock *ThenBB1 = BasicBlock::Create(C, "if.then1", F);
714 BasicBlock *ElseBB1 = BasicBlock::Create(C, "if.else1", F);
715 BasicBlock *EndBB1 = BasicBlock::Create(C, "if.end", F);
716 Value *ThrewCmp = IRB.CreateICmpNE(Threw, getAddrSizeInt(M, 0));
717 Value *ThrewValue = IRB.CreateLoad(IRB.getInt32Ty(), ThrewValueGV,
718 ThrewValueGV->getName() + ".val");
719 Value *ThrewValueCmp = IRB.CreateICmpNE(ThrewValue, IRB.getInt32(0));
720 Value *Cmp1 = IRB.CreateAnd(ThrewCmp, ThrewValueCmp, "cmp1");
721 IRB.CreateCondBr(Cmp1, ThenBB1, ElseBB1);
722
723 // Generate call.em.longjmp BB once and share it within the function
724 if (!CallEmLongjmpBB) {
725 // emscripten_longjmp(%__THREW__.val, %__threwValue.val);
726 CallEmLongjmpBB = BasicBlock::Create(C, "call.em.longjmp", F);
727 IRB.SetInsertPoint(CallEmLongjmpBB);
728 CallEmLongjmpBBThrewPHI = IRB.CreatePHI(getAddrIntType(M), 4, "threw.phi");
729 CallEmLongjmpBBThrewValuePHI =
730 IRB.CreatePHI(IRB.getInt32Ty(), 4, "threwvalue.phi");
731 CallEmLongjmpBBThrewPHI->addIncoming(Threw, ThenBB1);
732 CallEmLongjmpBBThrewValuePHI->addIncoming(ThrewValue, ThenBB1);
733 IRB.CreateCall(EmLongjmpF,
734 {CallEmLongjmpBBThrewPHI, CallEmLongjmpBBThrewValuePHI});
735 IRB.CreateUnreachable();
736 } else {
737 CallEmLongjmpBBThrewPHI->addIncoming(Threw, ThenBB1);
738 CallEmLongjmpBBThrewValuePHI->addIncoming(ThrewValue, ThenBB1);
739 }
740
741 // %label = __wasm_setjmp_test(%__THREW__.val, functionInvocationId);
742 // if (%label == 0)
743 IRB.SetInsertPoint(ThenBB1);
744 BasicBlock *EndBB2 = BasicBlock::Create(C, "if.end2", F);
745 Value *ThrewPtr =
746 IRB.CreateIntToPtr(Threw, getAddrPtrType(M), Threw->getName() + ".p");
747 Value *ThenLabel = IRB.CreateCall(WasmSetjmpTestF,
748 {ThrewPtr, FunctionInvocationId}, "label");
749 Value *Cmp2 = IRB.CreateICmpEQ(ThenLabel, IRB.getInt32(0));
750 IRB.CreateCondBr(Cmp2, CallEmLongjmpBB, EndBB2);
751
752 // setTempRet0(%__threwValue.val);
753 IRB.SetInsertPoint(EndBB2);
754 IRB.CreateCall(SetTempRet0F, ThrewValue);
755 IRB.CreateBr(EndBB1);
756
757 IRB.SetInsertPoint(ElseBB1);
758 IRB.CreateBr(EndBB1);
759
760 // longjmp_result = getTempRet0();
761 IRB.SetInsertPoint(EndBB1);
762 PHINode *LabelPHI = IRB.CreatePHI(IRB.getInt32Ty(), 2, "label");
763 LabelPHI->addIncoming(ThenLabel, EndBB2);
764
765 LabelPHI->addIncoming(IRB.getInt32(-1), ElseBB1);
766
767 // Output parameter assignment
768 Label = LabelPHI;
769 EndBB = EndBB1;
770 LongjmpResult = IRB.CreateCall(GetTempRet0F, std::nullopt, "longjmp_result");
771}
772
773void WebAssemblyLowerEmscriptenEHSjLj::rebuildSSA(Function &F) {
774 DominatorTree &DT = getAnalysis<DominatorTreeWrapperPass>(F).getDomTree();
775 DT.recalculate(F); // CFG has been changed
776
778 for (BasicBlock &BB : F) {
779 for (Instruction &I : BB) {
780 if (I.getType()->isVoidTy())
781 continue;
782 unsigned VarID = SSA.AddVariable(I.getName(), I.getType());
783 // If a value is defined by an invoke instruction, it is only available in
784 // its normal destination and not in its unwind destination.
785 if (auto *II = dyn_cast<InvokeInst>(&I))
786 SSA.AddAvailableValue(VarID, II->getNormalDest(), II);
787 else
788 SSA.AddAvailableValue(VarID, &BB, &I);
789 for (auto &U : I.uses()) {
790 auto *User = cast<Instruction>(U.getUser());
791 if (auto *UserPN = dyn_cast<PHINode>(User))
792 if (UserPN->getIncomingBlock(U) == &BB)
793 continue;
794 if (DT.dominates(&I, User))
795 continue;
796 SSA.AddUse(VarID, &U);
797 }
798 }
799 }
800 SSA.RewriteAllUses(&DT);
801}
802
803// Replace uses of longjmp with a new longjmp function in Emscripten library.
804// In Emscripten SjLj, the new function is
805// void emscripten_longjmp(uintptr_t, i32)
806// In Wasm SjLj, the new function is
807// void __wasm_longjmp(i8*, i32)
808// Because the original libc longjmp function takes (jmp_buf*, i32), we need a
809// ptrtoint/bitcast instruction here to make the type match. jmp_buf* will
810// eventually be lowered to i32/i64 in the wasm backend.
811void WebAssemblyLowerEmscriptenEHSjLj::replaceLongjmpWith(Function *LongjmpF,
812 Function *NewF) {
813 assert(NewF == EmLongjmpF || NewF == WasmLongjmpF);
814 Module *M = LongjmpF->getParent();
816 LLVMContext &C = LongjmpF->getParent()->getContext();
817 IRBuilder<> IRB(C);
818
819 // For calls to longjmp, replace it with emscripten_longjmp/__wasm_longjmp and
820 // cast its first argument (jmp_buf*) appropriately
821 for (User *U : LongjmpF->users()) {
822 auto *CI = dyn_cast<CallInst>(U);
823 if (CI && CI->getCalledFunction() == LongjmpF) {
824 IRB.SetInsertPoint(CI);
825 Value *Env = nullptr;
826 if (NewF == EmLongjmpF)
827 Env =
828 IRB.CreatePtrToInt(CI->getArgOperand(0), getAddrIntType(M), "env");
829 else // WasmLongjmpF
830 Env = IRB.CreateBitCast(CI->getArgOperand(0), IRB.getPtrTy(), "env");
831 IRB.CreateCall(NewF, {Env, CI->getArgOperand(1)});
832 ToErase.push_back(CI);
833 }
834 }
835 for (auto *I : ToErase)
836 I->eraseFromParent();
837
838 // If we have any remaining uses of longjmp's function pointer, replace it
839 // with (void(*)(jmp_buf*, int))emscripten_longjmp / __wasm_longjmp.
840 if (!LongjmpF->uses().empty()) {
841 Value *NewLongjmp =
842 IRB.CreateBitCast(NewF, LongjmpF->getType(), "longjmp.cast");
843 LongjmpF->replaceAllUsesWith(NewLongjmp);
844 }
845}
846
848 for (const auto &BB : *F)
849 for (const auto &I : BB)
850 if (const auto *CB = dyn_cast<CallBase>(&I))
851 if (canLongjmp(CB->getCalledOperand()))
852 return true;
853 return false;
854}
855
856// When a function contains a setjmp call but not other calls that can longjmp,
857// we don't do setjmp transformation for that setjmp. But we need to convert the
858// setjmp calls into "i32 0" so they don't cause link time errors. setjmp always
859// returns 0 when called directly.
860static void nullifySetjmp(Function *F) {
861 Module &M = *F->getParent();
862 IRBuilder<> IRB(M.getContext());
863 Function *SetjmpF = M.getFunction("setjmp");
865
866 for (User *U : make_early_inc_range(SetjmpF->users())) {
867 auto *CB = cast<CallBase>(U);
868 BasicBlock *BB = CB->getParent();
869 if (BB->getParent() != F) // in other function
870 continue;
871 CallInst *CI = nullptr;
872 // setjmp cannot throw. So if it is an invoke, lower it to a call
873 if (auto *II = dyn_cast<InvokeInst>(CB))
875 else
876 CI = cast<CallInst>(CB);
877 ToErase.push_back(CI);
878 CI->replaceAllUsesWith(IRB.getInt32(0));
879 }
880 for (auto *I : ToErase)
881 I->eraseFromParent();
882}
883
884bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) {
885 LLVM_DEBUG(dbgs() << "********** Lower Emscripten EH & SjLj **********\n");
886
887 LLVMContext &C = M.getContext();
888 IRBuilder<> IRB(C);
889
890 Function *SetjmpF = M.getFunction("setjmp");
891 Function *LongjmpF = M.getFunction("longjmp");
892
893 // In some platforms _setjmp and _longjmp are used instead. Change these to
894 // use setjmp/longjmp instead, because we later detect these functions by
895 // their names.
896 Function *SetjmpF2 = M.getFunction("_setjmp");
897 Function *LongjmpF2 = M.getFunction("_longjmp");
898 if (SetjmpF2) {
899 if (SetjmpF) {
900 if (SetjmpF->getFunctionType() != SetjmpF2->getFunctionType())
901 report_fatal_error("setjmp and _setjmp have different function types");
902 } else {
903 SetjmpF = Function::Create(SetjmpF2->getFunctionType(),
904 GlobalValue::ExternalLinkage, "setjmp", M);
905 }
906 SetjmpF2->replaceAllUsesWith(SetjmpF);
907 }
908 if (LongjmpF2) {
909 if (LongjmpF) {
910 if (LongjmpF->getFunctionType() != LongjmpF2->getFunctionType())
912 "longjmp and _longjmp have different function types");
913 } else {
914 LongjmpF = Function::Create(LongjmpF2->getFunctionType(),
915 GlobalValue::ExternalLinkage, "setjmp", M);
916 }
917 LongjmpF2->replaceAllUsesWith(LongjmpF);
918 }
919
920 auto *TPC = getAnalysisIfAvailable<TargetPassConfig>();
921 assert(TPC && "Expected a TargetPassConfig");
922 auto &TM = TPC->getTM<WebAssemblyTargetMachine>();
923
924 // Declare (or get) global variables __THREW__, __threwValue, and
925 // getTempRet0/setTempRet0 function which are used in common for both
926 // exception handling and setjmp/longjmp handling
927 ThrewGV = getGlobalVariable(M, getAddrIntType(&M), TM, "__THREW__");
928 ThrewValueGV = getGlobalVariable(M, IRB.getInt32Ty(), TM, "__threwValue");
929 GetTempRet0F = getEmscriptenFunction(
930 FunctionType::get(IRB.getInt32Ty(), false), "getTempRet0", &M);
931 SetTempRet0F = getEmscriptenFunction(
932 FunctionType::get(IRB.getVoidTy(), IRB.getInt32Ty(), false),
933 "setTempRet0", &M);
934 GetTempRet0F->setDoesNotThrow();
935 SetTempRet0F->setDoesNotThrow();
936
937 bool Changed = false;
938
939 // Function registration for exception handling
940 if (EnableEmEH) {
941 // Register __resumeException function
942 FunctionType *ResumeFTy =
943 FunctionType::get(IRB.getVoidTy(), IRB.getPtrTy(), false);
944 ResumeF = getEmscriptenFunction(ResumeFTy, "__resumeException", &M);
945 ResumeF->addFnAttr(Attribute::NoReturn);
946
947 // Register llvm_eh_typeid_for function
948 FunctionType *EHTypeIDTy =
949 FunctionType::get(IRB.getInt32Ty(), IRB.getPtrTy(), false);
950 EHTypeIDF = getEmscriptenFunction(EHTypeIDTy, "llvm_eh_typeid_for", &M);
951 }
952
953 // Functions that contains calls to setjmp but don't have other longjmpable
954 // calls within them.
955 SmallPtrSet<Function *, 4> SetjmpUsersToNullify;
956
957 if ((EnableEmSjLj || EnableWasmSjLj) && SetjmpF) {
958 // Precompute setjmp users
959 for (User *U : SetjmpF->users()) {
960 if (auto *CB = dyn_cast<CallBase>(U)) {
961 auto *UserF = CB->getFunction();
962 // If a function that calls setjmp does not contain any other calls that
963 // can longjmp, we don't need to do any transformation on that function,
964 // so can ignore it
965 if (containsLongjmpableCalls(UserF))
966 SetjmpUsers.insert(UserF);
967 else
968 SetjmpUsersToNullify.insert(UserF);
969 } else {
970 std::string S;
972 SS << *U;
973 report_fatal_error(Twine("Indirect use of setjmp is not supported: ") +
974 SS.str());
975 }
976 }
977 }
978
979 bool SetjmpUsed = SetjmpF && !SetjmpUsers.empty();
980 bool LongjmpUsed = LongjmpF && !LongjmpF->use_empty();
981 DoSjLj = (EnableEmSjLj | EnableWasmSjLj) && (SetjmpUsed || LongjmpUsed);
982
983 // Function registration and data pre-gathering for setjmp/longjmp handling
984 if (DoSjLj) {
985 assert(EnableEmSjLj || EnableWasmSjLj);
986 if (EnableEmSjLj) {
987 // Register emscripten_longjmp function
988 FunctionType *FTy = FunctionType::get(
989 IRB.getVoidTy(), {getAddrIntType(&M), IRB.getInt32Ty()}, false);
990 EmLongjmpF = getEmscriptenFunction(FTy, "emscripten_longjmp", &M);
991 EmLongjmpF->addFnAttr(Attribute::NoReturn);
992 } else { // EnableWasmSjLj
993 Type *Int8PtrTy = IRB.getPtrTy();
994 // Register __wasm_longjmp function, which calls __builtin_wasm_longjmp.
995 FunctionType *FTy = FunctionType::get(
996 IRB.getVoidTy(), {Int8PtrTy, IRB.getInt32Ty()}, false);
997 WasmLongjmpF = getEmscriptenFunction(FTy, "__wasm_longjmp", &M);
998 WasmLongjmpF->addFnAttr(Attribute::NoReturn);
999 }
1000
1001 if (SetjmpF) {
1002 Type *Int8PtrTy = IRB.getPtrTy();
1003 Type *Int32PtrTy = IRB.getPtrTy();
1004 Type *Int32Ty = IRB.getInt32Ty();
1005
1006 // Register __wasm_setjmp function
1007 FunctionType *SetjmpFTy = SetjmpF->getFunctionType();
1008 FunctionType *FTy = FunctionType::get(
1009 IRB.getVoidTy(), {SetjmpFTy->getParamType(0), Int32Ty, Int32PtrTy},
1010 false);
1011 WasmSetjmpF = getEmscriptenFunction(FTy, "__wasm_setjmp", &M);
1012
1013 // Register __wasm_setjmp_test function
1014 FTy = FunctionType::get(Int32Ty, {Int32PtrTy, Int32PtrTy}, false);
1015 WasmSetjmpTestF = getEmscriptenFunction(FTy, "__wasm_setjmp_test", &M);
1016
1017 // wasm.catch() will be lowered down to wasm 'catch' instruction in
1018 // instruction selection.
1019 CatchF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_catch);
1020 // Type for struct __WasmLongjmpArgs
1021 LongjmpArgsTy = StructType::get(Int8PtrTy, // env
1022 Int32Ty // val
1023 );
1024 }
1025 }
1026
1027 // Exception handling transformation
1028 if (EnableEmEH) {
1029 for (Function &F : M) {
1030 if (F.isDeclaration())
1031 continue;
1032 Changed |= runEHOnFunction(F);
1033 }
1034 }
1035
1036 // Setjmp/longjmp handling transformation
1037 if (DoSjLj) {
1038 Changed = true; // We have setjmp or longjmp somewhere
1039 if (LongjmpF)
1040 replaceLongjmpWith(LongjmpF, EnableEmSjLj ? EmLongjmpF : WasmLongjmpF);
1041 // Only traverse functions that uses setjmp in order not to insert
1042 // unnecessary prep / cleanup code in every function
1043 if (SetjmpF)
1044 for (Function *F : SetjmpUsers)
1045 runSjLjOnFunction(*F);
1046 }
1047
1048 // Replace unnecessary setjmp calls with 0
1049 if ((EnableEmSjLj || EnableWasmSjLj) && !SetjmpUsersToNullify.empty()) {
1050 Changed = true;
1051 assert(SetjmpF);
1052 for (Function *F : SetjmpUsersToNullify)
1054 }
1055
1056 // Delete unused global variables and functions
1057 for (auto *V : {ThrewGV, ThrewValueGV})
1058 if (V && V->use_empty())
1059 V->eraseFromParent();
1060 for (auto *V : {GetTempRet0F, SetTempRet0F, ResumeF, EHTypeIDF, EmLongjmpF,
1061 WasmSetjmpF, WasmSetjmpTestF, WasmLongjmpF, CatchF})
1062 if (V && V->use_empty())
1063 V->eraseFromParent();
1064
1065 return Changed;
1066}
1067
1068bool WebAssemblyLowerEmscriptenEHSjLj::runEHOnFunction(Function &F) {
1069 Module &M = *F.getParent();
1070 LLVMContext &C = F.getContext();
1071 IRBuilder<> IRB(C);
1072 bool Changed = false;
1075
1076 // rethrow.longjmp BB that will be shared within the function.
1077 BasicBlock *RethrowLongjmpBB = nullptr;
1078 // PHI node for the loaded value of __THREW__ global variable in
1079 // rethrow.longjmp BB
1080 PHINode *RethrowLongjmpBBThrewPHI = nullptr;
1081
1082 for (BasicBlock &BB : F) {
1083 auto *II = dyn_cast<InvokeInst>(BB.getTerminator());
1084 if (!II)
1085 continue;
1086 Changed = true;
1087 LandingPads.insert(II->getLandingPadInst());
1088 IRB.SetInsertPoint(II);
1089
1090 const Value *Callee = II->getCalledOperand();
1091 bool NeedInvoke = supportsException(&F) && canThrow(Callee);
1092 if (NeedInvoke) {
1093 // Wrap invoke with invoke wrapper and generate preamble/postamble
1094 Value *Threw = wrapInvoke(II);
1095 ToErase.push_back(II);
1096
1097 // If setjmp/longjmp handling is enabled, the thrown value can be not an
1098 // exception but a longjmp. If the current function contains calls to
1099 // setjmp, it will be appropriately handled in runSjLjOnFunction. But even
1100 // if the function does not contain setjmp calls, we shouldn't silently
1101 // ignore longjmps; we should rethrow them so they can be correctly
1102 // handled in somewhere up the call chain where setjmp is. __THREW__'s
1103 // value is 0 when nothing happened, 1 when an exception is thrown, and
1104 // other values when longjmp is thrown.
1105 //
1106 // if (%__THREW__.val == 0 || %__THREW__.val == 1)
1107 // goto %tail
1108 // else
1109 // goto %longjmp.rethrow
1110 //
1111 // rethrow.longjmp: ;; This is longjmp. Rethrow it
1112 // %__threwValue.val = __threwValue
1113 // emscripten_longjmp(%__THREW__.val, %__threwValue.val);
1114 //
1115 // tail: ;; Nothing happened or an exception is thrown
1116 // ... Continue exception handling ...
1117 if (DoSjLj && EnableEmSjLj && !SetjmpUsers.count(&F) &&
1118 canLongjmp(Callee)) {
1119 // Create longjmp.rethrow BB once and share it within the function
1120 if (!RethrowLongjmpBB) {
1121 RethrowLongjmpBB = BasicBlock::Create(C, "rethrow.longjmp", &F);
1122 IRB.SetInsertPoint(RethrowLongjmpBB);
1123 RethrowLongjmpBBThrewPHI =
1124 IRB.CreatePHI(getAddrIntType(&M), 4, "threw.phi");
1125 RethrowLongjmpBBThrewPHI->addIncoming(Threw, &BB);
1126 Value *ThrewValue = IRB.CreateLoad(IRB.getInt32Ty(), ThrewValueGV,
1127 ThrewValueGV->getName() + ".val");
1128 IRB.CreateCall(EmLongjmpF, {RethrowLongjmpBBThrewPHI, ThrewValue});
1129 IRB.CreateUnreachable();
1130 } else {
1131 RethrowLongjmpBBThrewPHI->addIncoming(Threw, &BB);
1132 }
1133
1134 IRB.SetInsertPoint(II); // Restore the insert point back
1135 BasicBlock *Tail = BasicBlock::Create(C, "tail", &F);
1136 Value *CmpEqOne =
1137 IRB.CreateICmpEQ(Threw, getAddrSizeInt(&M, 1), "cmp.eq.one");
1138 Value *CmpEqZero =
1139 IRB.CreateICmpEQ(Threw, getAddrSizeInt(&M, 0), "cmp.eq.zero");
1140 Value *Or = IRB.CreateOr(CmpEqZero, CmpEqOne, "or");
1141 IRB.CreateCondBr(Or, Tail, RethrowLongjmpBB);
1142 IRB.SetInsertPoint(Tail);
1143 BB.replaceSuccessorsPhiUsesWith(&BB, Tail);
1144 }
1145
1146 // Insert a branch based on __THREW__ variable
1147 Value *Cmp = IRB.CreateICmpEQ(Threw, getAddrSizeInt(&M, 1), "cmp");
1148 IRB.CreateCondBr(Cmp, II->getUnwindDest(), II->getNormalDest());
1149
1150 } else {
1151 // This can't throw, and we don't need this invoke, just replace it with a
1152 // call+branch
1154 }
1155 }
1156
1157 // Process resume instructions
1158 for (BasicBlock &BB : F) {
1159 // Scan the body of the basic block for resumes
1160 for (Instruction &I : BB) {
1161 auto *RI = dyn_cast<ResumeInst>(&I);
1162 if (!RI)
1163 continue;
1164 Changed = true;
1165
1166 // Split the input into legal values
1167 Value *Input = RI->getValue();
1168 IRB.SetInsertPoint(RI);
1169 Value *Low = IRB.CreateExtractValue(Input, 0, "low");
1170 // Create a call to __resumeException function
1171 IRB.CreateCall(ResumeF, {Low});
1172 // Add a terminator to the block
1173 IRB.CreateUnreachable();
1174 ToErase.push_back(RI);
1175 }
1176 }
1177
1178 // Process llvm.eh.typeid.for intrinsics
1179 for (BasicBlock &BB : F) {
1180 for (Instruction &I : BB) {
1181 auto *CI = dyn_cast<CallInst>(&I);
1182 if (!CI)
1183 continue;
1184 const Function *Callee = CI->getCalledFunction();
1185 if (!Callee)
1186 continue;
1187 if (Callee->getIntrinsicID() != Intrinsic::eh_typeid_for)
1188 continue;
1189 Changed = true;
1190
1191 IRB.SetInsertPoint(CI);
1192 CallInst *NewCI =
1193 IRB.CreateCall(EHTypeIDF, CI->getArgOperand(0), "typeid");
1194 CI->replaceAllUsesWith(NewCI);
1195 ToErase.push_back(CI);
1196 }
1197 }
1198
1199 // Look for orphan landingpads, can occur in blocks with no predecessors
1200 for (BasicBlock &BB : F) {
1201 Instruction *I = BB.getFirstNonPHI();
1202 if (auto *LPI = dyn_cast<LandingPadInst>(I))
1203 LandingPads.insert(LPI);
1204 }
1205 Changed |= !LandingPads.empty();
1206
1207 // Handle all the landingpad for this function together, as multiple invokes
1208 // may share a single lp
1209 for (LandingPadInst *LPI : LandingPads) {
1210 IRB.SetInsertPoint(LPI);
1212 for (unsigned I = 0, E = LPI->getNumClauses(); I < E; ++I) {
1213 Constant *Clause = LPI->getClause(I);
1214 // TODO Handle filters (= exception specifications).
1215 // https://github.com/llvm/llvm-project/issues/49740
1216 if (LPI->isCatch(I))
1217 FMCArgs.push_back(Clause);
1218 }
1219
1220 // Create a call to __cxa_find_matching_catch_N function
1221 Function *FMCF = getFindMatchingCatch(M, FMCArgs.size());
1222 CallInst *FMCI = IRB.CreateCall(FMCF, FMCArgs, "fmc");
1223 Value *Poison = PoisonValue::get(LPI->getType());
1224 Value *Pair0 = IRB.CreateInsertValue(Poison, FMCI, 0, "pair0");
1225 Value *TempRet0 = IRB.CreateCall(GetTempRet0F, std::nullopt, "tempret0");
1226 Value *Pair1 = IRB.CreateInsertValue(Pair0, TempRet0, 1, "pair1");
1227
1228 LPI->replaceAllUsesWith(Pair1);
1229 ToErase.push_back(LPI);
1230 }
1231
1232 // Erase everything we no longer need in this function
1233 for (Instruction *I : ToErase)
1234 I->eraseFromParent();
1235
1236 return Changed;
1237}
1238
1239// This tries to get debug info from the instruction before which a new
1240// instruction will be inserted, and if there's no debug info in that
1241// instruction, tries to get the info instead from the previous instruction (if
1242// any). If none of these has debug info and a DISubprogram is provided, it
1243// creates a dummy debug info with the first line of the function, because IR
1244// verifier requires all inlinable callsites should have debug info when both a
1245// caller and callee have DISubprogram. If none of these conditions are met,
1246// returns empty info.
1247static DebugLoc getOrCreateDebugLoc(const Instruction *InsertBefore,
1248 DISubprogram *SP) {
1249 assert(InsertBefore);
1250 if (InsertBefore->getDebugLoc())
1251 return InsertBefore->getDebugLoc();
1252 const Instruction *Prev = InsertBefore->getPrevNode();
1253 if (Prev && Prev->getDebugLoc())
1254 return Prev->getDebugLoc();
1255 if (SP)
1256 return DILocation::get(SP->getContext(), SP->getLine(), 1, SP);
1257 return DebugLoc();
1258}
1259
1260bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) {
1261 assert(EnableEmSjLj || EnableWasmSjLj);
1262 Module &M = *F.getParent();
1263 LLVMContext &C = F.getContext();
1264 IRBuilder<> IRB(C);
1266
1267 // Setjmp preparation
1268
1269 BasicBlock *Entry = &F.getEntryBlock();
1270 DebugLoc FirstDL = getOrCreateDebugLoc(&*Entry->begin(), F.getSubprogram());
1271 SplitBlock(Entry, &*Entry->getFirstInsertionPt());
1272
1273 IRB.SetInsertPoint(Entry->getTerminator()->getIterator());
1274 // This alloca'ed pointer is used by the runtime to identify function
1275 // invocations. It's just for pointer comparisons. It will never be
1276 // dereferenced.
1277 Instruction *FunctionInvocationId =
1278 IRB.CreateAlloca(IRB.getInt32Ty(), nullptr, "functionInvocationId");
1279 FunctionInvocationId->setDebugLoc(FirstDL);
1280
1281 // Setjmp transformation
1282 SmallVector<PHINode *, 4> SetjmpRetPHIs;
1283 Function *SetjmpF = M.getFunction("setjmp");
1284 for (auto *U : make_early_inc_range(SetjmpF->users())) {
1285 auto *CB = cast<CallBase>(U);
1286 BasicBlock *BB = CB->getParent();
1287 if (BB->getParent() != &F) // in other function
1288 continue;
1289 if (CB->getOperandBundle(LLVMContext::OB_funclet)) {
1290 std::string S;
1292 SS << "In function " + F.getName() +
1293 ": setjmp within a catch clause is not supported in Wasm EH:\n";
1294 SS << *CB;
1296 }
1297
1298 CallInst *CI = nullptr;
1299 // setjmp cannot throw. So if it is an invoke, lower it to a call
1300 if (auto *II = dyn_cast<InvokeInst>(CB))
1301 CI = llvm::changeToCall(II);
1302 else
1303 CI = cast<CallInst>(CB);
1304
1305 // The tail is everything right after the call, and will be reached once
1306 // when setjmp is called, and later when longjmp returns to the setjmp
1307 BasicBlock *Tail = SplitBlock(BB, CI->getNextNode());
1308 // Add a phi to the tail, which will be the output of setjmp, which
1309 // indicates if this is the first call or a longjmp back. The phi directly
1310 // uses the right value based on where we arrive from
1311 IRB.SetInsertPoint(Tail, Tail->getFirstNonPHIIt());
1312 PHINode *SetjmpRet = IRB.CreatePHI(IRB.getInt32Ty(), 2, "setjmp.ret");
1313
1314 // setjmp initial call returns 0
1315 SetjmpRet->addIncoming(IRB.getInt32(0), BB);
1316 // The proper output is now this, not the setjmp call itself
1317 CI->replaceAllUsesWith(SetjmpRet);
1318 // longjmp returns to the setjmp will add themselves to this phi
1319 SetjmpRetPHIs.push_back(SetjmpRet);
1320
1321 // Fix call target
1322 // Our index in the function is our place in the array + 1 to avoid index
1323 // 0, because index 0 means the longjmp is not ours to handle.
1324 IRB.SetInsertPoint(CI);
1325 Value *Args[] = {CI->getArgOperand(0), IRB.getInt32(SetjmpRetPHIs.size()),
1326 FunctionInvocationId};
1327 IRB.CreateCall(WasmSetjmpF, Args);
1328 ToErase.push_back(CI);
1329 }
1330
1331 // Handle longjmpable calls.
1332 if (EnableEmSjLj)
1333 handleLongjmpableCallsForEmscriptenSjLj(F, FunctionInvocationId,
1334 SetjmpRetPHIs);
1335 else // EnableWasmSjLj
1336 handleLongjmpableCallsForWasmSjLj(F, FunctionInvocationId, SetjmpRetPHIs);
1337
1338 // Erase everything we no longer need in this function
1339 for (Instruction *I : ToErase)
1340 I->eraseFromParent();
1341
1342 // Finally, our modifications to the cfg can break dominance of SSA variables.
1343 // For example, in this code,
1344 // if (x()) { .. setjmp() .. }
1345 // if (y()) { .. longjmp() .. }
1346 // We must split the longjmp block, and it can jump into the block splitted
1347 // from setjmp one. But that means that when we split the setjmp block, it's
1348 // first part no longer dominates its second part - there is a theoretically
1349 // possible control flow path where x() is false, then y() is true and we
1350 // reach the second part of the setjmp block, without ever reaching the first
1351 // part. So, we rebuild SSA form here.
1352 rebuildSSA(F);
1353 return true;
1354}
1355
1356// Update each call that can longjmp so it can return to the corresponding
1357// setjmp. Refer to 4) of "Emscripten setjmp/longjmp handling" section in the
1358// comments at top of the file for details.
1359void WebAssemblyLowerEmscriptenEHSjLj::handleLongjmpableCallsForEmscriptenSjLj(
1360 Function &F, Instruction *FunctionInvocationId,
1361 SmallVectorImpl<PHINode *> &SetjmpRetPHIs) {
1362 Module &M = *F.getParent();
1363 LLVMContext &C = F.getContext();
1364 IRBuilder<> IRB(C);
1366
1367 // call.em.longjmp BB that will be shared within the function.
1368 BasicBlock *CallEmLongjmpBB = nullptr;
1369 // PHI node for the loaded value of __THREW__ global variable in
1370 // call.em.longjmp BB
1371 PHINode *CallEmLongjmpBBThrewPHI = nullptr;
1372 // PHI node for the loaded value of __threwValue global variable in
1373 // call.em.longjmp BB
1374 PHINode *CallEmLongjmpBBThrewValuePHI = nullptr;
1375 // rethrow.exn BB that will be shared within the function.
1376 BasicBlock *RethrowExnBB = nullptr;
1377
1378 // Because we are creating new BBs while processing and don't want to make
1379 // all these newly created BBs candidates again for longjmp processing, we
1380 // first make the vector of candidate BBs.
1381 std::vector<BasicBlock *> BBs;
1382 for (BasicBlock &BB : F)
1383 BBs.push_back(&BB);
1384
1385 // BBs.size() will change within the loop, so we query it every time
1386 for (unsigned I = 0; I < BBs.size(); I++) {
1387 BasicBlock *BB = BBs[I];
1388 for (Instruction &I : *BB) {
1389 if (isa<InvokeInst>(&I)) {
1390 std::string S;
1392 SS << "In function " << F.getName()
1393 << ": When using Wasm EH with Emscripten SjLj, there is a "
1394 "restriction that `setjmp` function call and exception cannot be "
1395 "used within the same function:\n";
1396 SS << I;
1398 }
1399 auto *CI = dyn_cast<CallInst>(&I);
1400 if (!CI)
1401 continue;
1402
1403 const Value *Callee = CI->getCalledOperand();
1404 if (!canLongjmp(Callee))
1405 continue;
1406 if (isEmAsmCall(Callee))
1407 report_fatal_error("Cannot use EM_ASM* alongside setjmp/longjmp in " +
1408 F.getName() +
1409 ". Please consider using EM_JS, or move the "
1410 "EM_ASM into another function.",
1411 false);
1412
1413 Value *Threw = nullptr;
1415 if (Callee->getName().starts_with("__invoke_")) {
1416 // If invoke wrapper has already been generated for this call in
1417 // previous EH phase, search for the load instruction
1418 // %__THREW__.val = __THREW__;
1419 // in postamble after the invoke wrapper call
1420 LoadInst *ThrewLI = nullptr;
1421 StoreInst *ThrewResetSI = nullptr;
1422 for (auto I = std::next(BasicBlock::iterator(CI)), IE = BB->end();
1423 I != IE; ++I) {
1424 if (auto *LI = dyn_cast<LoadInst>(I))
1425 if (auto *GV = dyn_cast<GlobalVariable>(LI->getPointerOperand()))
1426 if (GV == ThrewGV) {
1427 Threw = ThrewLI = LI;
1428 break;
1429 }
1430 }
1431 // Search for the store instruction after the load above
1432 // __THREW__ = 0;
1433 for (auto I = std::next(BasicBlock::iterator(ThrewLI)), IE = BB->end();
1434 I != IE; ++I) {
1435 if (auto *SI = dyn_cast<StoreInst>(I)) {
1436 if (auto *GV = dyn_cast<GlobalVariable>(SI->getPointerOperand())) {
1437 if (GV == ThrewGV &&
1438 SI->getValueOperand() == getAddrSizeInt(&M, 0)) {
1439 ThrewResetSI = SI;
1440 break;
1441 }
1442 }
1443 }
1444 }
1445 assert(Threw && ThrewLI && "Cannot find __THREW__ load after invoke");
1446 assert(ThrewResetSI && "Cannot find __THREW__ store after invoke");
1447 Tail = SplitBlock(BB, ThrewResetSI->getNextNode());
1448
1449 } else {
1450 // Wrap call with invoke wrapper and generate preamble/postamble
1451 Threw = wrapInvoke(CI);
1452 ToErase.push_back(CI);
1453 Tail = SplitBlock(BB, CI->getNextNode());
1454
1455 // If exception handling is enabled, the thrown value can be not a
1456 // longjmp but an exception, in which case we shouldn't silently ignore
1457 // exceptions; we should rethrow them.
1458 // __THREW__'s value is 0 when nothing happened, 1 when an exception is
1459 // thrown, other values when longjmp is thrown.
1460 //
1461 // if (%__THREW__.val == 1)
1462 // goto %eh.rethrow
1463 // else
1464 // goto %normal
1465 //
1466 // eh.rethrow: ;; Rethrow exception
1467 // %exn = call @__cxa_find_matching_catch_2() ;; Retrieve thrown ptr
1468 // __resumeException(%exn)
1469 //
1470 // normal:
1471 // <-- Insertion point. Will insert sjlj handling code from here
1472 // goto %tail
1473 //
1474 // tail:
1475 // ...
1476 if (supportsException(&F) && canThrow(Callee)) {
1477 // We will add a new conditional branch. So remove the branch created
1478 // when we split the BB
1479 ToErase.push_back(BB->getTerminator());
1480
1481 // Generate rethrow.exn BB once and share it within the function
1482 if (!RethrowExnBB) {
1483 RethrowExnBB = BasicBlock::Create(C, "rethrow.exn", &F);
1484 IRB.SetInsertPoint(RethrowExnBB);
1485 CallInst *Exn =
1486 IRB.CreateCall(getFindMatchingCatch(M, 0), {}, "exn");
1487 IRB.CreateCall(ResumeF, {Exn});
1488 IRB.CreateUnreachable();
1489 }
1490
1491 IRB.SetInsertPoint(CI);
1492 BasicBlock *NormalBB = BasicBlock::Create(C, "normal", &F);
1493 Value *CmpEqOne =
1494 IRB.CreateICmpEQ(Threw, getAddrSizeInt(&M, 1), "cmp.eq.one");
1495 IRB.CreateCondBr(CmpEqOne, RethrowExnBB, NormalBB);
1496
1497 IRB.SetInsertPoint(NormalBB);
1498 IRB.CreateBr(Tail);
1499 BB = NormalBB; // New insertion point to insert __wasm_setjmp_test()
1500 }
1501 }
1502
1503 // We need to replace the terminator in Tail - SplitBlock makes BB go
1504 // straight to Tail, we need to check if a longjmp occurred, and go to the
1505 // right setjmp-tail if so
1506 ToErase.push_back(BB->getTerminator());
1507
1508 // Generate a function call to __wasm_setjmp_test function and
1509 // preamble/postamble code to figure out (1) whether longjmp
1510 // occurred (2) if longjmp occurred, which setjmp it corresponds to
1511 Value *Label = nullptr;
1512 Value *LongjmpResult = nullptr;
1513 BasicBlock *EndBB = nullptr;
1514 wrapTestSetjmp(BB, CI->getDebugLoc(), Threw, FunctionInvocationId, Label,
1515 LongjmpResult, CallEmLongjmpBB, CallEmLongjmpBBThrewPHI,
1516 CallEmLongjmpBBThrewValuePHI, EndBB);
1517 assert(Label && LongjmpResult && EndBB);
1518
1519 // Create switch instruction
1520 IRB.SetInsertPoint(EndBB);
1521 IRB.SetCurrentDebugLocation(EndBB->back().getDebugLoc());
1522 SwitchInst *SI = IRB.CreateSwitch(Label, Tail, SetjmpRetPHIs.size());
1523 // -1 means no longjmp happened, continue normally (will hit the default
1524 // switch case). 0 means a longjmp that is not ours to handle, needs a
1525 // rethrow. Otherwise the index is the same as the index in P+1 (to avoid
1526 // 0).
1527 for (unsigned I = 0; I < SetjmpRetPHIs.size(); I++) {
1528 SI->addCase(IRB.getInt32(I + 1), SetjmpRetPHIs[I]->getParent());
1529 SetjmpRetPHIs[I]->addIncoming(LongjmpResult, EndBB);
1530 }
1531
1532 // We are splitting the block here, and must continue to find other calls
1533 // in the block - which is now split. so continue to traverse in the Tail
1534 BBs.push_back(Tail);
1535 }
1536 }
1537
1538 for (Instruction *I : ToErase)
1539 I->eraseFromParent();
1540}
1541
1543 for (const User *U : CPI->users())
1544 if (const auto *CRI = dyn_cast<CleanupReturnInst>(U))
1545 return CRI->getUnwindDest();
1546 return nullptr;
1547}
1548
1549// Create a catchpad in which we catch a longjmp's env and val arguments, test
1550// if the longjmp corresponds to one of setjmps in the current function, and if
1551// so, jump to the setjmp dispatch BB from which we go to one of post-setjmp
1552// BBs. Refer to 4) of "Wasm setjmp/longjmp handling" section in the comments at
1553// top of the file for details.
1554void WebAssemblyLowerEmscriptenEHSjLj::handleLongjmpableCallsForWasmSjLj(
1555 Function &F, Instruction *FunctionInvocationId,
1556 SmallVectorImpl<PHINode *> &SetjmpRetPHIs) {
1557 Module &M = *F.getParent();
1558 LLVMContext &C = F.getContext();
1559 IRBuilder<> IRB(C);
1560
1561 // A function with catchswitch/catchpad instruction should have a personality
1562 // function attached to it. Search for the wasm personality function, and if
1563 // it exists, use it, and if it doesn't, create a dummy personality function.
1564 // (SjLj is not going to call it anyway.)
1565 if (!F.hasPersonalityFn()) {
1566 StringRef PersName = getEHPersonalityName(EHPersonality::Wasm_CXX);
1567 FunctionType *PersType =
1568 FunctionType::get(IRB.getInt32Ty(), /* isVarArg */ true);
1569 Value *PersF = M.getOrInsertFunction(PersName, PersType).getCallee();
1570 F.setPersonalityFn(
1571 cast<Constant>(IRB.CreateBitCast(PersF, IRB.getPtrTy())));
1572 }
1573
1574 // Use the entry BB's debugloc as a fallback
1575 BasicBlock *Entry = &F.getEntryBlock();
1576 DebugLoc FirstDL = getOrCreateDebugLoc(&*Entry->begin(), F.getSubprogram());
1577 IRB.SetCurrentDebugLocation(FirstDL);
1578
1579 // Add setjmp.dispatch BB right after the entry block. Because we have
1580 // initialized functionInvocationId in the entry block and split the
1581 // rest into another BB, here 'OrigEntry' is the function's original entry
1582 // block before the transformation.
1583 //
1584 // entry:
1585 // functionInvocationId initialization
1586 // setjmp.dispatch:
1587 // switch will be inserted here later
1588 // entry.split: (OrigEntry)
1589 // the original function starts here
1590 BasicBlock *OrigEntry = Entry->getNextNode();
1591 BasicBlock *SetjmpDispatchBB =
1592 BasicBlock::Create(C, "setjmp.dispatch", &F, OrigEntry);
1593 cast<BranchInst>(Entry->getTerminator())->setSuccessor(0, SetjmpDispatchBB);
1594
1595 // Create catch.dispatch.longjmp BB and a catchswitch instruction
1596 BasicBlock *CatchDispatchLongjmpBB =
1597 BasicBlock::Create(C, "catch.dispatch.longjmp", &F);
1598 IRB.SetInsertPoint(CatchDispatchLongjmpBB);
1599 CatchSwitchInst *CatchSwitchLongjmp =
1600 IRB.CreateCatchSwitch(ConstantTokenNone::get(C), nullptr, 1);
1601
1602 // Create catch.longjmp BB and a catchpad instruction
1603 BasicBlock *CatchLongjmpBB = BasicBlock::Create(C, "catch.longjmp", &F);
1604 CatchSwitchLongjmp->addHandler(CatchLongjmpBB);
1605 IRB.SetInsertPoint(CatchLongjmpBB);
1606 CatchPadInst *CatchPad = IRB.CreateCatchPad(CatchSwitchLongjmp, {});
1607
1608 // Wasm throw and catch instructions can throw and catch multiple values, but
1609 // that requires multivalue support in the toolchain, which is currently not
1610 // very reliable. We instead throw and catch a pointer to a struct value of
1611 // type 'struct __WasmLongjmpArgs', which is defined in Emscripten.
1612 Instruction *LongjmpArgs =
1613 IRB.CreateCall(CatchF, {IRB.getInt32(WebAssembly::C_LONGJMP)}, "thrown");
1614 Value *EnvField =
1615 IRB.CreateConstGEP2_32(LongjmpArgsTy, LongjmpArgs, 0, 0, "env_gep");
1616 Value *ValField =
1617 IRB.CreateConstGEP2_32(LongjmpArgsTy, LongjmpArgs, 0, 1, "val_gep");
1618 // void *env = __wasm_longjmp_args.env;
1619 Instruction *Env = IRB.CreateLoad(IRB.getPtrTy(), EnvField, "env");
1620 // int val = __wasm_longjmp_args.val;
1621 Instruction *Val = IRB.CreateLoad(IRB.getInt32Ty(), ValField, "val");
1622
1623 // %label = __wasm_setjmp_test(%env, functionInvocatinoId);
1624 // if (%label == 0)
1625 // __wasm_longjmp(%env, %val)
1626 // catchret to %setjmp.dispatch
1627 BasicBlock *ThenBB = BasicBlock::Create(C, "if.then", &F);
1628 BasicBlock *EndBB = BasicBlock::Create(C, "if.end", &F);
1629 Value *EnvP = IRB.CreateBitCast(Env, getAddrPtrType(&M), "env.p");
1630 Value *Label = IRB.CreateCall(WasmSetjmpTestF, {EnvP, FunctionInvocationId},
1631 OperandBundleDef("funclet", CatchPad), "label");
1632 Value *Cmp = IRB.CreateICmpEQ(Label, IRB.getInt32(0));
1633 IRB.CreateCondBr(Cmp, ThenBB, EndBB);
1634
1635 IRB.SetInsertPoint(ThenBB);
1636 CallInst *WasmLongjmpCI = IRB.CreateCall(
1637 WasmLongjmpF, {Env, Val}, OperandBundleDef("funclet", CatchPad));
1638 IRB.CreateUnreachable();
1639
1640 IRB.SetInsertPoint(EndBB);
1641 // Jump to setjmp.dispatch block
1642 IRB.CreateCatchRet(CatchPad, SetjmpDispatchBB);
1643
1644 // Go back to setjmp.dispatch BB
1645 // setjmp.dispatch:
1646 // switch %label {
1647 // label 1: goto post-setjmp BB 1
1648 // label 2: goto post-setjmp BB 2
1649 // ...
1650 // default: goto splitted next BB
1651 // }
1652 IRB.SetInsertPoint(SetjmpDispatchBB);
1653 PHINode *LabelPHI = IRB.CreatePHI(IRB.getInt32Ty(), 2, "label.phi");
1654 LabelPHI->addIncoming(Label, EndBB);
1655 LabelPHI->addIncoming(IRB.getInt32(-1), Entry);
1656 SwitchInst *SI = IRB.CreateSwitch(LabelPHI, OrigEntry, SetjmpRetPHIs.size());
1657 // -1 means no longjmp happened, continue normally (will hit the default
1658 // switch case). 0 means a longjmp that is not ours to handle, needs a
1659 // rethrow. Otherwise the index is the same as the index in P+1 (to avoid
1660 // 0).
1661 for (unsigned I = 0; I < SetjmpRetPHIs.size(); I++) {
1662 SI->addCase(IRB.getInt32(I + 1), SetjmpRetPHIs[I]->getParent());
1663 SetjmpRetPHIs[I]->addIncoming(Val, SetjmpDispatchBB);
1664 }
1665
1666 // Convert all longjmpable call instructions to invokes that unwind to the
1667 // newly created catch.dispatch.longjmp BB.
1668 SmallVector<CallInst *, 64> LongjmpableCalls;
1669 for (auto *BB = &*F.begin(); BB; BB = BB->getNextNode()) {
1670 for (auto &I : *BB) {
1671 auto *CI = dyn_cast<CallInst>(&I);
1672 if (!CI)
1673 continue;
1674 const Value *Callee = CI->getCalledOperand();
1675 if (!canLongjmp(Callee))
1676 continue;
1677 if (isEmAsmCall(Callee))
1678 report_fatal_error("Cannot use EM_ASM* alongside setjmp/longjmp in " +
1679 F.getName() +
1680 ". Please consider using EM_JS, or move the "
1681 "EM_ASM into another function.",
1682 false);
1683 // This is __wasm_longjmp() call we inserted in this function, which
1684 // rethrows the longjmp when the longjmp does not correspond to one of
1685 // setjmps in this function. We should not convert this call to an invoke.
1686 if (CI == WasmLongjmpCI)
1687 continue;
1688 LongjmpableCalls.push_back(CI);
1689 }
1690 }
1691
1693 UnwindDestToNewPreds;
1694 for (auto *CI : LongjmpableCalls) {
1695 // Even if the callee function has attribute 'nounwind', which is true for
1696 // all C functions, it can longjmp, which means it can throw a Wasm
1697 // exception now.
1698 CI->removeFnAttr(Attribute::NoUnwind);
1699 if (Function *CalleeF = CI->getCalledFunction())
1700 CalleeF->removeFnAttr(Attribute::NoUnwind);
1701
1702 // Change it to an invoke and make it unwind to the catch.dispatch.longjmp
1703 // BB. If the call is enclosed in another catchpad/cleanuppad scope, unwind
1704 // to its parent pad's unwind destination instead to preserve the scope
1705 // structure. It will eventually unwind to the catch.dispatch.longjmp.
1707 BasicBlock *UnwindDest = nullptr;
1708 if (auto Bundle = CI->getOperandBundle(LLVMContext::OB_funclet)) {
1709 Instruction *FromPad = cast<Instruction>(Bundle->Inputs[0]);
1710 while (!UnwindDest) {
1711 if (auto *CPI = dyn_cast<CatchPadInst>(FromPad)) {
1712 UnwindDest = CPI->getCatchSwitch()->getUnwindDest();
1713 break;
1714 }
1715 if (auto *CPI = dyn_cast<CleanupPadInst>(FromPad)) {
1716 // getCleanupRetUnwindDest() can return nullptr when
1717 // 1. This cleanuppad's matching cleanupret uwninds to caller
1718 // 2. There is no matching cleanupret because it ends with
1719 // unreachable.
1720 // In case of 2, we need to traverse the parent pad chain.
1721 UnwindDest = getCleanupRetUnwindDest(CPI);
1722 Value *ParentPad = CPI->getParentPad();
1723 if (isa<ConstantTokenNone>(ParentPad))
1724 break;
1725 FromPad = cast<Instruction>(ParentPad);
1726 }
1727 }
1728 }
1729 if (!UnwindDest)
1730 UnwindDest = CatchDispatchLongjmpBB;
1731 // Because we are changing a longjmpable call to an invoke, its unwind
1732 // destination can be an existing EH pad that already have phis, and the BB
1733 // with the newly created invoke will become a new predecessor of that EH
1734 // pad. In this case we need to add the new predecessor to those phis.
1735 UnwindDestToNewPreds[UnwindDest].insert(CI->getParent());
1736 changeToInvokeAndSplitBasicBlock(CI, UnwindDest);
1737 }
1738
1740 for (auto &BB : F) {
1741 if (auto *CSI = dyn_cast<CatchSwitchInst>(BB.getFirstNonPHI())) {
1742 if (CSI != CatchSwitchLongjmp && CSI->unwindsToCaller()) {
1743 IRB.SetInsertPoint(CSI);
1744 ToErase.push_back(CSI);
1745 auto *NewCSI = IRB.CreateCatchSwitch(CSI->getParentPad(),
1746 CatchDispatchLongjmpBB, 1);
1747 NewCSI->addHandler(*CSI->handler_begin());
1748 NewCSI->takeName(CSI);
1749 CSI->replaceAllUsesWith(NewCSI);
1750 }
1751 }
1752
1753 if (auto *CRI = dyn_cast<CleanupReturnInst>(BB.getTerminator())) {
1754 if (CRI->unwindsToCaller()) {
1755 IRB.SetInsertPoint(CRI);
1756 ToErase.push_back(CRI);
1757 IRB.CreateCleanupRet(CRI->getCleanupPad(), CatchDispatchLongjmpBB);
1758 }
1759 }
1760 }
1761
1762 for (Instruction *I : ToErase)
1763 I->eraseFromParent();
1764
1765 // Add entries for new predecessors to phis in unwind destinations. We use
1766 // 'undef' as a placeholder value. We should make sure the phis have a valid
1767 // set of predecessors before running SSAUpdater, because SSAUpdater
1768 // internally can use existing phis to gather predecessor info rather than
1769 // scanning the actual CFG (See FindPredecessorBlocks in SSAUpdater.cpp for
1770 // details).
1771 for (auto &[UnwindDest, NewPreds] : UnwindDestToNewPreds) {
1772 for (PHINode &PN : UnwindDest->phis()) {
1773 for (auto *NewPred : NewPreds) {
1774 assert(PN.getBasicBlockIndex(NewPred) == -1);
1775 PN.addIncoming(UndefValue::get(PN.getType()), NewPred);
1776 }
1777 }
1778 }
1779
1780 // For unwind destinations for newly added invokes to longjmpable functions,
1781 // calculate incoming values for the newly added predecessors using
1782 // SSAUpdater. We add existing values in the phis to SSAUpdater as available
1783 // values and let it calculate what the value should be at the end of new
1784 // incoming blocks.
1785 for (auto &[UnwindDest, NewPreds] : UnwindDestToNewPreds) {
1786 for (PHINode &PN : UnwindDest->phis()) {
1788 SSA.Initialize(PN.getType(), PN.getName());
1789 for (unsigned Idx = 0, E = PN.getNumIncomingValues(); Idx != E; ++Idx) {
1790 if (NewPreds.contains(PN.getIncomingBlock(Idx)))
1791 continue;
1792 Value *V = PN.getIncomingValue(Idx);
1793 if (auto *II = dyn_cast<InvokeInst>(V))
1794 SSA.AddAvailableValue(II->getNormalDest(), II);
1795 else if (auto *I = dyn_cast<Instruction>(V))
1796 SSA.AddAvailableValue(I->getParent(), I);
1797 else
1798 SSA.AddAvailableValue(PN.getIncomingBlock(Idx), V);
1799 }
1800 for (auto *NewPred : NewPreds)
1801 PN.setIncomingValueForBlock(NewPred, SSA.GetValueAtEndOfBlock(NewPred));
1802 assert(PN.isComplete());
1803 }
1804 }
1805}
@ 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(X)
Definition: Debug.h:101
std::string Name
#define F(x, y, z)
Definition: MD5.cpp:55
#define I(x, y, z)
Definition: MD5.cpp:58
Memory SSA
Definition: MemorySSA.cpp:72
Module.h This file contains the declarations for the Module class.
uint64_t IntrinsicInst * II
const char LLVMTargetMachineRef TM
#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:842
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:507
static BasicBlock * Create(LLVMContext &Context, const Twine &Name="", Function *Parent=nullptr, BasicBlock *InsertBefore=nullptr)
Creates a new BasicBlock.
Definition: BasicBlock.h:202
const Function * getParent() const
Return the enclosing method, or null if none.
Definition: BasicBlock.h:209
InstListType::iterator iterator
Instruction iterators...
Definition: BasicBlock.h:167
const Instruction & back() const
Definition: BasicBlock.h:463
Base class for all callable instructions (InvokeInst and CallInst) Holds everything related to callin...
Definition: InstrTypes.h:1236
void setCallingConv(CallingConv::ID CC)
Definition: InstrTypes.h:1527
std::optional< OperandBundleUse > getOperandBundle(StringRef Name) const
Return an operand bundle by name, if present.
Definition: InstrTypes.h:2143
Function * getCalledFunction() const
Returns the function called, or null if this is an indirect function invocation or the function signa...
Definition: InstrTypes.h:1465
User::op_iterator arg_begin()
Return the iterator pointing to the beginning of the argument list.
Definition: InstrTypes.h:1385
Value * getCalledOperand() const
Definition: InstrTypes.h:1458
void setAttributes(AttributeList A)
Set the parameter attributes for this call.
Definition: InstrTypes.h:1546
Value * getArgOperand(unsigned i) const
Definition: InstrTypes.h:1410
User::op_iterator arg_end()
Return the iterator pointing to the end of the argument list.
Definition: InstrTypes.h:1391
FunctionType * getFunctionType() const
Definition: InstrTypes.h:1323
void removeFnAttr(Attribute::AttrKind Kind)
Removes the attribute from the function.
Definition: InstrTypes.h:1621
unsigned arg_size() const
Definition: InstrTypes.h:1408
AttributeList getAttributes() const
Return the parameter attributes for this call.
Definition: InstrTypes.h:1542
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:1500
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:151
std::pair< iterator, bool > insert(const std::pair< KeyT, ValueT > &KV)
Definition: DenseMap.h:220
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:629
static Function * Create(FunctionType *Ty, LinkageTypes Linkage, unsigned AddrSpace, const Twine &N="", Module *M=nullptr)
Definition: Function.h:165
FunctionType * getFunctionType() const
Returns the FunctionType for me.
Definition: Function.h:207
void setDoesNotThrow()
Definition: Function.h:589
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:2671
const DebugLoc & getDebugLoc() const
Return the debug location for this node as a DebugLoc.
Definition: Instruction.h:466
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:463
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:174
LLVMContext & getContext() const
Definition: Metadata.h:1231
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:301
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:1852
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:412
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:344
SmallPtrSet - This class implements a set which is optimized for holding SmallSize or less elements.
Definition: SmallPtrSet.h:479
size_t size() const
Definition: SmallVector.h:91
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
Definition: SmallVector.h:586
void append(ItTy in_start, ItTy in_end)
Add the specified range to the end of the SmallVector.
Definition: SmallVector.h:696
void push_back(const T &Elt)
Definition: SmallVector.h:426
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1209
An instruction for storing to memory.
Definition: Instructions.h:290
StringMap - This is an unconventional map that is specialized for handling keys that are "strings",...
Definition: StringMap.h:128
bool contains(StringRef Key) const
contains - Return true if the element is in the map, false otherwise.
Definition: StringMap.h:273
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:50
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:373
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:1833
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:811
@ 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 * getDeclaration(Module *M, ID id, ArrayRef< Type * > Tys=std::nullopt)
Create or insert an LLVM Function declaration for an intrinsic, and return it.
Definition: Function.cpp:1513
cl::opt< bool > WasmEnableSjLj
cl::opt< bool > WasmEnableEmEH
cl::opt< bool > WasmEnableEmSjLj
@ SS
Definition: X86.h:211
@ 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:2923
CallInst * changeToCall(InvokeInst *II, DomTreeUpdater *DTU=nullptr)
This function converts the specified invoke into a normal call.
Definition: Local.cpp:2903
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:656
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
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:2051