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