xref: /freebsd/contrib/llvm-project/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp (revision 23408297fbf3089f0388a8873b02fa75ab3f5bb9)
10b57cec5SDimitry Andric //=== WebAssemblyLowerEmscriptenEHSjLj.cpp - Lower exceptions for Emscripten =//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric ///
90b57cec5SDimitry Andric /// \file
100b57cec5SDimitry Andric /// This file lowers exception-related instructions and setjmp/longjmp
110b57cec5SDimitry Andric /// function calls in order to use Emscripten's JavaScript try and catch
120b57cec5SDimitry Andric /// mechanism.
130b57cec5SDimitry Andric ///
140b57cec5SDimitry Andric /// To handle exceptions and setjmp/longjmps, this scheme relies on JavaScript's
150b57cec5SDimitry Andric /// try and catch syntax and relevant exception-related libraries implemented
160b57cec5SDimitry Andric /// in JavaScript glue code that will be produced by Emscripten. This is similar
170b57cec5SDimitry Andric /// to the current Emscripten asm.js exception handling in fastcomp. For
180b57cec5SDimitry Andric /// fastcomp's EH / SjLj scheme, see these files in fastcomp LLVM branch:
190b57cec5SDimitry Andric /// (Location: https://github.com/kripken/emscripten-fastcomp)
200b57cec5SDimitry Andric /// lib/Target/JSBackend/NaCl/LowerEmExceptionsPass.cpp
210b57cec5SDimitry Andric /// lib/Target/JSBackend/NaCl/LowerEmSetjmp.cpp
220b57cec5SDimitry Andric /// lib/Target/JSBackend/JSBackend.cpp
230b57cec5SDimitry Andric /// lib/Target/JSBackend/CallHandlers.h
240b57cec5SDimitry Andric ///
250b57cec5SDimitry Andric /// * Exception handling
260b57cec5SDimitry Andric /// This pass lowers invokes and landingpads into library functions in JS glue
270b57cec5SDimitry Andric /// code. Invokes are lowered into function wrappers called invoke wrappers that
280b57cec5SDimitry Andric /// exist in JS side, which wraps the original function call with JS try-catch.
290b57cec5SDimitry Andric /// If an exception occurred, cxa_throw() function in JS side sets some
300b57cec5SDimitry Andric /// variables (see below) so we can check whether an exception occurred from
310b57cec5SDimitry Andric /// wasm code and handle it appropriately.
320b57cec5SDimitry Andric ///
330b57cec5SDimitry Andric /// * Setjmp-longjmp handling
340b57cec5SDimitry Andric /// This pass lowers setjmp to a reasonably-performant approach for emscripten.
350b57cec5SDimitry Andric /// The idea is that each block with a setjmp is broken up into two parts: the
360b57cec5SDimitry Andric /// part containing setjmp and the part right after the setjmp. The latter part
370b57cec5SDimitry Andric /// is either reached from the setjmp, or later from a longjmp. To handle the
380b57cec5SDimitry Andric /// longjmp, all calls that might longjmp are also called using invoke wrappers
390b57cec5SDimitry Andric /// and thus JS / try-catch. JS longjmp() function also sets some variables so
400b57cec5SDimitry Andric /// we can check / whether a longjmp occurred from wasm code. Each block with a
410b57cec5SDimitry Andric /// function call that might longjmp is also split up after the longjmp call.
420b57cec5SDimitry Andric /// After the longjmp call, we check whether a longjmp occurred, and if it did,
430b57cec5SDimitry Andric /// which setjmp it corresponds to, and jump to the right post-setjmp block.
440b57cec5SDimitry Andric /// We assume setjmp-longjmp handling always run after EH handling, which means
450b57cec5SDimitry Andric /// we don't expect any exception-related instructions when SjLj runs.
460b57cec5SDimitry Andric /// FIXME Currently this scheme does not support indirect call of setjmp,
470b57cec5SDimitry Andric /// because of the limitation of the scheme itself. fastcomp does not support it
480b57cec5SDimitry Andric /// either.
490b57cec5SDimitry Andric ///
500b57cec5SDimitry Andric /// In detail, this pass does following things:
510b57cec5SDimitry Andric ///
520b57cec5SDimitry Andric /// 1) Assumes the existence of global variables: __THREW__, __threwValue
530b57cec5SDimitry Andric ///    __THREW__ and __threwValue will be set in invoke wrappers
540b57cec5SDimitry Andric ///    in JS glue code. For what invoke wrappers are, refer to 3). These
550b57cec5SDimitry Andric ///    variables are used for both exceptions and setjmp/longjmps.
560b57cec5SDimitry Andric ///    __THREW__ indicates whether an exception or a longjmp occurred or not. 0
570b57cec5SDimitry Andric ///    means nothing occurred, 1 means an exception occurred, and other numbers
580b57cec5SDimitry Andric ///    mean a longjmp occurred. In the case of longjmp, __threwValue variable
590b57cec5SDimitry Andric ///    indicates the corresponding setjmp buffer the longjmp corresponds to.
600b57cec5SDimitry Andric ///
610b57cec5SDimitry Andric /// * Exception handling
620b57cec5SDimitry Andric ///
630b57cec5SDimitry Andric /// 2) We assume the existence of setThrew and setTempRet0/getTempRet0 functions
640b57cec5SDimitry Andric ///    at link time.
650b57cec5SDimitry Andric ///    The global variables in 1) will exist in wasm address space,
660b57cec5SDimitry Andric ///    but their values should be set in JS code, so these functions
670b57cec5SDimitry Andric ///    as interfaces to JS glue code. These functions are equivalent to the
680b57cec5SDimitry Andric ///    following JS functions, which actually exist in asm.js version of JS
690b57cec5SDimitry Andric ///    library.
700b57cec5SDimitry Andric ///
710b57cec5SDimitry Andric ///    function setThrew(threw, value) {
720b57cec5SDimitry Andric ///      if (__THREW__ == 0) {
730b57cec5SDimitry Andric ///        __THREW__ = threw;
740b57cec5SDimitry Andric ///        __threwValue = value;
750b57cec5SDimitry Andric ///      }
760b57cec5SDimitry Andric ///    }
770b57cec5SDimitry Andric //
780b57cec5SDimitry Andric ///    setTempRet0 is called from __cxa_find_matching_catch() in JS glue code.
790b57cec5SDimitry Andric ///
800b57cec5SDimitry Andric ///    In exception handling, getTempRet0 indicates the type of an exception
810b57cec5SDimitry Andric ///    caught, and in setjmp/longjmp, it means the second argument to longjmp
820b57cec5SDimitry Andric ///    function.
830b57cec5SDimitry Andric ///
840b57cec5SDimitry Andric /// 3) Lower
850b57cec5SDimitry Andric ///      invoke @func(arg1, arg2) to label %invoke.cont unwind label %lpad
860b57cec5SDimitry Andric ///    into
870b57cec5SDimitry Andric ///      __THREW__ = 0;
880b57cec5SDimitry Andric ///      call @__invoke_SIG(func, arg1, arg2)
890b57cec5SDimitry Andric ///      %__THREW__.val = __THREW__;
900b57cec5SDimitry Andric ///      __THREW__ = 0;
910b57cec5SDimitry Andric ///      if (%__THREW__.val == 1)
920b57cec5SDimitry Andric ///        goto %lpad
930b57cec5SDimitry Andric ///      else
940b57cec5SDimitry Andric ///         goto %invoke.cont
950b57cec5SDimitry Andric ///    SIG is a mangled string generated based on the LLVM IR-level function
960b57cec5SDimitry Andric ///    signature. After LLVM IR types are lowered to the target wasm types,
970b57cec5SDimitry Andric ///    the names for these wrappers will change based on wasm types as well,
980b57cec5SDimitry Andric ///    as in invoke_vi (function takes an int and returns void). The bodies of
990b57cec5SDimitry Andric ///    these wrappers will be generated in JS glue code, and inside those
1000b57cec5SDimitry Andric ///    wrappers we use JS try-catch to generate actual exception effects. It
1010b57cec5SDimitry Andric ///    also calls the original callee function. An example wrapper in JS code
1020b57cec5SDimitry Andric ///    would look like this:
1030b57cec5SDimitry Andric ///      function invoke_vi(index,a1) {
1040b57cec5SDimitry Andric ///        try {
1050b57cec5SDimitry Andric ///          Module["dynCall_vi"](index,a1); // This calls original callee
1060b57cec5SDimitry Andric ///        } catch(e) {
1070b57cec5SDimitry Andric ///          if (typeof e !== 'number' && e !== 'longjmp') throw e;
1080b57cec5SDimitry Andric ///          asm["setThrew"](1, 0); // setThrew is called here
1090b57cec5SDimitry Andric ///        }
1100b57cec5SDimitry Andric ///      }
1110b57cec5SDimitry Andric ///    If an exception is thrown, __THREW__ will be set to true in a wrapper,
1120b57cec5SDimitry Andric ///    so we can jump to the right BB based on this value.
1130b57cec5SDimitry Andric ///
1140b57cec5SDimitry Andric /// 4) Lower
1150b57cec5SDimitry Andric ///      %val = landingpad catch c1 catch c2 catch c3 ...
1160b57cec5SDimitry Andric ///      ... use %val ...
1170b57cec5SDimitry Andric ///    into
1180b57cec5SDimitry Andric ///      %fmc = call @__cxa_find_matching_catch_N(c1, c2, c3, ...)
1190b57cec5SDimitry Andric ///      %val = {%fmc, getTempRet0()}
1200b57cec5SDimitry Andric ///      ... use %val ...
1210b57cec5SDimitry Andric ///    Here N is a number calculated based on the number of clauses.
1220b57cec5SDimitry Andric ///    setTempRet0 is called from __cxa_find_matching_catch() in JS glue code.
1230b57cec5SDimitry Andric ///
1240b57cec5SDimitry Andric /// 5) Lower
1250b57cec5SDimitry Andric ///      resume {%a, %b}
1260b57cec5SDimitry Andric ///    into
1270b57cec5SDimitry Andric ///      call @__resumeException(%a)
1280b57cec5SDimitry Andric ///    where __resumeException() is a function in JS glue code.
1290b57cec5SDimitry Andric ///
1300b57cec5SDimitry Andric /// 6) Lower
1310b57cec5SDimitry Andric ///      call @llvm.eh.typeid.for(type) (intrinsic)
1320b57cec5SDimitry Andric ///    into
1330b57cec5SDimitry Andric ///      call @llvm_eh_typeid_for(type)
1340b57cec5SDimitry Andric ///    llvm_eh_typeid_for function will be generated in JS glue code.
1350b57cec5SDimitry Andric ///
1360b57cec5SDimitry Andric /// * Setjmp / Longjmp handling
1370b57cec5SDimitry Andric ///
1380b57cec5SDimitry Andric /// In case calls to longjmp() exists
1390b57cec5SDimitry Andric ///
1400b57cec5SDimitry Andric /// 1) Lower
1410b57cec5SDimitry Andric ///      longjmp(buf, value)
1420b57cec5SDimitry Andric ///    into
143e8d8bef9SDimitry Andric ///      emscripten_longjmp(buf, value)
1440b57cec5SDimitry Andric ///
1450b57cec5SDimitry Andric /// In case calls to setjmp() exists
1460b57cec5SDimitry Andric ///
1470b57cec5SDimitry Andric /// 2) In the function entry that calls setjmp, initialize setjmpTable and
1480b57cec5SDimitry Andric ///    sejmpTableSize as follows:
1490b57cec5SDimitry Andric ///      setjmpTableSize = 4;
1500b57cec5SDimitry Andric ///      setjmpTable = (int *) malloc(40);
1510b57cec5SDimitry Andric ///      setjmpTable[0] = 0;
1520b57cec5SDimitry Andric ///    setjmpTable and setjmpTableSize are used in saveSetjmp() function in JS
1530b57cec5SDimitry Andric ///    code.
1540b57cec5SDimitry Andric ///
1550b57cec5SDimitry Andric /// 3) Lower
1560b57cec5SDimitry Andric ///      setjmp(buf)
1570b57cec5SDimitry Andric ///    into
1580b57cec5SDimitry Andric ///      setjmpTable = saveSetjmp(buf, label, setjmpTable, setjmpTableSize);
1590b57cec5SDimitry Andric ///      setjmpTableSize = getTempRet0();
1600b57cec5SDimitry Andric ///    For each dynamic setjmp call, setjmpTable stores its ID (a number which
1610b57cec5SDimitry Andric ///    is incrementally assigned from 0) and its label (a unique number that
1620b57cec5SDimitry Andric ///    represents each callsite of setjmp). When we need more entries in
1630b57cec5SDimitry Andric ///    setjmpTable, it is reallocated in saveSetjmp() in JS code and it will
1640b57cec5SDimitry Andric ///    return the new table address, and assign the new table size in
1650b57cec5SDimitry Andric ///    setTempRet0(). saveSetjmp also stores the setjmp's ID into the buffer
1660b57cec5SDimitry Andric ///    buf. A BB with setjmp is split into two after setjmp call in order to
1670b57cec5SDimitry Andric ///    make the post-setjmp BB the possible destination of longjmp BB.
1680b57cec5SDimitry Andric ///
1690b57cec5SDimitry Andric ///
1700b57cec5SDimitry Andric /// 4) Lower every call that might longjmp into
1710b57cec5SDimitry Andric ///      __THREW__ = 0;
1720b57cec5SDimitry Andric ///      call @__invoke_SIG(func, arg1, arg2)
1730b57cec5SDimitry Andric ///      %__THREW__.val = __THREW__;
1740b57cec5SDimitry Andric ///      __THREW__ = 0;
1750b57cec5SDimitry Andric ///      if (%__THREW__.val != 0 & __threwValue != 0) {
1760b57cec5SDimitry Andric ///        %label = testSetjmp(mem[%__THREW__.val], setjmpTable,
1770b57cec5SDimitry Andric ///                            setjmpTableSize);
1780b57cec5SDimitry Andric ///        if (%label == 0)
1790b57cec5SDimitry Andric ///          emscripten_longjmp(%__THREW__.val, __threwValue);
1800b57cec5SDimitry Andric ///        setTempRet0(__threwValue);
1810b57cec5SDimitry Andric ///      } else {
1820b57cec5SDimitry Andric ///        %label = -1;
1830b57cec5SDimitry Andric ///      }
1840b57cec5SDimitry Andric ///      longjmp_result = getTempRet0();
1850b57cec5SDimitry Andric ///      switch label {
1860b57cec5SDimitry Andric ///        label 1: goto post-setjmp BB 1
1870b57cec5SDimitry Andric ///        label 2: goto post-setjmp BB 2
1880b57cec5SDimitry Andric ///        ...
1890b57cec5SDimitry Andric ///        default: goto splitted next BB
1900b57cec5SDimitry Andric ///      }
1910b57cec5SDimitry Andric ///    testSetjmp examines setjmpTable to see if there is a matching setjmp
1920b57cec5SDimitry Andric ///    call. After calling an invoke wrapper, if a longjmp occurred, __THREW__
1930b57cec5SDimitry Andric ///    will be the address of matching jmp_buf buffer and __threwValue be the
1940b57cec5SDimitry Andric ///    second argument to longjmp. mem[__THREW__.val] is a setjmp ID that is
1950b57cec5SDimitry Andric ///    stored in saveSetjmp. testSetjmp returns a setjmp label, a unique ID to
1960b57cec5SDimitry Andric ///    each setjmp callsite. Label 0 means this longjmp buffer does not
1970b57cec5SDimitry Andric ///    correspond to one of the setjmp callsites in this function, so in this
198e8d8bef9SDimitry Andric ///    case we just chain the longjmp to the caller. Label -1 means no longjmp
199e8d8bef9SDimitry Andric ///    occurred. Otherwise we jump to the right post-setjmp BB based on the
200e8d8bef9SDimitry Andric ///    label.
2010b57cec5SDimitry Andric ///
2020b57cec5SDimitry Andric ///===----------------------------------------------------------------------===//
2030b57cec5SDimitry Andric 
2040b57cec5SDimitry Andric #include "WebAssembly.h"
205e8d8bef9SDimitry Andric #include "WebAssemblyTargetMachine.h"
2065ffd83dbSDimitry Andric #include "llvm/ADT/StringExtras.h"
207e8d8bef9SDimitry Andric #include "llvm/CodeGen/TargetPassConfig.h"
2085ffd83dbSDimitry Andric #include "llvm/IR/DebugInfoMetadata.h"
2090b57cec5SDimitry Andric #include "llvm/IR/Dominators.h"
2100b57cec5SDimitry Andric #include "llvm/IR/IRBuilder.h"
211480093f4SDimitry Andric #include "llvm/Support/CommandLine.h"
2120b57cec5SDimitry Andric #include "llvm/Transforms/Utils/BasicBlockUtils.h"
2130b57cec5SDimitry Andric #include "llvm/Transforms/Utils/SSAUpdater.h"
2140b57cec5SDimitry Andric 
2150b57cec5SDimitry Andric using namespace llvm;
2160b57cec5SDimitry Andric 
2170b57cec5SDimitry Andric #define DEBUG_TYPE "wasm-lower-em-ehsjlj"
2180b57cec5SDimitry Andric 
2190b57cec5SDimitry Andric static cl::list<std::string>
2205ffd83dbSDimitry Andric     EHAllowlist("emscripten-cxx-exceptions-allowed",
2210b57cec5SDimitry Andric                 cl::desc("The list of function names in which Emscripten-style "
2220b57cec5SDimitry Andric                          "exception handling is enabled (see emscripten "
2235ffd83dbSDimitry Andric                          "EMSCRIPTEN_CATCHING_ALLOWED options)"),
2240b57cec5SDimitry Andric                 cl::CommaSeparated);
2250b57cec5SDimitry Andric 
2260b57cec5SDimitry Andric namespace {
2270b57cec5SDimitry Andric class WebAssemblyLowerEmscriptenEHSjLj final : public ModulePass {
2280b57cec5SDimitry Andric   bool EnableEH;   // Enable exception handling
2290b57cec5SDimitry Andric   bool EnableSjLj; // Enable setjmp/longjmp handling
2300b57cec5SDimitry Andric 
2310b57cec5SDimitry Andric   GlobalVariable *ThrewGV = nullptr;
2320b57cec5SDimitry Andric   GlobalVariable *ThrewValueGV = nullptr;
2330b57cec5SDimitry Andric   Function *GetTempRet0Func = nullptr;
2340b57cec5SDimitry Andric   Function *SetTempRet0Func = nullptr;
2350b57cec5SDimitry Andric   Function *ResumeF = nullptr;
2360b57cec5SDimitry Andric   Function *EHTypeIDF = nullptr;
2370b57cec5SDimitry Andric   Function *EmLongjmpF = nullptr;
2380b57cec5SDimitry Andric   Function *SaveSetjmpF = nullptr;
2390b57cec5SDimitry Andric   Function *TestSetjmpF = nullptr;
2400b57cec5SDimitry Andric 
2410b57cec5SDimitry Andric   // __cxa_find_matching_catch_N functions.
2420b57cec5SDimitry Andric   // Indexed by the number of clauses in an original landingpad instruction.
2430b57cec5SDimitry Andric   DenseMap<int, Function *> FindMatchingCatches;
2440b57cec5SDimitry Andric   // Map of <function signature string, invoke_ wrappers>
2450b57cec5SDimitry Andric   StringMap<Function *> InvokeWrappers;
2465ffd83dbSDimitry Andric   // Set of allowed function names for exception handling
2475ffd83dbSDimitry Andric   std::set<std::string> EHAllowlistSet;
2480b57cec5SDimitry Andric 
2490b57cec5SDimitry Andric   StringRef getPassName() const override {
2500b57cec5SDimitry Andric     return "WebAssembly Lower Emscripten Exceptions";
2510b57cec5SDimitry Andric   }
2520b57cec5SDimitry Andric 
2530b57cec5SDimitry Andric   bool runEHOnFunction(Function &F);
2540b57cec5SDimitry Andric   bool runSjLjOnFunction(Function &F);
2550b57cec5SDimitry Andric   Function *getFindMatchingCatch(Module &M, unsigned NumClauses);
2560b57cec5SDimitry Andric 
2575ffd83dbSDimitry Andric   Value *wrapInvoke(CallBase *CI);
2585ffd83dbSDimitry Andric   void wrapTestSetjmp(BasicBlock *BB, DebugLoc DL, Value *Threw,
2590b57cec5SDimitry Andric                       Value *SetjmpTable, Value *SetjmpTableSize, Value *&Label,
2600b57cec5SDimitry Andric                       Value *&LongjmpResult, BasicBlock *&EndBB);
2615ffd83dbSDimitry Andric   Function *getInvokeWrapper(CallBase *CI);
2620b57cec5SDimitry Andric 
2635ffd83dbSDimitry Andric   bool areAllExceptionsAllowed() const { return EHAllowlistSet.empty(); }
2640b57cec5SDimitry Andric   bool canLongjmp(Module &M, const Value *Callee) const;
2658bcb0991SDimitry Andric   bool isEmAsmCall(Module &M, const Value *Callee) const;
2660b57cec5SDimitry Andric 
2670b57cec5SDimitry Andric   void rebuildSSA(Function &F);
2680b57cec5SDimitry Andric 
2690b57cec5SDimitry Andric public:
2700b57cec5SDimitry Andric   static char ID;
2710b57cec5SDimitry Andric 
2720b57cec5SDimitry Andric   WebAssemblyLowerEmscriptenEHSjLj(bool EnableEH = true, bool EnableSjLj = true)
2730b57cec5SDimitry Andric       : ModulePass(ID), EnableEH(EnableEH), EnableSjLj(EnableSjLj) {
2745ffd83dbSDimitry Andric     EHAllowlistSet.insert(EHAllowlist.begin(), EHAllowlist.end());
2750b57cec5SDimitry Andric   }
2760b57cec5SDimitry Andric   bool runOnModule(Module &M) override;
2770b57cec5SDimitry Andric 
2780b57cec5SDimitry Andric   void getAnalysisUsage(AnalysisUsage &AU) const override {
2790b57cec5SDimitry Andric     AU.addRequired<DominatorTreeWrapperPass>();
2800b57cec5SDimitry Andric   }
2810b57cec5SDimitry Andric };
2820b57cec5SDimitry Andric } // End anonymous namespace
2830b57cec5SDimitry Andric 
2840b57cec5SDimitry Andric char WebAssemblyLowerEmscriptenEHSjLj::ID = 0;
2850b57cec5SDimitry Andric INITIALIZE_PASS(WebAssemblyLowerEmscriptenEHSjLj, DEBUG_TYPE,
2860b57cec5SDimitry Andric                 "WebAssembly Lower Emscripten Exceptions / Setjmp / Longjmp",
2870b57cec5SDimitry Andric                 false, false)
2880b57cec5SDimitry Andric 
2890b57cec5SDimitry Andric ModulePass *llvm::createWebAssemblyLowerEmscriptenEHSjLj(bool EnableEH,
2900b57cec5SDimitry Andric                                                          bool EnableSjLj) {
2910b57cec5SDimitry Andric   return new WebAssemblyLowerEmscriptenEHSjLj(EnableEH, EnableSjLj);
2920b57cec5SDimitry Andric }
2930b57cec5SDimitry Andric 
2940b57cec5SDimitry Andric static bool canThrow(const Value *V) {
2950b57cec5SDimitry Andric   if (const auto *F = dyn_cast<const Function>(V)) {
2960b57cec5SDimitry Andric     // Intrinsics cannot throw
2970b57cec5SDimitry Andric     if (F->isIntrinsic())
2980b57cec5SDimitry Andric       return false;
2990b57cec5SDimitry Andric     StringRef Name = F->getName();
3000b57cec5SDimitry Andric     // leave setjmp and longjmp (mostly) alone, we process them properly later
3010b57cec5SDimitry Andric     if (Name == "setjmp" || Name == "longjmp")
3020b57cec5SDimitry Andric       return false;
3030b57cec5SDimitry Andric     return !F->doesNotThrow();
3040b57cec5SDimitry Andric   }
3050b57cec5SDimitry Andric   // not a function, so an indirect call - can throw, we can't tell
3060b57cec5SDimitry Andric   return true;
3070b57cec5SDimitry Andric }
3080b57cec5SDimitry Andric 
3090b57cec5SDimitry Andric // Get a global variable with the given name.  If it doesn't exist declare it,
3100b57cec5SDimitry Andric // which will generate an import and asssumes that it will exist at link time.
3110b57cec5SDimitry Andric static GlobalVariable *getGlobalVariableI32(Module &M, IRBuilder<> &IRB,
312e8d8bef9SDimitry Andric                                             WebAssemblyTargetMachine &TM,
3130b57cec5SDimitry Andric                                             const char *Name) {
314e8d8bef9SDimitry Andric   auto Int32Ty = IRB.getInt32Ty();
315e8d8bef9SDimitry Andric   auto *GV = dyn_cast<GlobalVariable>(M.getOrInsertGlobal(Name, Int32Ty));
3160b57cec5SDimitry Andric   if (!GV)
3170b57cec5SDimitry Andric     report_fatal_error(Twine("unable to create global: ") + Name);
3180b57cec5SDimitry Andric 
319e8d8bef9SDimitry Andric   // If the target supports TLS, make this variable thread-local. We can't just
320e8d8bef9SDimitry Andric   // unconditionally make it thread-local and depend on
321e8d8bef9SDimitry Andric   // CoalesceFeaturesAndStripAtomics to downgrade it, because stripping TLS has
322e8d8bef9SDimitry Andric   // the side effect of disallowing the object from being linked into a
323e8d8bef9SDimitry Andric   // shared-memory module, which we don't want to be responsible for.
324e8d8bef9SDimitry Andric   auto *Subtarget = TM.getSubtargetImpl();
325e8d8bef9SDimitry Andric   auto TLS = Subtarget->hasAtomics() && Subtarget->hasBulkMemory()
326e8d8bef9SDimitry Andric                  ? GlobalValue::LocalExecTLSModel
327e8d8bef9SDimitry Andric                  : GlobalValue::NotThreadLocal;
328e8d8bef9SDimitry Andric   GV->setThreadLocalMode(TLS);
3290b57cec5SDimitry Andric   return GV;
3300b57cec5SDimitry Andric }
3310b57cec5SDimitry Andric 
3320b57cec5SDimitry Andric // Simple function name mangler.
3330b57cec5SDimitry Andric // This function simply takes LLVM's string representation of parameter types
3340b57cec5SDimitry Andric // and concatenate them with '_'. There are non-alphanumeric characters but llc
3350b57cec5SDimitry Andric // is ok with it, and we need to postprocess these names after the lowering
3360b57cec5SDimitry Andric // phase anyway.
3370b57cec5SDimitry Andric static std::string getSignature(FunctionType *FTy) {
3380b57cec5SDimitry Andric   std::string Sig;
3390b57cec5SDimitry Andric   raw_string_ostream OS(Sig);
3400b57cec5SDimitry Andric   OS << *FTy->getReturnType();
3410b57cec5SDimitry Andric   for (Type *ParamTy : FTy->params())
3420b57cec5SDimitry Andric     OS << "_" << *ParamTy;
3430b57cec5SDimitry Andric   if (FTy->isVarArg())
3440b57cec5SDimitry Andric     OS << "_...";
3450b57cec5SDimitry Andric   Sig = OS.str();
346e8d8bef9SDimitry Andric   erase_if(Sig, isSpace);
3470b57cec5SDimitry Andric   // When s2wasm parses .s file, a comma means the end of an argument. So a
3480b57cec5SDimitry Andric   // mangled function name can contain any character but a comma.
3490b57cec5SDimitry Andric   std::replace(Sig.begin(), Sig.end(), ',', '.');
3500b57cec5SDimitry Andric   return Sig;
3510b57cec5SDimitry Andric }
3520b57cec5SDimitry Andric 
3535ffd83dbSDimitry Andric static Function *getEmscriptenFunction(FunctionType *Ty, const Twine &Name,
3545ffd83dbSDimitry Andric                                        Module *M) {
3555ffd83dbSDimitry Andric   Function* F = Function::Create(Ty, GlobalValue::ExternalLinkage, Name, M);
3565ffd83dbSDimitry Andric   // Tell the linker that this function is expected to be imported from the
3575ffd83dbSDimitry Andric   // 'env' module.
3585ffd83dbSDimitry Andric   if (!F->hasFnAttribute("wasm-import-module")) {
3595ffd83dbSDimitry Andric     llvm::AttrBuilder B;
3605ffd83dbSDimitry Andric     B.addAttribute("wasm-import-module", "env");
3615ffd83dbSDimitry Andric     F->addAttributes(llvm::AttributeList::FunctionIndex, B);
3625ffd83dbSDimitry Andric   }
3635ffd83dbSDimitry Andric   if (!F->hasFnAttribute("wasm-import-name")) {
3645ffd83dbSDimitry Andric     llvm::AttrBuilder B;
3655ffd83dbSDimitry Andric     B.addAttribute("wasm-import-name", F->getName());
3665ffd83dbSDimitry Andric     F->addAttributes(llvm::AttributeList::FunctionIndex, B);
3675ffd83dbSDimitry Andric   }
3685ffd83dbSDimitry Andric   return F;
3695ffd83dbSDimitry Andric }
3705ffd83dbSDimitry Andric 
3710b57cec5SDimitry Andric // Returns __cxa_find_matching_catch_N function, where N = NumClauses + 2.
3720b57cec5SDimitry Andric // This is because a landingpad instruction contains two more arguments, a
3730b57cec5SDimitry Andric // personality function and a cleanup bit, and __cxa_find_matching_catch_N
3740b57cec5SDimitry Andric // functions are named after the number of arguments in the original landingpad
3750b57cec5SDimitry Andric // instruction.
3760b57cec5SDimitry Andric Function *
3770b57cec5SDimitry Andric WebAssemblyLowerEmscriptenEHSjLj::getFindMatchingCatch(Module &M,
3780b57cec5SDimitry Andric                                                        unsigned NumClauses) {
3790b57cec5SDimitry Andric   if (FindMatchingCatches.count(NumClauses))
3800b57cec5SDimitry Andric     return FindMatchingCatches[NumClauses];
3810b57cec5SDimitry Andric   PointerType *Int8PtrTy = Type::getInt8PtrTy(M.getContext());
3820b57cec5SDimitry Andric   SmallVector<Type *, 16> Args(NumClauses, Int8PtrTy);
3830b57cec5SDimitry Andric   FunctionType *FTy = FunctionType::get(Int8PtrTy, Args, false);
3845ffd83dbSDimitry Andric   Function *F = getEmscriptenFunction(
3855ffd83dbSDimitry Andric       FTy, "__cxa_find_matching_catch_" + Twine(NumClauses + 2), &M);
3860b57cec5SDimitry Andric   FindMatchingCatches[NumClauses] = F;
3870b57cec5SDimitry Andric   return F;
3880b57cec5SDimitry Andric }
3890b57cec5SDimitry Andric 
3900b57cec5SDimitry Andric // Generate invoke wrapper seqence with preamble and postamble
3910b57cec5SDimitry Andric // Preamble:
3920b57cec5SDimitry Andric // __THREW__ = 0;
3930b57cec5SDimitry Andric // Postamble:
3940b57cec5SDimitry Andric // %__THREW__.val = __THREW__; __THREW__ = 0;
3950b57cec5SDimitry Andric // Returns %__THREW__.val, which indicates whether an exception is thrown (or
3960b57cec5SDimitry Andric // whether longjmp occurred), for future use.
3975ffd83dbSDimitry Andric Value *WebAssemblyLowerEmscriptenEHSjLj::wrapInvoke(CallBase *CI) {
3980b57cec5SDimitry Andric   LLVMContext &C = CI->getModule()->getContext();
3990b57cec5SDimitry Andric 
4000b57cec5SDimitry Andric   // If we are calling a function that is noreturn, we must remove that
4010b57cec5SDimitry Andric   // attribute. The code we insert here does expect it to return, after we
4020b57cec5SDimitry Andric   // catch the exception.
4030b57cec5SDimitry Andric   if (CI->doesNotReturn()) {
4045ffd83dbSDimitry Andric     if (auto *F = CI->getCalledFunction())
4050b57cec5SDimitry Andric       F->removeFnAttr(Attribute::NoReturn);
4060b57cec5SDimitry Andric     CI->removeAttribute(AttributeList::FunctionIndex, Attribute::NoReturn);
4070b57cec5SDimitry Andric   }
4080b57cec5SDimitry Andric 
4090b57cec5SDimitry Andric   IRBuilder<> IRB(C);
4100b57cec5SDimitry Andric   IRB.SetInsertPoint(CI);
4110b57cec5SDimitry Andric 
4120b57cec5SDimitry Andric   // Pre-invoke
4130b57cec5SDimitry Andric   // __THREW__ = 0;
4140b57cec5SDimitry Andric   IRB.CreateStore(IRB.getInt32(0), ThrewGV);
4150b57cec5SDimitry Andric 
4160b57cec5SDimitry Andric   // Invoke function wrapper in JavaScript
4170b57cec5SDimitry Andric   SmallVector<Value *, 16> Args;
4180b57cec5SDimitry Andric   // Put the pointer to the callee as first argument, so it can be called
4190b57cec5SDimitry Andric   // within the invoke wrapper later
4205ffd83dbSDimitry Andric   Args.push_back(CI->getCalledOperand());
4210b57cec5SDimitry Andric   Args.append(CI->arg_begin(), CI->arg_end());
4220b57cec5SDimitry Andric   CallInst *NewCall = IRB.CreateCall(getInvokeWrapper(CI), Args);
4230b57cec5SDimitry Andric   NewCall->takeName(CI);
4248bcb0991SDimitry Andric   NewCall->setCallingConv(CallingConv::WASM_EmscriptenInvoke);
4250b57cec5SDimitry Andric   NewCall->setDebugLoc(CI->getDebugLoc());
4260b57cec5SDimitry Andric 
4270b57cec5SDimitry Andric   // Because we added the pointer to the callee as first argument, all
4280b57cec5SDimitry Andric   // argument attribute indices have to be incremented by one.
4290b57cec5SDimitry Andric   SmallVector<AttributeSet, 8> ArgAttributes;
4300b57cec5SDimitry Andric   const AttributeList &InvokeAL = CI->getAttributes();
4310b57cec5SDimitry Andric 
4320b57cec5SDimitry Andric   // No attributes for the callee pointer.
4330b57cec5SDimitry Andric   ArgAttributes.push_back(AttributeSet());
4340b57cec5SDimitry Andric   // Copy the argument attributes from the original
4350b57cec5SDimitry Andric   for (unsigned I = 0, E = CI->getNumArgOperands(); I < E; ++I)
4360b57cec5SDimitry Andric     ArgAttributes.push_back(InvokeAL.getParamAttributes(I));
4370b57cec5SDimitry Andric 
4388bcb0991SDimitry Andric   AttrBuilder FnAttrs(InvokeAL.getFnAttributes());
4398bcb0991SDimitry Andric   if (FnAttrs.contains(Attribute::AllocSize)) {
4408bcb0991SDimitry Andric     // The allocsize attribute (if any) referes to parameters by index and needs
4418bcb0991SDimitry Andric     // to be adjusted.
4428bcb0991SDimitry Andric     unsigned SizeArg;
4438bcb0991SDimitry Andric     Optional<unsigned> NEltArg;
4448bcb0991SDimitry Andric     std::tie(SizeArg, NEltArg) = FnAttrs.getAllocSizeArgs();
4458bcb0991SDimitry Andric     SizeArg += 1;
4468bcb0991SDimitry Andric     if (NEltArg.hasValue())
4478bcb0991SDimitry Andric       NEltArg = NEltArg.getValue() + 1;
4488bcb0991SDimitry Andric     FnAttrs.addAllocSizeAttr(SizeArg, NEltArg);
4498bcb0991SDimitry Andric   }
4508bcb0991SDimitry Andric 
4510b57cec5SDimitry Andric   // Reconstruct the AttributesList based on the vector we constructed.
4520b57cec5SDimitry Andric   AttributeList NewCallAL =
4538bcb0991SDimitry Andric       AttributeList::get(C, AttributeSet::get(C, FnAttrs),
4540b57cec5SDimitry Andric                          InvokeAL.getRetAttributes(), ArgAttributes);
4550b57cec5SDimitry Andric   NewCall->setAttributes(NewCallAL);
4560b57cec5SDimitry Andric 
4570b57cec5SDimitry Andric   CI->replaceAllUsesWith(NewCall);
4580b57cec5SDimitry Andric 
4590b57cec5SDimitry Andric   // Post-invoke
4600b57cec5SDimitry Andric   // %__THREW__.val = __THREW__; __THREW__ = 0;
4610b57cec5SDimitry Andric   Value *Threw =
4620b57cec5SDimitry Andric       IRB.CreateLoad(IRB.getInt32Ty(), ThrewGV, ThrewGV->getName() + ".val");
4630b57cec5SDimitry Andric   IRB.CreateStore(IRB.getInt32(0), ThrewGV);
4640b57cec5SDimitry Andric   return Threw;
4650b57cec5SDimitry Andric }
4660b57cec5SDimitry Andric 
4670b57cec5SDimitry Andric // Get matching invoke wrapper based on callee signature
4685ffd83dbSDimitry Andric Function *WebAssemblyLowerEmscriptenEHSjLj::getInvokeWrapper(CallBase *CI) {
4690b57cec5SDimitry Andric   Module *M = CI->getModule();
4700b57cec5SDimitry Andric   SmallVector<Type *, 16> ArgTys;
4715ffd83dbSDimitry Andric   FunctionType *CalleeFTy = CI->getFunctionType();
4720b57cec5SDimitry Andric 
4730b57cec5SDimitry Andric   std::string Sig = getSignature(CalleeFTy);
4740b57cec5SDimitry Andric   if (InvokeWrappers.find(Sig) != InvokeWrappers.end())
4750b57cec5SDimitry Andric     return InvokeWrappers[Sig];
4760b57cec5SDimitry Andric 
4770b57cec5SDimitry Andric   // Put the pointer to the callee as first argument
4780b57cec5SDimitry Andric   ArgTys.push_back(PointerType::getUnqual(CalleeFTy));
4790b57cec5SDimitry Andric   // Add argument types
4800b57cec5SDimitry Andric   ArgTys.append(CalleeFTy->param_begin(), CalleeFTy->param_end());
4810b57cec5SDimitry Andric 
4820b57cec5SDimitry Andric   FunctionType *FTy = FunctionType::get(CalleeFTy->getReturnType(), ArgTys,
4830b57cec5SDimitry Andric                                         CalleeFTy->isVarArg());
4845ffd83dbSDimitry Andric   Function *F = getEmscriptenFunction(FTy, "__invoke_" + Sig, M);
4850b57cec5SDimitry Andric   InvokeWrappers[Sig] = F;
4860b57cec5SDimitry Andric   return F;
4870b57cec5SDimitry Andric }
4880b57cec5SDimitry Andric 
4890b57cec5SDimitry Andric bool WebAssemblyLowerEmscriptenEHSjLj::canLongjmp(Module &M,
4900b57cec5SDimitry Andric                                                   const Value *Callee) const {
4910b57cec5SDimitry Andric   if (auto *CalleeF = dyn_cast<Function>(Callee))
4920b57cec5SDimitry Andric     if (CalleeF->isIntrinsic())
4930b57cec5SDimitry Andric       return false;
4940b57cec5SDimitry Andric 
4950b57cec5SDimitry Andric   // Attempting to transform inline assembly will result in something like:
4960b57cec5SDimitry Andric   //     call void @__invoke_void(void ()* asm ...)
4970b57cec5SDimitry Andric   // which is invalid because inline assembly blocks do not have addresses
4980b57cec5SDimitry Andric   // and can't be passed by pointer. The result is a crash with illegal IR.
4990b57cec5SDimitry Andric   if (isa<InlineAsm>(Callee))
5000b57cec5SDimitry Andric     return false;
5018bcb0991SDimitry Andric   StringRef CalleeName = Callee->getName();
5020b57cec5SDimitry Andric 
5030b57cec5SDimitry Andric   // The reason we include malloc/free here is to exclude the malloc/free
5040b57cec5SDimitry Andric   // calls generated in setjmp prep / cleanup routines.
5058bcb0991SDimitry Andric   if (CalleeName == "setjmp" || CalleeName == "malloc" || CalleeName == "free")
5060b57cec5SDimitry Andric     return false;
5070b57cec5SDimitry Andric 
5080b57cec5SDimitry Andric   // There are functions in JS glue code
5098bcb0991SDimitry Andric   if (CalleeName == "__resumeException" || CalleeName == "llvm_eh_typeid_for" ||
5108bcb0991SDimitry Andric       CalleeName == "saveSetjmp" || CalleeName == "testSetjmp" ||
5118bcb0991SDimitry Andric       CalleeName == "getTempRet0" || CalleeName == "setTempRet0")
5120b57cec5SDimitry Andric     return false;
5130b57cec5SDimitry Andric 
5140b57cec5SDimitry Andric   // __cxa_find_matching_catch_N functions cannot longjmp
5158bcb0991SDimitry Andric   if (Callee->getName().startswith("__cxa_find_matching_catch_"))
5160b57cec5SDimitry Andric     return false;
5170b57cec5SDimitry Andric 
5180b57cec5SDimitry Andric   // Exception-catching related functions
5198bcb0991SDimitry Andric   if (CalleeName == "__cxa_begin_catch" || CalleeName == "__cxa_end_catch" ||
5208bcb0991SDimitry Andric       CalleeName == "__cxa_allocate_exception" || CalleeName == "__cxa_throw" ||
5218bcb0991SDimitry Andric       CalleeName == "__clang_call_terminate")
5220b57cec5SDimitry Andric     return false;
5230b57cec5SDimitry Andric 
5240b57cec5SDimitry Andric   // Otherwise we don't know
5250b57cec5SDimitry Andric   return true;
5260b57cec5SDimitry Andric }
5270b57cec5SDimitry Andric 
5288bcb0991SDimitry Andric bool WebAssemblyLowerEmscriptenEHSjLj::isEmAsmCall(Module &M,
5298bcb0991SDimitry Andric                                                    const Value *Callee) const {
5308bcb0991SDimitry Andric   StringRef CalleeName = Callee->getName();
5318bcb0991SDimitry Andric   // This is an exhaustive list from Emscripten's <emscripten/em_asm.h>.
5328bcb0991SDimitry Andric   return CalleeName == "emscripten_asm_const_int" ||
5338bcb0991SDimitry Andric          CalleeName == "emscripten_asm_const_double" ||
5348bcb0991SDimitry Andric          CalleeName == "emscripten_asm_const_int_sync_on_main_thread" ||
5358bcb0991SDimitry Andric          CalleeName == "emscripten_asm_const_double_sync_on_main_thread" ||
5368bcb0991SDimitry Andric          CalleeName == "emscripten_asm_const_async_on_main_thread";
5378bcb0991SDimitry Andric }
5388bcb0991SDimitry Andric 
5390b57cec5SDimitry Andric // Generate testSetjmp function call seqence with preamble and postamble.
5400b57cec5SDimitry Andric // The code this generates is equivalent to the following JavaScript code:
5410b57cec5SDimitry Andric // if (%__THREW__.val != 0 & threwValue != 0) {
5420b57cec5SDimitry Andric //   %label = _testSetjmp(mem[%__THREW__.val], setjmpTable, setjmpTableSize);
5430b57cec5SDimitry Andric //   if (%label == 0)
5440b57cec5SDimitry Andric //     emscripten_longjmp(%__THREW__.val, threwValue);
5450b57cec5SDimitry Andric //   setTempRet0(threwValue);
5460b57cec5SDimitry Andric // } else {
5470b57cec5SDimitry Andric //   %label = -1;
5480b57cec5SDimitry Andric // }
5490b57cec5SDimitry Andric // %longjmp_result = getTempRet0();
5500b57cec5SDimitry Andric //
5510b57cec5SDimitry Andric // As output parameters. returns %label, %longjmp_result, and the BB the last
5520b57cec5SDimitry Andric // instruction (%longjmp_result = ...) is in.
5530b57cec5SDimitry Andric void WebAssemblyLowerEmscriptenEHSjLj::wrapTestSetjmp(
5545ffd83dbSDimitry Andric     BasicBlock *BB, DebugLoc DL, Value *Threw, Value *SetjmpTable,
5550b57cec5SDimitry Andric     Value *SetjmpTableSize, Value *&Label, Value *&LongjmpResult,
5560b57cec5SDimitry Andric     BasicBlock *&EndBB) {
5570b57cec5SDimitry Andric   Function *F = BB->getParent();
5580b57cec5SDimitry Andric   LLVMContext &C = BB->getModule()->getContext();
5590b57cec5SDimitry Andric   IRBuilder<> IRB(C);
5605ffd83dbSDimitry Andric   IRB.SetCurrentDebugLocation(DL);
5610b57cec5SDimitry Andric 
5620b57cec5SDimitry Andric   // if (%__THREW__.val != 0 & threwValue != 0)
5630b57cec5SDimitry Andric   IRB.SetInsertPoint(BB);
5640b57cec5SDimitry Andric   BasicBlock *ThenBB1 = BasicBlock::Create(C, "if.then1", F);
5650b57cec5SDimitry Andric   BasicBlock *ElseBB1 = BasicBlock::Create(C, "if.else1", F);
5660b57cec5SDimitry Andric   BasicBlock *EndBB1 = BasicBlock::Create(C, "if.end", F);
5670b57cec5SDimitry Andric   Value *ThrewCmp = IRB.CreateICmpNE(Threw, IRB.getInt32(0));
5680b57cec5SDimitry Andric   Value *ThrewValue = IRB.CreateLoad(IRB.getInt32Ty(), ThrewValueGV,
5690b57cec5SDimitry Andric                                      ThrewValueGV->getName() + ".val");
5700b57cec5SDimitry Andric   Value *ThrewValueCmp = IRB.CreateICmpNE(ThrewValue, IRB.getInt32(0));
5710b57cec5SDimitry Andric   Value *Cmp1 = IRB.CreateAnd(ThrewCmp, ThrewValueCmp, "cmp1");
5720b57cec5SDimitry Andric   IRB.CreateCondBr(Cmp1, ThenBB1, ElseBB1);
5730b57cec5SDimitry Andric 
5740b57cec5SDimitry Andric   // %label = _testSetjmp(mem[%__THREW__.val], _setjmpTable, _setjmpTableSize);
5750b57cec5SDimitry Andric   // if (%label == 0)
5760b57cec5SDimitry Andric   IRB.SetInsertPoint(ThenBB1);
5770b57cec5SDimitry Andric   BasicBlock *ThenBB2 = BasicBlock::Create(C, "if.then2", F);
5780b57cec5SDimitry Andric   BasicBlock *EndBB2 = BasicBlock::Create(C, "if.end2", F);
5790b57cec5SDimitry Andric   Value *ThrewInt = IRB.CreateIntToPtr(Threw, Type::getInt32PtrTy(C),
5800b57cec5SDimitry Andric                                        Threw->getName() + ".i32p");
5810b57cec5SDimitry Andric   Value *LoadedThrew = IRB.CreateLoad(IRB.getInt32Ty(), ThrewInt,
5820b57cec5SDimitry Andric                                       ThrewInt->getName() + ".loaded");
5830b57cec5SDimitry Andric   Value *ThenLabel = IRB.CreateCall(
5840b57cec5SDimitry Andric       TestSetjmpF, {LoadedThrew, SetjmpTable, SetjmpTableSize}, "label");
5850b57cec5SDimitry Andric   Value *Cmp2 = IRB.CreateICmpEQ(ThenLabel, IRB.getInt32(0));
5860b57cec5SDimitry Andric   IRB.CreateCondBr(Cmp2, ThenBB2, EndBB2);
5870b57cec5SDimitry Andric 
5880b57cec5SDimitry Andric   // emscripten_longjmp(%__THREW__.val, threwValue);
5890b57cec5SDimitry Andric   IRB.SetInsertPoint(ThenBB2);
5900b57cec5SDimitry Andric   IRB.CreateCall(EmLongjmpF, {Threw, ThrewValue});
5910b57cec5SDimitry Andric   IRB.CreateUnreachable();
5920b57cec5SDimitry Andric 
5930b57cec5SDimitry Andric   // setTempRet0(threwValue);
5940b57cec5SDimitry Andric   IRB.SetInsertPoint(EndBB2);
5950b57cec5SDimitry Andric   IRB.CreateCall(SetTempRet0Func, ThrewValue);
5960b57cec5SDimitry Andric   IRB.CreateBr(EndBB1);
5970b57cec5SDimitry Andric 
5980b57cec5SDimitry Andric   IRB.SetInsertPoint(ElseBB1);
5990b57cec5SDimitry Andric   IRB.CreateBr(EndBB1);
6000b57cec5SDimitry Andric 
6010b57cec5SDimitry Andric   // longjmp_result = getTempRet0();
6020b57cec5SDimitry Andric   IRB.SetInsertPoint(EndBB1);
6030b57cec5SDimitry Andric   PHINode *LabelPHI = IRB.CreatePHI(IRB.getInt32Ty(), 2, "label");
6040b57cec5SDimitry Andric   LabelPHI->addIncoming(ThenLabel, EndBB2);
6050b57cec5SDimitry Andric 
6060b57cec5SDimitry Andric   LabelPHI->addIncoming(IRB.getInt32(-1), ElseBB1);
6070b57cec5SDimitry Andric 
6080b57cec5SDimitry Andric   // Output parameter assignment
6090b57cec5SDimitry Andric   Label = LabelPHI;
6100b57cec5SDimitry Andric   EndBB = EndBB1;
6110b57cec5SDimitry Andric   LongjmpResult = IRB.CreateCall(GetTempRet0Func, None, "longjmp_result");
6120b57cec5SDimitry Andric }
6130b57cec5SDimitry Andric 
6140b57cec5SDimitry Andric void WebAssemblyLowerEmscriptenEHSjLj::rebuildSSA(Function &F) {
6150b57cec5SDimitry Andric   DominatorTree &DT = getAnalysis<DominatorTreeWrapperPass>(F).getDomTree();
6160b57cec5SDimitry Andric   DT.recalculate(F); // CFG has been changed
6170b57cec5SDimitry Andric   SSAUpdater SSA;
6180b57cec5SDimitry Andric   for (BasicBlock &BB : F) {
6190b57cec5SDimitry Andric     for (Instruction &I : BB) {
6208bcb0991SDimitry Andric       SSA.Initialize(I.getType(), I.getName());
6218bcb0991SDimitry Andric       SSA.AddAvailableValue(&BB, &I);
6220b57cec5SDimitry Andric       for (auto UI = I.use_begin(), UE = I.use_end(); UI != UE;) {
6230b57cec5SDimitry Andric         Use &U = *UI;
6240b57cec5SDimitry Andric         ++UI;
6250b57cec5SDimitry Andric         auto *User = cast<Instruction>(U.getUser());
6260b57cec5SDimitry Andric         if (auto *UserPN = dyn_cast<PHINode>(User))
6270b57cec5SDimitry Andric           if (UserPN->getIncomingBlock(U) == &BB)
6280b57cec5SDimitry Andric             continue;
6290b57cec5SDimitry Andric 
6300b57cec5SDimitry Andric         if (DT.dominates(&I, User))
6310b57cec5SDimitry Andric           continue;
6320b57cec5SDimitry Andric         SSA.RewriteUseAfterInsertions(U);
6330b57cec5SDimitry Andric       }
6340b57cec5SDimitry Andric     }
6350b57cec5SDimitry Andric   }
6360b57cec5SDimitry Andric }
6370b57cec5SDimitry Andric 
638e8d8bef9SDimitry Andric // Replace uses of longjmp with emscripten_longjmp. emscripten_longjmp takes
639e8d8bef9SDimitry Andric // arguments of type {i32, i32} and longjmp takes {jmp_buf*, i32}, so we need a
640e8d8bef9SDimitry Andric // ptrtoint instruction here to make the type match. jmp_buf* will eventually be
641e8d8bef9SDimitry Andric // lowered to i32 in the wasm backend.
642e8d8bef9SDimitry Andric static void replaceLongjmpWithEmscriptenLongjmp(Function *LongjmpF,
643e8d8bef9SDimitry Andric                                                 Function *EmLongjmpF) {
644e8d8bef9SDimitry Andric   SmallVector<CallInst *, 8> ToErase;
645e8d8bef9SDimitry Andric   LLVMContext &C = LongjmpF->getParent()->getContext();
646e8d8bef9SDimitry Andric   IRBuilder<> IRB(C);
647e8d8bef9SDimitry Andric 
648e8d8bef9SDimitry Andric   // For calls to longjmp, replace it with emscripten_longjmp and cast its first
649e8d8bef9SDimitry Andric   // argument (jmp_buf*) to int
650e8d8bef9SDimitry Andric   for (User *U : LongjmpF->users()) {
651e8d8bef9SDimitry Andric     auto *CI = dyn_cast<CallInst>(U);
652e8d8bef9SDimitry Andric     if (CI && CI->getCalledFunction() == LongjmpF) {
653e8d8bef9SDimitry Andric       IRB.SetInsertPoint(CI);
654e8d8bef9SDimitry Andric       Value *Jmpbuf =
655e8d8bef9SDimitry Andric           IRB.CreatePtrToInt(CI->getArgOperand(0), IRB.getInt32Ty(), "jmpbuf");
656e8d8bef9SDimitry Andric       IRB.CreateCall(EmLongjmpF, {Jmpbuf, CI->getArgOperand(1)});
657e8d8bef9SDimitry Andric       ToErase.push_back(CI);
658e8d8bef9SDimitry Andric     }
659e8d8bef9SDimitry Andric   }
660e8d8bef9SDimitry Andric   for (auto *I : ToErase)
661e8d8bef9SDimitry Andric     I->eraseFromParent();
662e8d8bef9SDimitry Andric 
663e8d8bef9SDimitry Andric   // If we have any remaining uses of longjmp's function pointer, replace it
664e8d8bef9SDimitry Andric   // with (int(*)(jmp_buf*, int))emscripten_longjmp.
665e8d8bef9SDimitry Andric   if (!LongjmpF->uses().empty()) {
666e8d8bef9SDimitry Andric     Value *EmLongjmp =
667e8d8bef9SDimitry Andric         IRB.CreateBitCast(EmLongjmpF, LongjmpF->getType(), "em_longjmp");
668e8d8bef9SDimitry Andric     LongjmpF->replaceAllUsesWith(EmLongjmp);
669e8d8bef9SDimitry Andric   }
670e8d8bef9SDimitry Andric }
671e8d8bef9SDimitry Andric 
6720b57cec5SDimitry Andric bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) {
6730b57cec5SDimitry Andric   LLVM_DEBUG(dbgs() << "********** Lower Emscripten EH & SjLj **********\n");
6740b57cec5SDimitry Andric 
6750b57cec5SDimitry Andric   LLVMContext &C = M.getContext();
6760b57cec5SDimitry Andric   IRBuilder<> IRB(C);
6770b57cec5SDimitry Andric 
6780b57cec5SDimitry Andric   Function *SetjmpF = M.getFunction("setjmp");
6790b57cec5SDimitry Andric   Function *LongjmpF = M.getFunction("longjmp");
6800b57cec5SDimitry Andric   bool SetjmpUsed = SetjmpF && !SetjmpF->use_empty();
6810b57cec5SDimitry Andric   bool LongjmpUsed = LongjmpF && !LongjmpF->use_empty();
6820b57cec5SDimitry Andric   bool DoSjLj = EnableSjLj && (SetjmpUsed || LongjmpUsed);
6830b57cec5SDimitry Andric 
684e8d8bef9SDimitry Andric   if ((EnableEH || DoSjLj) &&
685e8d8bef9SDimitry Andric       Triple(M.getTargetTriple()).getArch() == Triple::wasm64)
686e8d8bef9SDimitry Andric     report_fatal_error("Emscripten EH/SjLj is not supported with wasm64 yet");
687e8d8bef9SDimitry Andric 
688e8d8bef9SDimitry Andric   auto *TPC = getAnalysisIfAvailable<TargetPassConfig>();
689e8d8bef9SDimitry Andric   assert(TPC && "Expected a TargetPassConfig");
690e8d8bef9SDimitry Andric   auto &TM = TPC->getTM<WebAssemblyTargetMachine>();
691e8d8bef9SDimitry Andric 
6920b57cec5SDimitry Andric   // Declare (or get) global variables __THREW__, __threwValue, and
6930b57cec5SDimitry Andric   // getTempRet0/setTempRet0 function which are used in common for both
6940b57cec5SDimitry Andric   // exception handling and setjmp/longjmp handling
695e8d8bef9SDimitry Andric   ThrewGV = getGlobalVariableI32(M, IRB, TM, "__THREW__");
696e8d8bef9SDimitry Andric   ThrewValueGV = getGlobalVariableI32(M, IRB, TM, "__threwValue");
6975ffd83dbSDimitry Andric   GetTempRet0Func = getEmscriptenFunction(
6985ffd83dbSDimitry Andric       FunctionType::get(IRB.getInt32Ty(), false), "getTempRet0", &M);
6995ffd83dbSDimitry Andric   SetTempRet0Func = getEmscriptenFunction(
7000b57cec5SDimitry Andric       FunctionType::get(IRB.getVoidTy(), IRB.getInt32Ty(), false),
7015ffd83dbSDimitry Andric       "setTempRet0", &M);
7020b57cec5SDimitry Andric   GetTempRet0Func->setDoesNotThrow();
7030b57cec5SDimitry Andric   SetTempRet0Func->setDoesNotThrow();
7040b57cec5SDimitry Andric 
7050b57cec5SDimitry Andric   bool Changed = false;
7060b57cec5SDimitry Andric 
7070b57cec5SDimitry Andric   // Exception handling
7080b57cec5SDimitry Andric   if (EnableEH) {
7090b57cec5SDimitry Andric     // Register __resumeException function
7100b57cec5SDimitry Andric     FunctionType *ResumeFTy =
7110b57cec5SDimitry Andric         FunctionType::get(IRB.getVoidTy(), IRB.getInt8PtrTy(), false);
7125ffd83dbSDimitry Andric     ResumeF = getEmscriptenFunction(ResumeFTy, "__resumeException", &M);
7130b57cec5SDimitry Andric 
7140b57cec5SDimitry Andric     // Register llvm_eh_typeid_for function
7150b57cec5SDimitry Andric     FunctionType *EHTypeIDTy =
7160b57cec5SDimitry Andric         FunctionType::get(IRB.getInt32Ty(), IRB.getInt8PtrTy(), false);
7175ffd83dbSDimitry Andric     EHTypeIDF = getEmscriptenFunction(EHTypeIDTy, "llvm_eh_typeid_for", &M);
7180b57cec5SDimitry Andric 
7190b57cec5SDimitry Andric     for (Function &F : M) {
7200b57cec5SDimitry Andric       if (F.isDeclaration())
7210b57cec5SDimitry Andric         continue;
7220b57cec5SDimitry Andric       Changed |= runEHOnFunction(F);
7230b57cec5SDimitry Andric     }
7240b57cec5SDimitry Andric   }
7250b57cec5SDimitry Andric 
7260b57cec5SDimitry Andric   // Setjmp/longjmp handling
7270b57cec5SDimitry Andric   if (DoSjLj) {
7280b57cec5SDimitry Andric     Changed = true; // We have setjmp or longjmp somewhere
7290b57cec5SDimitry Andric 
730e8d8bef9SDimitry Andric     // Register emscripten_longjmp function
731e8d8bef9SDimitry Andric     FunctionType *FTy = FunctionType::get(
732e8d8bef9SDimitry Andric         IRB.getVoidTy(), {IRB.getInt32Ty(), IRB.getInt32Ty()}, false);
733e8d8bef9SDimitry Andric     EmLongjmpF = getEmscriptenFunction(FTy, "emscripten_longjmp", &M);
734e8d8bef9SDimitry Andric 
735e8d8bef9SDimitry Andric     if (LongjmpF)
736e8d8bef9SDimitry Andric       replaceLongjmpWithEmscriptenLongjmp(LongjmpF, EmLongjmpF);
7370b57cec5SDimitry Andric 
7380b57cec5SDimitry Andric     if (SetjmpF) {
7390b57cec5SDimitry Andric       // Register saveSetjmp function
7400b57cec5SDimitry Andric       FunctionType *SetjmpFTy = SetjmpF->getFunctionType();
741e8d8bef9SDimitry Andric       FTy = FunctionType::get(Type::getInt32PtrTy(C),
7425ffd83dbSDimitry Andric                               {SetjmpFTy->getParamType(0), IRB.getInt32Ty(),
7435ffd83dbSDimitry Andric                                Type::getInt32PtrTy(C), IRB.getInt32Ty()},
7445ffd83dbSDimitry Andric                               false);
7455ffd83dbSDimitry Andric       SaveSetjmpF = getEmscriptenFunction(FTy, "saveSetjmp", &M);
7460b57cec5SDimitry Andric 
7470b57cec5SDimitry Andric       // Register testSetjmp function
7485ffd83dbSDimitry Andric       FTy = FunctionType::get(
7495ffd83dbSDimitry Andric           IRB.getInt32Ty(),
7505ffd83dbSDimitry Andric           {IRB.getInt32Ty(), Type::getInt32PtrTy(C), IRB.getInt32Ty()}, false);
7515ffd83dbSDimitry Andric       TestSetjmpF = getEmscriptenFunction(FTy, "testSetjmp", &M);
7520b57cec5SDimitry Andric 
7530b57cec5SDimitry Andric       // Only traverse functions that uses setjmp in order not to insert
7540b57cec5SDimitry Andric       // unnecessary prep / cleanup code in every function
7550b57cec5SDimitry Andric       SmallPtrSet<Function *, 8> SetjmpUsers;
7560b57cec5SDimitry Andric       for (User *U : SetjmpF->users()) {
7570b57cec5SDimitry Andric         auto *UI = cast<Instruction>(U);
7580b57cec5SDimitry Andric         SetjmpUsers.insert(UI->getFunction());
7590b57cec5SDimitry Andric       }
7600b57cec5SDimitry Andric       for (Function *F : SetjmpUsers)
7610b57cec5SDimitry Andric         runSjLjOnFunction(*F);
7620b57cec5SDimitry Andric     }
7630b57cec5SDimitry Andric   }
7640b57cec5SDimitry Andric 
7650b57cec5SDimitry Andric   if (!Changed) {
7660b57cec5SDimitry Andric     // Delete unused global variables and functions
7670b57cec5SDimitry Andric     if (ResumeF)
7680b57cec5SDimitry Andric       ResumeF->eraseFromParent();
7690b57cec5SDimitry Andric     if (EHTypeIDF)
7700b57cec5SDimitry Andric       EHTypeIDF->eraseFromParent();
7710b57cec5SDimitry Andric     if (EmLongjmpF)
7720b57cec5SDimitry Andric       EmLongjmpF->eraseFromParent();
7730b57cec5SDimitry Andric     if (SaveSetjmpF)
7740b57cec5SDimitry Andric       SaveSetjmpF->eraseFromParent();
7750b57cec5SDimitry Andric     if (TestSetjmpF)
7760b57cec5SDimitry Andric       TestSetjmpF->eraseFromParent();
7770b57cec5SDimitry Andric     return false;
7780b57cec5SDimitry Andric   }
7790b57cec5SDimitry Andric 
7800b57cec5SDimitry Andric   return true;
7810b57cec5SDimitry Andric }
7820b57cec5SDimitry Andric 
7830b57cec5SDimitry Andric bool WebAssemblyLowerEmscriptenEHSjLj::runEHOnFunction(Function &F) {
7840b57cec5SDimitry Andric   Module &M = *F.getParent();
7850b57cec5SDimitry Andric   LLVMContext &C = F.getContext();
7860b57cec5SDimitry Andric   IRBuilder<> IRB(C);
7870b57cec5SDimitry Andric   bool Changed = false;
7880b57cec5SDimitry Andric   SmallVector<Instruction *, 64> ToErase;
7890b57cec5SDimitry Andric   SmallPtrSet<LandingPadInst *, 32> LandingPads;
7905ffd83dbSDimitry Andric   bool AllowExceptions = areAllExceptionsAllowed() ||
7915ffd83dbSDimitry Andric                          EHAllowlistSet.count(std::string(F.getName()));
7920b57cec5SDimitry Andric 
7930b57cec5SDimitry Andric   for (BasicBlock &BB : F) {
7940b57cec5SDimitry Andric     auto *II = dyn_cast<InvokeInst>(BB.getTerminator());
7950b57cec5SDimitry Andric     if (!II)
7960b57cec5SDimitry Andric       continue;
79713138422SDimitry Andric     Changed = true;
7980b57cec5SDimitry Andric     LandingPads.insert(II->getLandingPadInst());
7990b57cec5SDimitry Andric     IRB.SetInsertPoint(II);
8000b57cec5SDimitry Andric 
8015ffd83dbSDimitry Andric     bool NeedInvoke = AllowExceptions && canThrow(II->getCalledOperand());
8020b57cec5SDimitry Andric     if (NeedInvoke) {
8030b57cec5SDimitry Andric       // Wrap invoke with invoke wrapper and generate preamble/postamble
8040b57cec5SDimitry Andric       Value *Threw = wrapInvoke(II);
8050b57cec5SDimitry Andric       ToErase.push_back(II);
8060b57cec5SDimitry Andric 
8070b57cec5SDimitry Andric       // Insert a branch based on __THREW__ variable
8080b57cec5SDimitry Andric       Value *Cmp = IRB.CreateICmpEQ(Threw, IRB.getInt32(1), "cmp");
8090b57cec5SDimitry Andric       IRB.CreateCondBr(Cmp, II->getUnwindDest(), II->getNormalDest());
8100b57cec5SDimitry Andric 
8110b57cec5SDimitry Andric     } else {
8120b57cec5SDimitry Andric       // This can't throw, and we don't need this invoke, just replace it with a
8130b57cec5SDimitry Andric       // call+branch
814e8d8bef9SDimitry Andric       SmallVector<Value *, 16> Args(II->args());
8150b57cec5SDimitry Andric       CallInst *NewCall =
8165ffd83dbSDimitry Andric           IRB.CreateCall(II->getFunctionType(), II->getCalledOperand(), Args);
8170b57cec5SDimitry Andric       NewCall->takeName(II);
8180b57cec5SDimitry Andric       NewCall->setCallingConv(II->getCallingConv());
8190b57cec5SDimitry Andric       NewCall->setDebugLoc(II->getDebugLoc());
8200b57cec5SDimitry Andric       NewCall->setAttributes(II->getAttributes());
8210b57cec5SDimitry Andric       II->replaceAllUsesWith(NewCall);
8220b57cec5SDimitry Andric       ToErase.push_back(II);
8230b57cec5SDimitry Andric 
8240b57cec5SDimitry Andric       IRB.CreateBr(II->getNormalDest());
8250b57cec5SDimitry Andric 
8260b57cec5SDimitry Andric       // Remove any PHI node entries from the exception destination
8270b57cec5SDimitry Andric       II->getUnwindDest()->removePredecessor(&BB);
8280b57cec5SDimitry Andric     }
8290b57cec5SDimitry Andric   }
8300b57cec5SDimitry Andric 
8310b57cec5SDimitry Andric   // Process resume instructions
8320b57cec5SDimitry Andric   for (BasicBlock &BB : F) {
8330b57cec5SDimitry Andric     // Scan the body of the basic block for resumes
8340b57cec5SDimitry Andric     for (Instruction &I : BB) {
8350b57cec5SDimitry Andric       auto *RI = dyn_cast<ResumeInst>(&I);
8360b57cec5SDimitry Andric       if (!RI)
8370b57cec5SDimitry Andric         continue;
83813138422SDimitry Andric       Changed = true;
8390b57cec5SDimitry Andric 
8400b57cec5SDimitry Andric       // Split the input into legal values
8410b57cec5SDimitry Andric       Value *Input = RI->getValue();
8420b57cec5SDimitry Andric       IRB.SetInsertPoint(RI);
8430b57cec5SDimitry Andric       Value *Low = IRB.CreateExtractValue(Input, 0, "low");
8440b57cec5SDimitry Andric       // Create a call to __resumeException function
8450b57cec5SDimitry Andric       IRB.CreateCall(ResumeF, {Low});
8460b57cec5SDimitry Andric       // Add a terminator to the block
8470b57cec5SDimitry Andric       IRB.CreateUnreachable();
8480b57cec5SDimitry Andric       ToErase.push_back(RI);
8490b57cec5SDimitry Andric     }
8500b57cec5SDimitry Andric   }
8510b57cec5SDimitry Andric 
8520b57cec5SDimitry Andric   // Process llvm.eh.typeid.for intrinsics
8530b57cec5SDimitry Andric   for (BasicBlock &BB : F) {
8540b57cec5SDimitry Andric     for (Instruction &I : BB) {
8550b57cec5SDimitry Andric       auto *CI = dyn_cast<CallInst>(&I);
8560b57cec5SDimitry Andric       if (!CI)
8570b57cec5SDimitry Andric         continue;
8580b57cec5SDimitry Andric       const Function *Callee = CI->getCalledFunction();
8590b57cec5SDimitry Andric       if (!Callee)
8600b57cec5SDimitry Andric         continue;
8610b57cec5SDimitry Andric       if (Callee->getIntrinsicID() != Intrinsic::eh_typeid_for)
8620b57cec5SDimitry Andric         continue;
86313138422SDimitry Andric       Changed = true;
8640b57cec5SDimitry Andric 
8650b57cec5SDimitry Andric       IRB.SetInsertPoint(CI);
8660b57cec5SDimitry Andric       CallInst *NewCI =
8670b57cec5SDimitry Andric           IRB.CreateCall(EHTypeIDF, CI->getArgOperand(0), "typeid");
8680b57cec5SDimitry Andric       CI->replaceAllUsesWith(NewCI);
8690b57cec5SDimitry Andric       ToErase.push_back(CI);
8700b57cec5SDimitry Andric     }
8710b57cec5SDimitry Andric   }
8720b57cec5SDimitry Andric 
8730b57cec5SDimitry Andric   // Look for orphan landingpads, can occur in blocks with no predecessors
8740b57cec5SDimitry Andric   for (BasicBlock &BB : F) {
8750b57cec5SDimitry Andric     Instruction *I = BB.getFirstNonPHI();
8760b57cec5SDimitry Andric     if (auto *LPI = dyn_cast<LandingPadInst>(I))
8770b57cec5SDimitry Andric       LandingPads.insert(LPI);
8780b57cec5SDimitry Andric   }
87913138422SDimitry Andric   Changed |= !LandingPads.empty();
8800b57cec5SDimitry Andric 
8810b57cec5SDimitry Andric   // Handle all the landingpad for this function together, as multiple invokes
8820b57cec5SDimitry Andric   // may share a single lp
8830b57cec5SDimitry Andric   for (LandingPadInst *LPI : LandingPads) {
8840b57cec5SDimitry Andric     IRB.SetInsertPoint(LPI);
8850b57cec5SDimitry Andric     SmallVector<Value *, 16> FMCArgs;
8860b57cec5SDimitry Andric     for (unsigned I = 0, E = LPI->getNumClauses(); I < E; ++I) {
8870b57cec5SDimitry Andric       Constant *Clause = LPI->getClause(I);
888*23408297SDimitry Andric       // TODO Handle filters (= exception specifications).
889*23408297SDimitry Andric       // https://bugs.llvm.org/show_bug.cgi?id=50396
890*23408297SDimitry Andric       if (LPI->isCatch(I))
8910b57cec5SDimitry Andric         FMCArgs.push_back(Clause);
8920b57cec5SDimitry Andric     }
8930b57cec5SDimitry Andric 
8940b57cec5SDimitry Andric     // Create a call to __cxa_find_matching_catch_N function
8950b57cec5SDimitry Andric     Function *FMCF = getFindMatchingCatch(M, FMCArgs.size());
8960b57cec5SDimitry Andric     CallInst *FMCI = IRB.CreateCall(FMCF, FMCArgs, "fmc");
8970b57cec5SDimitry Andric     Value *Undef = UndefValue::get(LPI->getType());
8980b57cec5SDimitry Andric     Value *Pair0 = IRB.CreateInsertValue(Undef, FMCI, 0, "pair0");
8990b57cec5SDimitry Andric     Value *TempRet0 = IRB.CreateCall(GetTempRet0Func, None, "tempret0");
9000b57cec5SDimitry Andric     Value *Pair1 = IRB.CreateInsertValue(Pair0, TempRet0, 1, "pair1");
9010b57cec5SDimitry Andric 
9020b57cec5SDimitry Andric     LPI->replaceAllUsesWith(Pair1);
9030b57cec5SDimitry Andric     ToErase.push_back(LPI);
9040b57cec5SDimitry Andric   }
9050b57cec5SDimitry Andric 
9060b57cec5SDimitry Andric   // Erase everything we no longer need in this function
9070b57cec5SDimitry Andric   for (Instruction *I : ToErase)
9080b57cec5SDimitry Andric     I->eraseFromParent();
9090b57cec5SDimitry Andric 
9100b57cec5SDimitry Andric   return Changed;
9110b57cec5SDimitry Andric }
9120b57cec5SDimitry Andric 
9135ffd83dbSDimitry Andric // This tries to get debug info from the instruction before which a new
9145ffd83dbSDimitry Andric // instruction will be inserted, and if there's no debug info in that
9155ffd83dbSDimitry Andric // instruction, tries to get the info instead from the previous instruction (if
9165ffd83dbSDimitry Andric // any). If none of these has debug info and a DISubprogram is provided, it
9175ffd83dbSDimitry Andric // creates a dummy debug info with the first line of the function, because IR
9185ffd83dbSDimitry Andric // verifier requires all inlinable callsites should have debug info when both a
9195ffd83dbSDimitry Andric // caller and callee have DISubprogram. If none of these conditions are met,
9205ffd83dbSDimitry Andric // returns empty info.
9215ffd83dbSDimitry Andric static DebugLoc getOrCreateDebugLoc(const Instruction *InsertBefore,
9225ffd83dbSDimitry Andric                                     DISubprogram *SP) {
9235ffd83dbSDimitry Andric   assert(InsertBefore);
9245ffd83dbSDimitry Andric   if (InsertBefore->getDebugLoc())
9255ffd83dbSDimitry Andric     return InsertBefore->getDebugLoc();
9265ffd83dbSDimitry Andric   const Instruction *Prev = InsertBefore->getPrevNode();
9275ffd83dbSDimitry Andric   if (Prev && Prev->getDebugLoc())
9285ffd83dbSDimitry Andric     return Prev->getDebugLoc();
9295ffd83dbSDimitry Andric   if (SP)
9305ffd83dbSDimitry Andric     return DILocation::get(SP->getContext(), SP->getLine(), 1, SP);
9315ffd83dbSDimitry Andric   return DebugLoc();
9325ffd83dbSDimitry Andric }
9335ffd83dbSDimitry Andric 
9340b57cec5SDimitry Andric bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) {
9350b57cec5SDimitry Andric   Module &M = *F.getParent();
9360b57cec5SDimitry Andric   LLVMContext &C = F.getContext();
9370b57cec5SDimitry Andric   IRBuilder<> IRB(C);
9380b57cec5SDimitry Andric   SmallVector<Instruction *, 64> ToErase;
9390b57cec5SDimitry Andric   // Vector of %setjmpTable values
9400b57cec5SDimitry Andric   std::vector<Instruction *> SetjmpTableInsts;
9410b57cec5SDimitry Andric   // Vector of %setjmpTableSize values
9420b57cec5SDimitry Andric   std::vector<Instruction *> SetjmpTableSizeInsts;
9430b57cec5SDimitry Andric 
9440b57cec5SDimitry Andric   // Setjmp preparation
9450b57cec5SDimitry Andric 
9460b57cec5SDimitry Andric   // This instruction effectively means %setjmpTableSize = 4.
9470b57cec5SDimitry Andric   // We create this as an instruction intentionally, and we don't want to fold
9480b57cec5SDimitry Andric   // this instruction to a constant 4, because this value will be used in
9490b57cec5SDimitry Andric   // SSAUpdater.AddAvailableValue(...) later.
9500b57cec5SDimitry Andric   BasicBlock &EntryBB = F.getEntryBlock();
9515ffd83dbSDimitry Andric   DebugLoc FirstDL = getOrCreateDebugLoc(&*EntryBB.begin(), F.getSubprogram());
9520b57cec5SDimitry Andric   BinaryOperator *SetjmpTableSize = BinaryOperator::Create(
9530b57cec5SDimitry Andric       Instruction::Add, IRB.getInt32(4), IRB.getInt32(0), "setjmpTableSize",
9540b57cec5SDimitry Andric       &*EntryBB.getFirstInsertionPt());
9555ffd83dbSDimitry Andric   SetjmpTableSize->setDebugLoc(FirstDL);
9560b57cec5SDimitry Andric   // setjmpTable = (int *) malloc(40);
9570b57cec5SDimitry Andric   Instruction *SetjmpTable = CallInst::CreateMalloc(
9580b57cec5SDimitry Andric       SetjmpTableSize, IRB.getInt32Ty(), IRB.getInt32Ty(), IRB.getInt32(40),
9590b57cec5SDimitry Andric       nullptr, nullptr, "setjmpTable");
9605ffd83dbSDimitry Andric   SetjmpTable->setDebugLoc(FirstDL);
9615ffd83dbSDimitry Andric   // CallInst::CreateMalloc may return a bitcast instruction if the result types
9625ffd83dbSDimitry Andric   // mismatch. We need to set the debug loc for the original call too.
9635ffd83dbSDimitry Andric   auto *MallocCall = SetjmpTable->stripPointerCasts();
9645ffd83dbSDimitry Andric   if (auto *MallocCallI = dyn_cast<Instruction>(MallocCall)) {
9655ffd83dbSDimitry Andric     MallocCallI->setDebugLoc(FirstDL);
9665ffd83dbSDimitry Andric   }
9670b57cec5SDimitry Andric   // setjmpTable[0] = 0;
9680b57cec5SDimitry Andric   IRB.SetInsertPoint(SetjmpTableSize);
9690b57cec5SDimitry Andric   IRB.CreateStore(IRB.getInt32(0), SetjmpTable);
9700b57cec5SDimitry Andric   SetjmpTableInsts.push_back(SetjmpTable);
9710b57cec5SDimitry Andric   SetjmpTableSizeInsts.push_back(SetjmpTableSize);
9720b57cec5SDimitry Andric 
9730b57cec5SDimitry Andric   // Setjmp transformation
9740b57cec5SDimitry Andric   std::vector<PHINode *> SetjmpRetPHIs;
9750b57cec5SDimitry Andric   Function *SetjmpF = M.getFunction("setjmp");
9760b57cec5SDimitry Andric   for (User *U : SetjmpF->users()) {
9770b57cec5SDimitry Andric     auto *CI = dyn_cast<CallInst>(U);
9780b57cec5SDimitry Andric     if (!CI)
9790b57cec5SDimitry Andric       report_fatal_error("Does not support indirect calls to setjmp");
9800b57cec5SDimitry Andric 
9810b57cec5SDimitry Andric     BasicBlock *BB = CI->getParent();
9820b57cec5SDimitry Andric     if (BB->getParent() != &F) // in other function
9830b57cec5SDimitry Andric       continue;
9840b57cec5SDimitry Andric 
9850b57cec5SDimitry Andric     // The tail is everything right after the call, and will be reached once
9860b57cec5SDimitry Andric     // when setjmp is called, and later when longjmp returns to the setjmp
9870b57cec5SDimitry Andric     BasicBlock *Tail = SplitBlock(BB, CI->getNextNode());
9880b57cec5SDimitry Andric     // Add a phi to the tail, which will be the output of setjmp, which
9890b57cec5SDimitry Andric     // indicates if this is the first call or a longjmp back. The phi directly
9900b57cec5SDimitry Andric     // uses the right value based on where we arrive from
9910b57cec5SDimitry Andric     IRB.SetInsertPoint(Tail->getFirstNonPHI());
9920b57cec5SDimitry Andric     PHINode *SetjmpRet = IRB.CreatePHI(IRB.getInt32Ty(), 2, "setjmp.ret");
9930b57cec5SDimitry Andric 
9940b57cec5SDimitry Andric     // setjmp initial call returns 0
9950b57cec5SDimitry Andric     SetjmpRet->addIncoming(IRB.getInt32(0), BB);
9960b57cec5SDimitry Andric     // The proper output is now this, not the setjmp call itself
9970b57cec5SDimitry Andric     CI->replaceAllUsesWith(SetjmpRet);
9980b57cec5SDimitry Andric     // longjmp returns to the setjmp will add themselves to this phi
9990b57cec5SDimitry Andric     SetjmpRetPHIs.push_back(SetjmpRet);
10000b57cec5SDimitry Andric 
10010b57cec5SDimitry Andric     // Fix call target
10020b57cec5SDimitry Andric     // Our index in the function is our place in the array + 1 to avoid index
10030b57cec5SDimitry Andric     // 0, because index 0 means the longjmp is not ours to handle.
10040b57cec5SDimitry Andric     IRB.SetInsertPoint(CI);
10050b57cec5SDimitry Andric     Value *Args[] = {CI->getArgOperand(0), IRB.getInt32(SetjmpRetPHIs.size()),
10060b57cec5SDimitry Andric                      SetjmpTable, SetjmpTableSize};
10070b57cec5SDimitry Andric     Instruction *NewSetjmpTable =
10080b57cec5SDimitry Andric         IRB.CreateCall(SaveSetjmpF, Args, "setjmpTable");
10090b57cec5SDimitry Andric     Instruction *NewSetjmpTableSize =
10100b57cec5SDimitry Andric         IRB.CreateCall(GetTempRet0Func, None, "setjmpTableSize");
10110b57cec5SDimitry Andric     SetjmpTableInsts.push_back(NewSetjmpTable);
10120b57cec5SDimitry Andric     SetjmpTableSizeInsts.push_back(NewSetjmpTableSize);
10130b57cec5SDimitry Andric     ToErase.push_back(CI);
10140b57cec5SDimitry Andric   }
10150b57cec5SDimitry Andric 
10160b57cec5SDimitry Andric   // Update each call that can longjmp so it can return to a setjmp where
10170b57cec5SDimitry Andric   // relevant.
10180b57cec5SDimitry Andric 
10190b57cec5SDimitry Andric   // Because we are creating new BBs while processing and don't want to make
10200b57cec5SDimitry Andric   // all these newly created BBs candidates again for longjmp processing, we
10210b57cec5SDimitry Andric   // first make the vector of candidate BBs.
10220b57cec5SDimitry Andric   std::vector<BasicBlock *> BBs;
10230b57cec5SDimitry Andric   for (BasicBlock &BB : F)
10240b57cec5SDimitry Andric     BBs.push_back(&BB);
10250b57cec5SDimitry Andric 
10260b57cec5SDimitry Andric   // BBs.size() will change within the loop, so we query it every time
10270b57cec5SDimitry Andric   for (unsigned I = 0; I < BBs.size(); I++) {
10280b57cec5SDimitry Andric     BasicBlock *BB = BBs[I];
10290b57cec5SDimitry Andric     for (Instruction &I : *BB) {
10300b57cec5SDimitry Andric       assert(!isa<InvokeInst>(&I));
10310b57cec5SDimitry Andric       auto *CI = dyn_cast<CallInst>(&I);
10320b57cec5SDimitry Andric       if (!CI)
10330b57cec5SDimitry Andric         continue;
10340b57cec5SDimitry Andric 
10355ffd83dbSDimitry Andric       const Value *Callee = CI->getCalledOperand();
10360b57cec5SDimitry Andric       if (!canLongjmp(M, Callee))
10370b57cec5SDimitry Andric         continue;
10388bcb0991SDimitry Andric       if (isEmAsmCall(M, Callee))
10398bcb0991SDimitry Andric         report_fatal_error("Cannot use EM_ASM* alongside setjmp/longjmp in " +
10408bcb0991SDimitry Andric                                F.getName() +
10418bcb0991SDimitry Andric                                ". Please consider using EM_JS, or move the "
10428bcb0991SDimitry Andric                                "EM_ASM into another function.",
10438bcb0991SDimitry Andric                            false);
10440b57cec5SDimitry Andric 
10450b57cec5SDimitry Andric       Value *Threw = nullptr;
10460b57cec5SDimitry Andric       BasicBlock *Tail;
10478bcb0991SDimitry Andric       if (Callee->getName().startswith("__invoke_")) {
10480b57cec5SDimitry Andric         // If invoke wrapper has already been generated for this call in
10490b57cec5SDimitry Andric         // previous EH phase, search for the load instruction
10500b57cec5SDimitry Andric         // %__THREW__.val = __THREW__;
10510b57cec5SDimitry Andric         // in postamble after the invoke wrapper call
10520b57cec5SDimitry Andric         LoadInst *ThrewLI = nullptr;
10530b57cec5SDimitry Andric         StoreInst *ThrewResetSI = nullptr;
10540b57cec5SDimitry Andric         for (auto I = std::next(BasicBlock::iterator(CI)), IE = BB->end();
10550b57cec5SDimitry Andric              I != IE; ++I) {
10560b57cec5SDimitry Andric           if (auto *LI = dyn_cast<LoadInst>(I))
10570b57cec5SDimitry Andric             if (auto *GV = dyn_cast<GlobalVariable>(LI->getPointerOperand()))
10580b57cec5SDimitry Andric               if (GV == ThrewGV) {
10590b57cec5SDimitry Andric                 Threw = ThrewLI = LI;
10600b57cec5SDimitry Andric                 break;
10610b57cec5SDimitry Andric               }
10620b57cec5SDimitry Andric         }
10630b57cec5SDimitry Andric         // Search for the store instruction after the load above
10640b57cec5SDimitry Andric         // __THREW__ = 0;
10650b57cec5SDimitry Andric         for (auto I = std::next(BasicBlock::iterator(ThrewLI)), IE = BB->end();
10660b57cec5SDimitry Andric              I != IE; ++I) {
10670b57cec5SDimitry Andric           if (auto *SI = dyn_cast<StoreInst>(I))
10680b57cec5SDimitry Andric             if (auto *GV = dyn_cast<GlobalVariable>(SI->getPointerOperand()))
10690b57cec5SDimitry Andric               if (GV == ThrewGV && SI->getValueOperand() == IRB.getInt32(0)) {
10700b57cec5SDimitry Andric                 ThrewResetSI = SI;
10710b57cec5SDimitry Andric                 break;
10720b57cec5SDimitry Andric               }
10730b57cec5SDimitry Andric         }
10740b57cec5SDimitry Andric         assert(Threw && ThrewLI && "Cannot find __THREW__ load after invoke");
10750b57cec5SDimitry Andric         assert(ThrewResetSI && "Cannot find __THREW__ store after invoke");
10760b57cec5SDimitry Andric         Tail = SplitBlock(BB, ThrewResetSI->getNextNode());
10770b57cec5SDimitry Andric 
10780b57cec5SDimitry Andric       } else {
10790b57cec5SDimitry Andric         // Wrap call with invoke wrapper and generate preamble/postamble
10800b57cec5SDimitry Andric         Threw = wrapInvoke(CI);
10810b57cec5SDimitry Andric         ToErase.push_back(CI);
10820b57cec5SDimitry Andric         Tail = SplitBlock(BB, CI->getNextNode());
10830b57cec5SDimitry Andric       }
10840b57cec5SDimitry Andric 
10850b57cec5SDimitry Andric       // We need to replace the terminator in Tail - SplitBlock makes BB go
10860b57cec5SDimitry Andric       // straight to Tail, we need to check if a longjmp occurred, and go to the
10870b57cec5SDimitry Andric       // right setjmp-tail if so
10880b57cec5SDimitry Andric       ToErase.push_back(BB->getTerminator());
10890b57cec5SDimitry Andric 
10900b57cec5SDimitry Andric       // Generate a function call to testSetjmp function and preamble/postamble
10910b57cec5SDimitry Andric       // code to figure out (1) whether longjmp occurred (2) if longjmp
10920b57cec5SDimitry Andric       // occurred, which setjmp it corresponds to
10930b57cec5SDimitry Andric       Value *Label = nullptr;
10940b57cec5SDimitry Andric       Value *LongjmpResult = nullptr;
10950b57cec5SDimitry Andric       BasicBlock *EndBB = nullptr;
10965ffd83dbSDimitry Andric       wrapTestSetjmp(BB, CI->getDebugLoc(), Threw, SetjmpTable, SetjmpTableSize,
10975ffd83dbSDimitry Andric                      Label, LongjmpResult, EndBB);
10980b57cec5SDimitry Andric       assert(Label && LongjmpResult && EndBB);
10990b57cec5SDimitry Andric 
11000b57cec5SDimitry Andric       // Create switch instruction
11010b57cec5SDimitry Andric       IRB.SetInsertPoint(EndBB);
11025ffd83dbSDimitry Andric       IRB.SetCurrentDebugLocation(EndBB->getInstList().back().getDebugLoc());
11030b57cec5SDimitry Andric       SwitchInst *SI = IRB.CreateSwitch(Label, Tail, SetjmpRetPHIs.size());
11040b57cec5SDimitry Andric       // -1 means no longjmp happened, continue normally (will hit the default
11050b57cec5SDimitry Andric       // switch case). 0 means a longjmp that is not ours to handle, needs a
11060b57cec5SDimitry Andric       // rethrow. Otherwise the index is the same as the index in P+1 (to avoid
11070b57cec5SDimitry Andric       // 0).
11080b57cec5SDimitry Andric       for (unsigned I = 0; I < SetjmpRetPHIs.size(); I++) {
11090b57cec5SDimitry Andric         SI->addCase(IRB.getInt32(I + 1), SetjmpRetPHIs[I]->getParent());
11100b57cec5SDimitry Andric         SetjmpRetPHIs[I]->addIncoming(LongjmpResult, EndBB);
11110b57cec5SDimitry Andric       }
11120b57cec5SDimitry Andric 
11130b57cec5SDimitry Andric       // We are splitting the block here, and must continue to find other calls
11140b57cec5SDimitry Andric       // in the block - which is now split. so continue to traverse in the Tail
11150b57cec5SDimitry Andric       BBs.push_back(Tail);
11160b57cec5SDimitry Andric     }
11170b57cec5SDimitry Andric   }
11180b57cec5SDimitry Andric 
11190b57cec5SDimitry Andric   // Erase everything we no longer need in this function
11200b57cec5SDimitry Andric   for (Instruction *I : ToErase)
11210b57cec5SDimitry Andric     I->eraseFromParent();
11220b57cec5SDimitry Andric 
11230b57cec5SDimitry Andric   // Free setjmpTable buffer before each return instruction
11240b57cec5SDimitry Andric   for (BasicBlock &BB : F) {
11250b57cec5SDimitry Andric     Instruction *TI = BB.getTerminator();
11265ffd83dbSDimitry Andric     if (isa<ReturnInst>(TI)) {
11275ffd83dbSDimitry Andric       DebugLoc DL = getOrCreateDebugLoc(TI, F.getSubprogram());
11285ffd83dbSDimitry Andric       auto *Free = CallInst::CreateFree(SetjmpTable, TI);
11295ffd83dbSDimitry Andric       Free->setDebugLoc(DL);
11305ffd83dbSDimitry Andric       // CallInst::CreateFree may create a bitcast instruction if its argument
11315ffd83dbSDimitry Andric       // types mismatch. We need to set the debug loc for the bitcast too.
11325ffd83dbSDimitry Andric       if (auto *FreeCallI = dyn_cast<CallInst>(Free)) {
11335ffd83dbSDimitry Andric         if (auto *BitCastI = dyn_cast<BitCastInst>(FreeCallI->getArgOperand(0)))
11345ffd83dbSDimitry Andric           BitCastI->setDebugLoc(DL);
11355ffd83dbSDimitry Andric       }
11365ffd83dbSDimitry Andric     }
11370b57cec5SDimitry Andric   }
11380b57cec5SDimitry Andric 
11390b57cec5SDimitry Andric   // Every call to saveSetjmp can change setjmpTable and setjmpTableSize
11400b57cec5SDimitry Andric   // (when buffer reallocation occurs)
11410b57cec5SDimitry Andric   // entry:
11420b57cec5SDimitry Andric   //   setjmpTableSize = 4;
11430b57cec5SDimitry Andric   //   setjmpTable = (int *) malloc(40);
11440b57cec5SDimitry Andric   //   setjmpTable[0] = 0;
11450b57cec5SDimitry Andric   // ...
11460b57cec5SDimitry Andric   // somebb:
11470b57cec5SDimitry Andric   //   setjmpTable = saveSetjmp(buf, label, setjmpTable, setjmpTableSize);
11480b57cec5SDimitry Andric   //   setjmpTableSize = getTempRet0();
11490b57cec5SDimitry Andric   // So we need to make sure the SSA for these variables is valid so that every
11500b57cec5SDimitry Andric   // saveSetjmp and testSetjmp calls have the correct arguments.
11510b57cec5SDimitry Andric   SSAUpdater SetjmpTableSSA;
11520b57cec5SDimitry Andric   SSAUpdater SetjmpTableSizeSSA;
11530b57cec5SDimitry Andric   SetjmpTableSSA.Initialize(Type::getInt32PtrTy(C), "setjmpTable");
11540b57cec5SDimitry Andric   SetjmpTableSizeSSA.Initialize(Type::getInt32Ty(C), "setjmpTableSize");
11550b57cec5SDimitry Andric   for (Instruction *I : SetjmpTableInsts)
11560b57cec5SDimitry Andric     SetjmpTableSSA.AddAvailableValue(I->getParent(), I);
11570b57cec5SDimitry Andric   for (Instruction *I : SetjmpTableSizeInsts)
11580b57cec5SDimitry Andric     SetjmpTableSizeSSA.AddAvailableValue(I->getParent(), I);
11590b57cec5SDimitry Andric 
11600b57cec5SDimitry Andric   for (auto UI = SetjmpTable->use_begin(), UE = SetjmpTable->use_end();
11610b57cec5SDimitry Andric        UI != UE;) {
11620b57cec5SDimitry Andric     // Grab the use before incrementing the iterator.
11630b57cec5SDimitry Andric     Use &U = *UI;
11640b57cec5SDimitry Andric     // Increment the iterator before removing the use from the list.
11650b57cec5SDimitry Andric     ++UI;
11660b57cec5SDimitry Andric     if (auto *I = dyn_cast<Instruction>(U.getUser()))
11670b57cec5SDimitry Andric       if (I->getParent() != &EntryBB)
11680b57cec5SDimitry Andric         SetjmpTableSSA.RewriteUse(U);
11690b57cec5SDimitry Andric   }
11700b57cec5SDimitry Andric   for (auto UI = SetjmpTableSize->use_begin(), UE = SetjmpTableSize->use_end();
11710b57cec5SDimitry Andric        UI != UE;) {
11720b57cec5SDimitry Andric     Use &U = *UI;
11730b57cec5SDimitry Andric     ++UI;
11740b57cec5SDimitry Andric     if (auto *I = dyn_cast<Instruction>(U.getUser()))
11750b57cec5SDimitry Andric       if (I->getParent() != &EntryBB)
11760b57cec5SDimitry Andric         SetjmpTableSizeSSA.RewriteUse(U);
11770b57cec5SDimitry Andric   }
11780b57cec5SDimitry Andric 
11790b57cec5SDimitry Andric   // Finally, our modifications to the cfg can break dominance of SSA variables.
11800b57cec5SDimitry Andric   // For example, in this code,
11810b57cec5SDimitry Andric   // if (x()) { .. setjmp() .. }
11820b57cec5SDimitry Andric   // if (y()) { .. longjmp() .. }
11830b57cec5SDimitry Andric   // We must split the longjmp block, and it can jump into the block splitted
11840b57cec5SDimitry Andric   // from setjmp one. But that means that when we split the setjmp block, it's
11850b57cec5SDimitry Andric   // first part no longer dominates its second part - there is a theoretically
11860b57cec5SDimitry Andric   // possible control flow path where x() is false, then y() is true and we
11870b57cec5SDimitry Andric   // reach the second part of the setjmp block, without ever reaching the first
11880b57cec5SDimitry Andric   // part. So, we rebuild SSA form here.
11890b57cec5SDimitry Andric   rebuildSSA(F);
11900b57cec5SDimitry Andric   return true;
11910b57cec5SDimitry Andric }
1192