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