Coverage Report

Created: 2019-07-24 05:18

/Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp
Line
Count
Source (jump to first uncovered line)
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
11
/// function calls in order to use Emscripten's JavaScript try and catch
12
/// mechanism.
13
///
14
/// To handle exceptions and setjmp/longjmps, this scheme relies on JavaScript's
15
/// try and catch syntax and relevant exception-related libraries implemented
16
/// in JavaScript glue code that will be produced by Emscripten. This is similar
17
/// to the current Emscripten asm.js exception handling in fastcomp. For
18
/// fastcomp's EH / SjLj scheme, see these files in fastcomp LLVM branch:
19
/// (Location: https://github.com/kripken/emscripten-fastcomp)
20
/// lib/Target/JSBackend/NaCl/LowerEmExceptionsPass.cpp
21
/// lib/Target/JSBackend/NaCl/LowerEmSetjmp.cpp
22
/// lib/Target/JSBackend/JSBackend.cpp
23
/// lib/Target/JSBackend/CallHandlers.h
24
///
25
/// * Exception handling
26
/// This pass lowers invokes and landingpads into library functions in JS glue
27
/// code. Invokes are lowered into function wrappers called invoke wrappers that
28
/// exist in JS side, which wraps the original function call with JS try-catch.
29
/// If an exception occurred, cxa_throw() function in JS side sets some
30
/// variables (see below) so we can check whether an exception occurred from
31
/// wasm code and handle it appropriately.
32
///
33
/// * Setjmp-longjmp handling
34
/// This pass lowers setjmp to a reasonably-performant approach for emscripten.
35
/// The idea is that each block with a setjmp is broken up into two parts: the
36
/// part containing setjmp and the part right after the setjmp. The latter part
37
/// is either reached from the setjmp, or later from a longjmp. To handle the
38
/// longjmp, all calls that might longjmp are also called using invoke wrappers
39
/// and thus JS / try-catch. JS longjmp() function also sets some variables so
40
/// we can check / whether a longjmp occurred from wasm code. Each block with a
41
/// function call that might longjmp is also split up after the longjmp call.
42
/// After the longjmp call, we check whether a longjmp occurred, and if it did,
43
/// which setjmp it corresponds to, and jump to the right post-setjmp block.
44
/// We assume setjmp-longjmp handling always run after EH handling, which means
45
/// we don't expect any exception-related instructions when SjLj runs.
46
/// FIXME Currently this scheme does not support indirect call of setjmp,
47
/// because of the limitation of the scheme itself. fastcomp does not support it
48
/// either.
49
///
50
/// In detail, this pass does following things:
51
///
52
/// 1) Assumes the existence of global variables: __THREW__, __threwValue
53
///    __THREW__ and __threwValue will be set in invoke wrappers
54
///    in JS glue code. For what invoke wrappers are, refer to 3). These
55
///    variables are used for both exceptions and setjmp/longjmps.
56
///    __THREW__ indicates whether an exception or a longjmp occurred or not. 0
57
///    means nothing occurred, 1 means an exception occurred, and other numbers
58
///    mean a longjmp occurred. In the case of longjmp, __threwValue variable
59
///    indicates the corresponding setjmp buffer the longjmp corresponds to.
60
///
61
/// * Exception handling
62
///
63
/// 2) We assume the existence of setThrew and setTempRet0/getTempRet0 functions
64
///    at link time.
65
///    The global variables in 1) will exist in wasm address space,
66
///    but their values should be set in JS code, so these functions
67
///    as interfaces to JS glue code. These functions are equivalent to the
68
///    following JS functions, which actually exist in asm.js version of JS
69
///    library.
70
///
71
///    function setThrew(threw, value) {
72
///      if (__THREW__ == 0) {
73
///        __THREW__ = threw;
74
///        __threwValue = value;
75
///      }
76
///    }
77
//
78
///    setTempRet0 is called from __cxa_find_matching_catch() in JS glue code.
79
///
80
///    In exception handling, getTempRet0 indicates the type of an exception
81
///    caught, and in setjmp/longjmp, it means the second argument to longjmp
82
///    function.
83
///
84
/// 3) Lower
85
///      invoke @func(arg1, arg2) to label %invoke.cont unwind label %lpad
86
///    into
87
///      __THREW__ = 0;
88
///      call @__invoke_SIG(func, arg1, arg2)
89
///      %__THREW__.val = __THREW__;
90
///      __THREW__ = 0;
91
///      if (%__THREW__.val == 1)
92
///        goto %lpad
93
///      else
94
///         goto %invoke.cont
95
///    SIG is a mangled string generated based on the LLVM IR-level function
96
///    signature. After LLVM IR types are lowered to the target wasm types,
97
///    the names for these wrappers will change based on wasm types as well,
98
///    as in invoke_vi (function takes an int and returns void). The bodies of
99
///    these wrappers will be generated in JS glue code, and inside those
100
///    wrappers we use JS try-catch to generate actual exception effects. It
101
///    also calls the original callee function. An example wrapper in JS code
102
///    would look like this:
103
///      function invoke_vi(index,a1) {
104
///        try {
105
///          Module["dynCall_vi"](index,a1); // This calls original callee
106
///        } catch(e) {
107
///          if (typeof e !== 'number' && e !== 'longjmp') throw e;
108
///          asm["setThrew"](1, 0); // setThrew is called here
109
///        }
110
///      }
111
///    If an exception is thrown, __THREW__ will be set to true in a wrapper,
112
///    so we can jump to the right BB based on this value.
113
///
114
/// 4) Lower
115
///      %val = landingpad catch c1 catch c2 catch c3 ...
116
///      ... use %val ...
117
///    into
118
///      %fmc = call @__cxa_find_matching_catch_N(c1, c2, c3, ...)
119
///      %val = {%fmc, getTempRet0()}
120
///      ... use %val ...
121
///    Here N is a number calculated based on the number of clauses.
122
///    setTempRet0 is called from __cxa_find_matching_catch() in JS glue code.
123
///
124
/// 5) Lower
125
///      resume {%a, %b}
126
///    into
127
///      call @__resumeException(%a)
128
///    where __resumeException() is a function in JS glue code.
129
///
130
/// 6) Lower
131
///      call @llvm.eh.typeid.for(type) (intrinsic)
132
///    into
133
///      call @llvm_eh_typeid_for(type)
134
///    llvm_eh_typeid_for function will be generated in JS glue code.
135
///
136
/// * Setjmp / Longjmp handling
137
///
138
/// In case calls to longjmp() exists
139
///
140
/// 1) Lower
141
///      longjmp(buf, value)
142
///    into
143
///      emscripten_longjmp_jmpbuf(buf, value)
144
///    emscripten_longjmp_jmpbuf will be lowered to emscripten_longjmp later.
145
///
146
/// In case calls to setjmp() exists
147
///
148
/// 2) In the function entry that calls setjmp, initialize setjmpTable and
149
///    sejmpTableSize as follows:
150
///      setjmpTableSize = 4;
151
///      setjmpTable = (int *) malloc(40);
152
///      setjmpTable[0] = 0;
153
///    setjmpTable and setjmpTableSize are used in saveSetjmp() function in JS
154
///    code.
155
///
156
/// 3) Lower
157
///      setjmp(buf)
158
///    into
159
///      setjmpTable = saveSetjmp(buf, label, setjmpTable, setjmpTableSize);
160
///      setjmpTableSize = getTempRet0();
161
///    For each dynamic setjmp call, setjmpTable stores its ID (a number which
162
///    is incrementally assigned from 0) and its label (a unique number that
163
///    represents each callsite of setjmp). When we need more entries in
164
///    setjmpTable, it is reallocated in saveSetjmp() in JS code and it will
165
///    return the new table address, and assign the new table size in
166
///    setTempRet0(). saveSetjmp also stores the setjmp's ID into the buffer
167
///    buf. A BB with setjmp is split into two after setjmp call in order to
168
///    make the post-setjmp BB the possible destination of longjmp BB.
169
///
170
///
171
/// 4) Lower every call that might longjmp into
172
///      __THREW__ = 0;
173
///      call @__invoke_SIG(func, arg1, arg2)
174
///      %__THREW__.val = __THREW__;
175
///      __THREW__ = 0;
176
///      if (%__THREW__.val != 0 & __threwValue != 0) {
177
///        %label = testSetjmp(mem[%__THREW__.val], setjmpTable,
178
///                            setjmpTableSize);
179
///        if (%label == 0)
180
///          emscripten_longjmp(%__THREW__.val, __threwValue);
181
///        setTempRet0(__threwValue);
182
///      } else {
183
///        %label = -1;
184
///      }
185
///      longjmp_result = getTempRet0();
186
///      switch label {
187
///        label 1: goto post-setjmp BB 1
188
///        label 2: goto post-setjmp BB 2
189
///        ...
190
///        default: goto splitted next BB
191
///      }
192
///    testSetjmp examines setjmpTable to see if there is a matching setjmp
193
///    call. After calling an invoke wrapper, if a longjmp occurred, __THREW__
194
///    will be the address of matching jmp_buf buffer and __threwValue be the
195
///    second argument to longjmp. mem[__THREW__.val] is a setjmp ID that is
196
///    stored in saveSetjmp. testSetjmp returns a setjmp label, a unique ID to
197
///    each setjmp callsite. Label 0 means this longjmp buffer does not
198
///    correspond to one of the setjmp callsites in this function, so in this
199
///    case we just chain the longjmp to the caller. (Here we call
200
///    emscripten_longjmp, which is different from emscripten_longjmp_jmpbuf.
201
///    emscripten_longjmp_jmpbuf takes jmp_buf as its first argument, while
202
///    emscripten_longjmp takes an int. Both of them will eventually be lowered
203
///    to emscripten_longjmp in s2wasm, but here we need two signatures - we
204
///    can't translate an int value to a jmp_buf.)
205
///    Label -1 means no longjmp occurred. Otherwise we jump to the right
206
///    post-setjmp BB based on the label.
207
///
208
///===----------------------------------------------------------------------===//
209
210
#include "WebAssembly.h"
211
#include "llvm/IR/CallSite.h"
212
#include "llvm/IR/Dominators.h"
213
#include "llvm/IR/IRBuilder.h"
214
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
215
#include "llvm/Transforms/Utils/SSAUpdater.h"
216
217
using namespace llvm;
218
219
#define DEBUG_TYPE "wasm-lower-em-ehsjlj"
220
221
static cl::list<std::string>
222
    EHWhitelist("emscripten-cxx-exceptions-whitelist",
223
                cl::desc("The list of function names in which Emscripten-style "
224
                         "exception handling is enabled (see emscripten "
225
                         "EMSCRIPTEN_CATCHING_WHITELIST options)"),
226
                cl::CommaSeparated);
227
228
namespace {
229
class WebAssemblyLowerEmscriptenEHSjLj final : public ModulePass {
230
  static const char *ResumeFName;
231
  static const char *EHTypeIDFName;
232
  static const char *EmLongjmpFName;
233
  static const char *EmLongjmpJmpbufFName;
234
  static const char *SaveSetjmpFName;
235
  static const char *TestSetjmpFName;
236
  static const char *FindMatchingCatchPrefix;
237
  static const char *InvokePrefix;
238
239
  bool EnableEH;   // Enable exception handling
240
  bool EnableSjLj; // Enable setjmp/longjmp handling
241
242
  GlobalVariable *ThrewGV = nullptr;
243
  GlobalVariable *ThrewValueGV = nullptr;
244
  Function *GetTempRet0Func = nullptr;
245
  Function *SetTempRet0Func = nullptr;
246
  Function *ResumeF = nullptr;
247
  Function *EHTypeIDF = nullptr;
248
  Function *EmLongjmpF = nullptr;
249
  Function *EmLongjmpJmpbufF = nullptr;
250
  Function *SaveSetjmpF = nullptr;
251
  Function *TestSetjmpF = nullptr;
252
253
  // __cxa_find_matching_catch_N functions.
254
  // Indexed by the number of clauses in an original landingpad instruction.
255
  DenseMap<int, Function *> FindMatchingCatches;
256
  // Map of <function signature string, invoke_ wrappers>
257
  StringMap<Function *> InvokeWrappers;
258
  // Set of whitelisted function names for exception handling
259
  std::set<std::string> EHWhitelistSet;
260
261
0
  StringRef getPassName() const override {
262
0
    return "WebAssembly Lower Emscripten Exceptions";
263
0
  }
264
265
  bool runEHOnFunction(Function &F);
266
  bool runSjLjOnFunction(Function &F);
267
  Function *getFindMatchingCatch(Module &M, unsigned NumClauses);
268
269
  template <typename CallOrInvoke> Value *wrapInvoke(CallOrInvoke *CI);
270
  void wrapTestSetjmp(BasicBlock *BB, Instruction *InsertPt, Value *Threw,
271
                      Value *SetjmpTable, Value *SetjmpTableSize, Value *&Label,
272
                      Value *&LongjmpResult, BasicBlock *&EndBB);
273
  template <typename CallOrInvoke> Function *getInvokeWrapper(CallOrInvoke *CI);
274
275
33
  bool areAllExceptionsAllowed() const { return EHWhitelistSet.empty(); }
276
  bool canLongjmp(Module &M, const Value *Callee) const;
277
278
  void rebuildSSA(Function &F);
279
280
public:
281
  static char ID;
282
283
  WebAssemblyLowerEmscriptenEHSjLj(bool EnableEH = true, bool EnableSjLj = true)
284
8
      : ModulePass(ID), EnableEH(EnableEH), EnableSjLj(EnableSjLj) {
285
8
    EHWhitelistSet.insert(EHWhitelist.begin(), EHWhitelist.end());
286
8
  }
287
  bool runOnModule(Module &M) override;
288
289
8
  void getAnalysisUsage(AnalysisUsage &AU) const override {
290
8
    AU.addRequired<DominatorTreeWrapperPass>();
291
8
  }
292
};
293
} // End anonymous namespace
294
295
const char *WebAssemblyLowerEmscriptenEHSjLj::ResumeFName = "__resumeException";
296
const char *WebAssemblyLowerEmscriptenEHSjLj::EHTypeIDFName =
297
    "llvm_eh_typeid_for";
298
const char *WebAssemblyLowerEmscriptenEHSjLj::EmLongjmpFName =
299
    "emscripten_longjmp";
300
const char *WebAssemblyLowerEmscriptenEHSjLj::EmLongjmpJmpbufFName =
301
    "emscripten_longjmp_jmpbuf";
302
const char *WebAssemblyLowerEmscriptenEHSjLj::SaveSetjmpFName = "saveSetjmp";
303
const char *WebAssemblyLowerEmscriptenEHSjLj::TestSetjmpFName = "testSetjmp";
304
const char *WebAssemblyLowerEmscriptenEHSjLj::FindMatchingCatchPrefix =
305
    "__cxa_find_matching_catch_";
306
const char *WebAssemblyLowerEmscriptenEHSjLj::InvokePrefix = "__invoke_";
307
308
char WebAssemblyLowerEmscriptenEHSjLj::ID = 0;
309
INITIALIZE_PASS(WebAssemblyLowerEmscriptenEHSjLj, DEBUG_TYPE,
310
                "WebAssembly Lower Emscripten Exceptions / Setjmp / Longjmp",
311
                false, false)
312
313
ModulePass *llvm::createWebAssemblyLowerEmscriptenEHSjLj(bool EnableEH,
314
4
                                                         bool EnableSjLj) {
315
4
  return new WebAssemblyLowerEmscriptenEHSjLj(EnableEH, EnableSjLj);
316
4
}
317
318
20
static bool canThrow(const Value *V) {
319
20
  if (const auto *F = dyn_cast<const Function>(V)) {
320
9
    // Intrinsics cannot throw
321
9
    if (F->isIntrinsic())
322
0
      return false;
323
9
    StringRef Name = F->getName();
324
9
    // leave setjmp and longjmp (mostly) alone, we process them properly later
325
9
    if (Name == "setjmp" || Name == "longjmp")
326
0
      return false;
327
9
    return !F->doesNotThrow();
328
9
  }
329
11
  // not a function, so an indirect call - can throw, we can't tell
330
11
  return true;
331
11
}
332
333
// Get a global variable with the given name.  If it doesn't exist declare it,
334
// which will generate an import and asssumes that it will exist at link time.
335
static GlobalVariable *getGlobalVariableI32(Module &M, IRBuilder<> &IRB,
336
16
                                            const char *Name) {
337
16
338
16
  auto* GV = dyn_cast<GlobalVariable>(M.getOrInsertGlobal(Name, IRB.getInt32Ty()));
339
16
  if (!GV)
340
0
    report_fatal_error(Twine("unable to create global: ") + Name);
341
16
342
16
  return GV;
343
16
}
344
345
// Simple function name mangler.
346
// This function simply takes LLVM's string representation of parameter types
347
// and concatenate them with '_'. There are non-alphanumeric characters but llc
348
// is ok with it, and we need to postprocess these names after the lowering
349
// phase anyway.
350
24
static std::string getSignature(FunctionType *FTy) {
351
24
  std::string Sig;
352
24
  raw_string_ostream OS(Sig);
353
24
  OS << *FTy->getReturnType();
354
24
  for (Type *ParamTy : FTy->params())
355
12
    OS << "_" << *ParamTy;
356
24
  if (FTy->isVarArg())
357
0
    OS << "_...";
358
24
  Sig = OS.str();
359
24
  Sig.erase(remove_if(Sig, isspace), Sig.end());
360
24
  // When s2wasm parses .s file, a comma means the end of an argument. So a
361
24
  // mangled function name can contain any character but a comma.
362
24
  std::replace(Sig.begin(), Sig.end(), ',', '.');
363
24
  return Sig;
364
24
}
365
366
// Returns __cxa_find_matching_catch_N function, where N = NumClauses + 2.
367
// This is because a landingpad instruction contains two more arguments, a
368
// personality function and a cleanup bit, and __cxa_find_matching_catch_N
369
// functions are named after the number of arguments in the original landingpad
370
// instruction.
371
Function *
372
WebAssemblyLowerEmscriptenEHSjLj::getFindMatchingCatch(Module &M,
373
13
                                                       unsigned NumClauses) {
374
13
  if (FindMatchingCatches.count(NumClauses))
375
7
    return FindMatchingCatches[NumClauses];
376
6
  PointerType *Int8PtrTy = Type::getInt8PtrTy(M.getContext());
377
6
  SmallVector<Type *, 16> Args(NumClauses, Int8PtrTy);
378
6
  FunctionType *FTy = FunctionType::get(Int8PtrTy, Args, false);
379
6
  Function *F =
380
6
      Function::Create(FTy, GlobalValue::ExternalLinkage,
381
6
                       FindMatchingCatchPrefix + Twine(NumClauses + 2), &M);
382
6
  FindMatchingCatches[NumClauses] = F;
383
6
  return F;
384
6
}
385
386
// Generate invoke wrapper seqence with preamble and postamble
387
// Preamble:
388
// __THREW__ = 0;
389
// Postamble:
390
// %__THREW__.val = __THREW__; __THREW__ = 0;
391
// Returns %__THREW__.val, which indicates whether an exception is thrown (or
392
// whether longjmp occurred), for future use.
393
template <typename CallOrInvoke>
394
24
Value *WebAssemblyLowerEmscriptenEHSjLj::wrapInvoke(CallOrInvoke *CI) {
395
24
  LLVMContext &C = CI->getModule()->getContext();
396
24
397
24
  // If we are calling a function that is noreturn, we must remove that
398
24
  // attribute. The code we insert here does expect it to return, after we
399
24
  // catch the exception.
400
24
  if (CI->doesNotReturn()) {
401
3
    if (auto *F = dyn_cast<Function>(CI->getCalledValue()))
402
3
      F->removeFnAttr(Attribute::NoReturn);
403
3
    CI->removeAttribute(AttributeList::FunctionIndex, Attribute::NoReturn);
404
3
  }
405
24
406
24
  IRBuilder<> IRB(C);
407
24
  IRB.SetInsertPoint(CI);
408
24
409
24
  // Pre-invoke
410
24
  // __THREW__ = 0;
411
24
  IRB.CreateStore(IRB.getInt32(0), ThrewGV);
412
24
413
24
  // Invoke function wrapper in JavaScript
414
24
  SmallVector<Value *, 16> Args;
415
24
  // Put the pointer to the callee as first argument, so it can be called
416
24
  // within the invoke wrapper later
417
24
  Args.push_back(CI->getCalledValue());
418
24
  Args.append(CI->arg_begin(), CI->arg_end());
419
24
  CallInst *NewCall = IRB.CreateCall(getInvokeWrapper(CI), Args);
420
24
  NewCall->takeName(CI);
421
24
  NewCall->setCallingConv(CI->getCallingConv());
422
24
  NewCall->setDebugLoc(CI->getDebugLoc());
423
24
424
24
  // Because we added the pointer to the callee as first argument, all
425
24
  // argument attribute indices have to be incremented by one.
426
24
  SmallVector<AttributeSet, 8> ArgAttributes;
427
24
  const AttributeList &InvokeAL = CI->getAttributes();
428
24
429
24
  // No attributes for the callee pointer.
430
24
  ArgAttributes.push_back(AttributeSet());
431
24
  // Copy the argument attributes from the original
432
36
  for (unsigned I = 0, E = CI->getNumArgOperands(); I < E; 
++I12
)
433
12
    ArgAttributes.push_back(InvokeAL.getParamAttributes(I));
434
24
435
24
  // Reconstruct the AttributesList based on the vector we constructed.
436
24
  AttributeList NewCallAL =
437
24
      AttributeList::get(C, InvokeAL.getFnAttributes(),
438
24
                         InvokeAL.getRetAttributes(), ArgAttributes);
439
24
  NewCall->setAttributes(NewCallAL);
440
24
441
24
  CI->replaceAllUsesWith(NewCall);
442
24
443
24
  // Post-invoke
444
24
  // %__THREW__.val = __THREW__; __THREW__ = 0;
445
24
  Value *Threw =
446
24
      IRB.CreateLoad(IRB.getInt32Ty(), ThrewGV, ThrewGV->getName() + ".val");
447
24
  IRB.CreateStore(IRB.getInt32(0), ThrewGV);
448
24
  return Threw;
449
24
}
WebAssemblyLowerEmscriptenEHSjLj.cpp:llvm::Value* (anonymous namespace)::WebAssemblyLowerEmscriptenEHSjLj::wrapInvoke<llvm::InvokeInst>(llvm::InvokeInst*)
Line
Count
Source
394
20
Value *WebAssemblyLowerEmscriptenEHSjLj::wrapInvoke(CallOrInvoke *CI) {
395
20
  LLVMContext &C = CI->getModule()->getContext();
396
20
397
20
  // If we are calling a function that is noreturn, we must remove that
398
20
  // attribute. The code we insert here does expect it to return, after we
399
20
  // catch the exception.
400
20
  if (CI->doesNotReturn()) {
401
0
    if (auto *F = dyn_cast<Function>(CI->getCalledValue()))
402
0
      F->removeFnAttr(Attribute::NoReturn);
403
0
    CI->removeAttribute(AttributeList::FunctionIndex, Attribute::NoReturn);
404
0
  }
405
20
406
20
  IRBuilder<> IRB(C);
407
20
  IRB.SetInsertPoint(CI);
408
20
409
20
  // Pre-invoke
410
20
  // __THREW__ = 0;
411
20
  IRB.CreateStore(IRB.getInt32(0), ThrewGV);
412
20
413
20
  // Invoke function wrapper in JavaScript
414
20
  SmallVector<Value *, 16> Args;
415
20
  // Put the pointer to the callee as first argument, so it can be called
416
20
  // within the invoke wrapper later
417
20
  Args.push_back(CI->getCalledValue());
418
20
  Args.append(CI->arg_begin(), CI->arg_end());
419
20
  CallInst *NewCall = IRB.CreateCall(getInvokeWrapper(CI), Args);
420
20
  NewCall->takeName(CI);
421
20
  NewCall->setCallingConv(CI->getCallingConv());
422
20
  NewCall->setDebugLoc(CI->getDebugLoc());
423
20
424
20
  // Because we added the pointer to the callee as first argument, all
425
20
  // argument attribute indices have to be incremented by one.
426
20
  SmallVector<AttributeSet, 8> ArgAttributes;
427
20
  const AttributeList &InvokeAL = CI->getAttributes();
428
20
429
20
  // No attributes for the callee pointer.
430
20
  ArgAttributes.push_back(AttributeSet());
431
20
  // Copy the argument attributes from the original
432
26
  for (unsigned I = 0, E = CI->getNumArgOperands(); I < E; 
++I6
)
433
6
    ArgAttributes.push_back(InvokeAL.getParamAttributes(I));
434
20
435
20
  // Reconstruct the AttributesList based on the vector we constructed.
436
20
  AttributeList NewCallAL =
437
20
      AttributeList::get(C, InvokeAL.getFnAttributes(),
438
20
                         InvokeAL.getRetAttributes(), ArgAttributes);
439
20
  NewCall->setAttributes(NewCallAL);
440
20
441
20
  CI->replaceAllUsesWith(NewCall);
442
20
443
20
  // Post-invoke
444
20
  // %__THREW__.val = __THREW__; __THREW__ = 0;
445
20
  Value *Threw =
446
20
      IRB.CreateLoad(IRB.getInt32Ty(), ThrewGV, ThrewGV->getName() + ".val");
447
20
  IRB.CreateStore(IRB.getInt32(0), ThrewGV);
448
20
  return Threw;
449
20
}
WebAssemblyLowerEmscriptenEHSjLj.cpp:llvm::Value* (anonymous namespace)::WebAssemblyLowerEmscriptenEHSjLj::wrapInvoke<llvm::CallInst>(llvm::CallInst*)
Line
Count
Source
394
4
Value *WebAssemblyLowerEmscriptenEHSjLj::wrapInvoke(CallOrInvoke *CI) {
395
4
  LLVMContext &C = CI->getModule()->getContext();
396
4
397
4
  // If we are calling a function that is noreturn, we must remove that
398
4
  // attribute. The code we insert here does expect it to return, after we
399
4
  // catch the exception.
400
4
  if (CI->doesNotReturn()) {
401
3
    if (auto *F = dyn_cast<Function>(CI->getCalledValue()))
402
3
      F->removeFnAttr(Attribute::NoReturn);
403
3
    CI->removeAttribute(AttributeList::FunctionIndex, Attribute::NoReturn);
404
3
  }
405
4
406
4
  IRBuilder<> IRB(C);
407
4
  IRB.SetInsertPoint(CI);
408
4
409
4
  // Pre-invoke
410
4
  // __THREW__ = 0;
411
4
  IRB.CreateStore(IRB.getInt32(0), ThrewGV);
412
4
413
4
  // Invoke function wrapper in JavaScript
414
4
  SmallVector<Value *, 16> Args;
415
4
  // Put the pointer to the callee as first argument, so it can be called
416
4
  // within the invoke wrapper later
417
4
  Args.push_back(CI->getCalledValue());
418
4
  Args.append(CI->arg_begin(), CI->arg_end());
419
4
  CallInst *NewCall = IRB.CreateCall(getInvokeWrapper(CI), Args);
420
4
  NewCall->takeName(CI);
421
4
  NewCall->setCallingConv(CI->getCallingConv());
422
4
  NewCall->setDebugLoc(CI->getDebugLoc());
423
4
424
4
  // Because we added the pointer to the callee as first argument, all
425
4
  // argument attribute indices have to be incremented by one.
426
4
  SmallVector<AttributeSet, 8> ArgAttributes;
427
4
  const AttributeList &InvokeAL = CI->getAttributes();
428
4
429
4
  // No attributes for the callee pointer.
430
4
  ArgAttributes.push_back(AttributeSet());
431
4
  // Copy the argument attributes from the original
432
10
  for (unsigned I = 0, E = CI->getNumArgOperands(); I < E; 
++I6
)
433
6
    ArgAttributes.push_back(InvokeAL.getParamAttributes(I));
434
4
435
4
  // Reconstruct the AttributesList based on the vector we constructed.
436
4
  AttributeList NewCallAL =
437
4
      AttributeList::get(C, InvokeAL.getFnAttributes(),
438
4
                         InvokeAL.getRetAttributes(), ArgAttributes);
439
4
  NewCall->setAttributes(NewCallAL);
440
4
441
4
  CI->replaceAllUsesWith(NewCall);
442
4
443
4
  // Post-invoke
444
4
  // %__THREW__.val = __THREW__; __THREW__ = 0;
445
4
  Value *Threw =
446
4
      IRB.CreateLoad(IRB.getInt32Ty(), ThrewGV, ThrewGV->getName() + ".val");
447
4
  IRB.CreateStore(IRB.getInt32(0), ThrewGV);
448
4
  return Threw;
449
4
}
450
451
// Get matching invoke wrapper based on callee signature
452
template <typename CallOrInvoke>
453
24
Function *WebAssemblyLowerEmscriptenEHSjLj::getInvokeWrapper(CallOrInvoke *CI) {
454
24
  Module *M = CI->getModule();
455
24
  SmallVector<Type *, 16> ArgTys;
456
24
  Value *Callee = CI->getCalledValue();
457
24
  FunctionType *CalleeFTy;
458
24
  if (auto *F = dyn_cast<Function>(Callee))
459
13
    CalleeFTy = F->getFunctionType();
460
11
  else {
461
11
    auto *CalleeTy = cast<PointerType>(Callee->getType())->getElementType();
462
11
    CalleeFTy = dyn_cast<FunctionType>(CalleeTy);
463
11
  }
464
24
465
24
  std::string Sig = getSignature(CalleeFTy);
466
24
  if (InvokeWrappers.find(Sig) != InvokeWrappers.end())
467
12
    return InvokeWrappers[Sig];
468
12
469
12
  // Put the pointer to the callee as first argument
470
12
  ArgTys.push_back(PointerType::getUnqual(CalleeFTy));
471
12
  // Add argument types
472
12
  ArgTys.append(CalleeFTy->param_begin(), CalleeFTy->param_end());
473
12
474
12
  FunctionType *FTy = FunctionType::get(CalleeFTy->getReturnType(), ArgTys,
475
12
                                        CalleeFTy->isVarArg());
476
12
  Function *F = Function::Create(FTy, GlobalValue::ExternalLinkage,
477
12
                                 InvokePrefix + Sig, M);
478
12
  InvokeWrappers[Sig] = F;
479
12
  return F;
480
12
}
WebAssemblyLowerEmscriptenEHSjLj.cpp:llvm::Function* (anonymous namespace)::WebAssemblyLowerEmscriptenEHSjLj::getInvokeWrapper<llvm::InvokeInst>(llvm::InvokeInst*)
Line
Count
Source
453
20
Function *WebAssemblyLowerEmscriptenEHSjLj::getInvokeWrapper(CallOrInvoke *CI) {
454
20
  Module *M = CI->getModule();
455
20
  SmallVector<Type *, 16> ArgTys;
456
20
  Value *Callee = CI->getCalledValue();
457
20
  FunctionType *CalleeFTy;
458
20
  if (auto *F = dyn_cast<Function>(Callee))
459
9
    CalleeFTy = F->getFunctionType();
460
11
  else {
461
11
    auto *CalleeTy = cast<PointerType>(Callee->getType())->getElementType();
462
11
    CalleeFTy = dyn_cast<FunctionType>(CalleeTy);
463
11
  }
464
20
465
20
  std::string Sig = getSignature(CalleeFTy);
466
20
  if (InvokeWrappers.find(Sig) != InvokeWrappers.end())
467
10
    return InvokeWrappers[Sig];
468
10
469
10
  // Put the pointer to the callee as first argument
470
10
  ArgTys.push_back(PointerType::getUnqual(CalleeFTy));
471
10
  // Add argument types
472
10
  ArgTys.append(CalleeFTy->param_begin(), CalleeFTy->param_end());
473
10
474
10
  FunctionType *FTy = FunctionType::get(CalleeFTy->getReturnType(), ArgTys,
475
10
                                        CalleeFTy->isVarArg());
476
10
  Function *F = Function::Create(FTy, GlobalValue::ExternalLinkage,
477
10
                                 InvokePrefix + Sig, M);
478
10
  InvokeWrappers[Sig] = F;
479
10
  return F;
480
10
}
WebAssemblyLowerEmscriptenEHSjLj.cpp:llvm::Function* (anonymous namespace)::WebAssemblyLowerEmscriptenEHSjLj::getInvokeWrapper<llvm::CallInst>(llvm::CallInst*)
Line
Count
Source
453
4
Function *WebAssemblyLowerEmscriptenEHSjLj::getInvokeWrapper(CallOrInvoke *CI) {
454
4
  Module *M = CI->getModule();
455
4
  SmallVector<Type *, 16> ArgTys;
456
4
  Value *Callee = CI->getCalledValue();
457
4
  FunctionType *CalleeFTy;
458
4
  if (auto *F = dyn_cast<Function>(Callee))
459
4
    CalleeFTy = F->getFunctionType();
460
0
  else {
461
0
    auto *CalleeTy = cast<PointerType>(Callee->getType())->getElementType();
462
0
    CalleeFTy = dyn_cast<FunctionType>(CalleeTy);
463
0
  }
464
4
465
4
  std::string Sig = getSignature(CalleeFTy);
466
4
  if (InvokeWrappers.find(Sig) != InvokeWrappers.end())
467
2
    return InvokeWrappers[Sig];
468
2
469
2
  // Put the pointer to the callee as first argument
470
2
  ArgTys.push_back(PointerType::getUnqual(CalleeFTy));
471
2
  // Add argument types
472
2
  ArgTys.append(CalleeFTy->param_begin(), CalleeFTy->param_end());
473
2
474
2
  FunctionType *FTy = FunctionType::get(CalleeFTy->getReturnType(), ArgTys,
475
2
                                        CalleeFTy->isVarArg());
476
2
  Function *F = Function::Create(FTy, GlobalValue::ExternalLinkage,
477
2
                                 InvokePrefix + Sig, M);
478
2
  InvokeWrappers[Sig] = F;
479
2
  return F;
480
2
}
481
482
bool WebAssemblyLowerEmscriptenEHSjLj::canLongjmp(Module &M,
483
34
                                                  const Value *Callee) const {
484
34
  if (auto *CalleeF = dyn_cast<Function>(Callee))
485
33
    if (CalleeF->isIntrinsic())
486
0
      return false;
487
34
488
34
  // Attempting to transform inline assembly will result in something like:
489
34
  //     call void @__invoke_void(void ()* asm ...)
490
34
  // which is invalid because inline assembly blocks do not have addresses
491
34
  // and can't be passed by pointer. The result is a crash with illegal IR.
492
34
  if (isa<InlineAsm>(Callee))
493
1
    return false;
494
33
495
33
  // The reason we include malloc/free here is to exclude the malloc/free
496
33
  // calls generated in setjmp prep / cleanup routines.
497
33
  Function *SetjmpF = M.getFunction("setjmp");
498
33
  Function *MallocF = M.getFunction("malloc");
499
33
  Function *FreeF = M.getFunction("free");
500
33
  if (Callee == SetjmpF || 
Callee == MallocF27
||
Callee == FreeF21
)
501
12
    return false;
502
21
503
21
  // There are functions in JS glue code
504
21
  if (Callee == ResumeF || Callee == EHTypeIDF || Callee == SaveSetjmpF ||
505
21
      
Callee == TestSetjmpF15
)
506
6
    return false;
507
15
508
15
  // __cxa_find_matching_catch_N functions cannot longjmp
509
15
  if (Callee->getName().startswith(FindMatchingCatchPrefix))
510
1
    return false;
511
14
512
14
  // Exception-catching related functions
513
14
  Function *BeginCatchF = M.getFunction("__cxa_begin_catch");
514
14
  Function *EndCatchF = M.getFunction("__cxa_end_catch");
515
14
  Function *AllocExceptionF = M.getFunction("__cxa_allocate_exception");
516
14
  Function *ThrowF = M.getFunction("__cxa_throw");
517
14
  Function *TerminateF = M.getFunction("__clang_call_terminate");
518
14
  if (Callee == BeginCatchF || 
Callee == EndCatchF13
||
519
14
      
Callee == AllocExceptionF12
||
Callee == ThrowF12
||
Callee == TerminateF12
||
520
14
      
Callee == GetTempRet0Func12
||
Callee == SetTempRet0Func5
)
521
9
    return false;
522
5
523
5
  // Otherwise we don't know
524
5
  return true;
525
5
}
526
527
// Generate testSetjmp function call seqence with preamble and postamble.
528
// The code this generates is equivalent to the following JavaScript code:
529
// if (%__THREW__.val != 0 & threwValue != 0) {
530
//   %label = _testSetjmp(mem[%__THREW__.val], setjmpTable, setjmpTableSize);
531
//   if (%label == 0)
532
//     emscripten_longjmp(%__THREW__.val, threwValue);
533
//   setTempRet0(threwValue);
534
// } else {
535
//   %label = -1;
536
// }
537
// %longjmp_result = getTempRet0();
538
//
539
// As output parameters. returns %label, %longjmp_result, and the BB the last
540
// instruction (%longjmp_result = ...) is in.
541
void WebAssemblyLowerEmscriptenEHSjLj::wrapTestSetjmp(
542
    BasicBlock *BB, Instruction *InsertPt, Value *Threw, Value *SetjmpTable,
543
    Value *SetjmpTableSize, Value *&Label, Value *&LongjmpResult,
544
5
    BasicBlock *&EndBB) {
545
5
  Function *F = BB->getParent();
546
5
  LLVMContext &C = BB->getModule()->getContext();
547
5
  IRBuilder<> IRB(C);
548
5
  IRB.SetInsertPoint(InsertPt);
549
5
550
5
  // if (%__THREW__.val != 0 & threwValue != 0)
551
5
  IRB.SetInsertPoint(BB);
552
5
  BasicBlock *ThenBB1 = BasicBlock::Create(C, "if.then1", F);
553
5
  BasicBlock *ElseBB1 = BasicBlock::Create(C, "if.else1", F);
554
5
  BasicBlock *EndBB1 = BasicBlock::Create(C, "if.end", F);
555
5
  Value *ThrewCmp = IRB.CreateICmpNE(Threw, IRB.getInt32(0));
556
5
  Value *ThrewValue = IRB.CreateLoad(IRB.getInt32Ty(), ThrewValueGV,
557
5
                                     ThrewValueGV->getName() + ".val");
558
5
  Value *ThrewValueCmp = IRB.CreateICmpNE(ThrewValue, IRB.getInt32(0));
559
5
  Value *Cmp1 = IRB.CreateAnd(ThrewCmp, ThrewValueCmp, "cmp1");
560
5
  IRB.CreateCondBr(Cmp1, ThenBB1, ElseBB1);
561
5
562
5
  // %label = _testSetjmp(mem[%__THREW__.val], _setjmpTable, _setjmpTableSize);
563
5
  // if (%label == 0)
564
5
  IRB.SetInsertPoint(ThenBB1);
565
5
  BasicBlock *ThenBB2 = BasicBlock::Create(C, "if.then2", F);
566
5
  BasicBlock *EndBB2 = BasicBlock::Create(C, "if.end2", F);
567
5
  Value *ThrewInt = IRB.CreateIntToPtr(Threw, Type::getInt32PtrTy(C),
568
5
                                       Threw->getName() + ".i32p");
569
5
  Value *LoadedThrew = IRB.CreateLoad(IRB.getInt32Ty(), ThrewInt,
570
5
                                      ThrewInt->getName() + ".loaded");
571
5
  Value *ThenLabel = IRB.CreateCall(
572
5
      TestSetjmpF, {LoadedThrew, SetjmpTable, SetjmpTableSize}, "label");
573
5
  Value *Cmp2 = IRB.CreateICmpEQ(ThenLabel, IRB.getInt32(0));
574
5
  IRB.CreateCondBr(Cmp2, ThenBB2, EndBB2);
575
5
576
5
  // emscripten_longjmp(%__THREW__.val, threwValue);
577
5
  IRB.SetInsertPoint(ThenBB2);
578
5
  IRB.CreateCall(EmLongjmpF, {Threw, ThrewValue});
579
5
  IRB.CreateUnreachable();
580
5
581
5
  // setTempRet0(threwValue);
582
5
  IRB.SetInsertPoint(EndBB2);
583
5
  IRB.CreateCall(SetTempRet0Func, ThrewValue);
584
5
  IRB.CreateBr(EndBB1);
585
5
586
5
  IRB.SetInsertPoint(ElseBB1);
587
5
  IRB.CreateBr(EndBB1);
588
5
589
5
  // longjmp_result = getTempRet0();
590
5
  IRB.SetInsertPoint(EndBB1);
591
5
  PHINode *LabelPHI = IRB.CreatePHI(IRB.getInt32Ty(), 2, "label");
592
5
  LabelPHI->addIncoming(ThenLabel, EndBB2);
593
5
594
5
  LabelPHI->addIncoming(IRB.getInt32(-1), ElseBB1);
595
5
596
5
  // Output parameter assignment
597
5
  Label = LabelPHI;
598
5
  EndBB = EndBB1;
599
5
  LongjmpResult = IRB.CreateCall(GetTempRet0Func, None, "longjmp_result");
600
5
}
601
602
6
void WebAssemblyLowerEmscriptenEHSjLj::rebuildSSA(Function &F) {
603
6
  DominatorTree &DT = getAnalysis<DominatorTreeWrapperPass>(F).getDomTree();
604
6
  DT.recalculate(F); // CFG has been changed
605
6
  SSAUpdater SSA;
606
46
  for (BasicBlock &BB : F) {
607
208
    for (Instruction &I : BB) {
608
367
      for (auto UI = I.use_begin(), UE = I.use_end(); UI != UE;) {
609
159
        Use &U = *UI;
610
159
        ++UI;
611
159
        SSA.Initialize(I.getType(), I.getName());
612
159
        SSA.AddAvailableValue(&BB, &I);
613
159
        auto *User = cast<Instruction>(U.getUser());
614
159
        if (User->getParent() == &BB)
615
98
          continue;
616
61
617
61
        if (auto *UserPN = dyn_cast<PHINode>(User))
618
20
          if (UserPN->getIncomingBlock(U) == &BB)
619
12
            continue;
620
49
621
49
        if (DT.dominates(&I, User))
622
40
          continue;
623
9
        SSA.RewriteUseAfterInsertions(U);
624
9
      }
625
208
    }
626
46
  }
627
6
}
628
629
8
bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) {
630
8
  LLVM_DEBUG(dbgs() << "********** Lower Emscripten EH & SjLj **********\n");
631
8
632
8
  LLVMContext &C = M.getContext();
633
8
  IRBuilder<> IRB(C);
634
8
635
8
  Function *SetjmpF = M.getFunction("setjmp");
636
8
  Function *LongjmpF = M.getFunction("longjmp");
637
8
  bool SetjmpUsed = SetjmpF && 
!SetjmpF->use_empty()3
;
638
8
  bool LongjmpUsed = LongjmpF && 
!LongjmpF->use_empty()4
;
639
8
  bool DoSjLj = EnableSjLj && 
(5
SetjmpUsed5
||
LongjmpUsed3
);
640
8
641
8
  // Declare (or get) global variables __THREW__, __threwValue, and
642
8
  // getTempRet0/setTempRet0 function which are used in common for both
643
8
  // exception handling and setjmp/longjmp handling
644
8
  ThrewGV = getGlobalVariableI32(M, IRB, "__THREW__");
645
8
  ThrewValueGV = getGlobalVariableI32(M, IRB, "__threwValue");
646
8
  GetTempRet0Func =
647
8
      Function::Create(FunctionType::get(IRB.getInt32Ty(), false),
648
8
                       GlobalValue::ExternalLinkage, "getTempRet0", &M);
649
8
  SetTempRet0Func = Function::Create(
650
8
      FunctionType::get(IRB.getVoidTy(), IRB.getInt32Ty(), false),
651
8
      GlobalValue::ExternalLinkage, "setTempRet0", &M);
652
8
  GetTempRet0Func->setDoesNotThrow();
653
8
  SetTempRet0Func->setDoesNotThrow();
654
8
655
8
  bool Changed = false;
656
8
657
8
  // Exception handling
658
8
  if (EnableEH) {
659
7
    // Register __resumeException function
660
7
    FunctionType *ResumeFTy =
661
7
        FunctionType::get(IRB.getVoidTy(), IRB.getInt8PtrTy(), false);
662
7
    ResumeF = Function::Create(ResumeFTy, GlobalValue::ExternalLinkage,
663
7
                               ResumeFName, &M);
664
7
665
7
    // Register llvm_eh_typeid_for function
666
7
    FunctionType *EHTypeIDTy =
667
7
        FunctionType::get(IRB.getInt32Ty(), IRB.getInt8PtrTy(), false);
668
7
    EHTypeIDF = Function::Create(EHTypeIDTy, GlobalValue::ExternalLinkage,
669
7
                                 EHTypeIDFName, &M);
670
7
671
117
    for (Function &F : M) {
672
117
      if (F.isDeclaration())
673
84
        continue;
674
33
      Changed |= runEHOnFunction(F);
675
33
    }
676
7
  }
677
8
678
8
  // Setjmp/longjmp handling
679
8
  if (DoSjLj) {
680
3
    Changed = true; // We have setjmp or longjmp somewhere
681
3
682
3
    if (LongjmpF) {
683
3
      // Replace all uses of longjmp with emscripten_longjmp_jmpbuf, which is
684
3
      // defined in JS code
685
3
      EmLongjmpJmpbufF = Function::Create(LongjmpF->getFunctionType(),
686
3
                                          GlobalValue::ExternalLinkage,
687
3
                                          EmLongjmpJmpbufFName, &M);
688
3
689
3
      LongjmpF->replaceAllUsesWith(EmLongjmpJmpbufF);
690
3
    }
691
3
692
3
    if (SetjmpF) {
693
2
      // Register saveSetjmp function
694
2
      FunctionType *SetjmpFTy = SetjmpF->getFunctionType();
695
2
      SmallVector<Type *, 4> Params = {SetjmpFTy->getParamType(0),
696
2
                                       IRB.getInt32Ty(), Type::getInt32PtrTy(C),
697
2
                                       IRB.getInt32Ty()};
698
2
      FunctionType *FTy =
699
2
          FunctionType::get(Type::getInt32PtrTy(C), Params, false);
700
2
      SaveSetjmpF = Function::Create(FTy, GlobalValue::ExternalLinkage,
701
2
                                     SaveSetjmpFName, &M);
702
2
703
2
      // Register testSetjmp function
704
2
      Params = {IRB.getInt32Ty(), Type::getInt32PtrTy(C), IRB.getInt32Ty()};
705
2
      FTy = FunctionType::get(IRB.getInt32Ty(), Params, false);
706
2
      TestSetjmpF = Function::Create(FTy, GlobalValue::ExternalLinkage,
707
2
                                     TestSetjmpFName, &M);
708
2
709
2
      FTy = FunctionType::get(IRB.getVoidTy(),
710
2
                              {IRB.getInt32Ty(), IRB.getInt32Ty()}, false);
711
2
      EmLongjmpF = Function::Create(FTy, GlobalValue::ExternalLinkage,
712
2
                                    EmLongjmpFName, &M);
713
2
714
2
      // Only traverse functions that uses setjmp in order not to insert
715
2
      // unnecessary prep / cleanup code in every function
716
2
      SmallPtrSet<Function *, 8> SetjmpUsers;
717
6
      for (User *U : SetjmpF->users()) {
718
6
        auto *UI = cast<Instruction>(U);
719
6
        SetjmpUsers.insert(UI->getFunction());
720
6
      }
721
2
      for (Function *F : SetjmpUsers)
722
6
        runSjLjOnFunction(*F);
723
2
    }
724
3
  }
725
8
726
8
  if (!Changed) {
727
0
    // Delete unused global variables and functions
728
0
    if (ResumeF)
729
0
      ResumeF->eraseFromParent();
730
0
    if (EHTypeIDF)
731
0
      EHTypeIDF->eraseFromParent();
732
0
    if (EmLongjmpF)
733
0
      EmLongjmpF->eraseFromParent();
734
0
    if (SaveSetjmpF)
735
0
      SaveSetjmpF->eraseFromParent();
736
0
    if (TestSetjmpF)
737
0
      TestSetjmpF->eraseFromParent();
738
0
    return false;
739
0
  }
740
8
741
8
  return true;
742
8
}
743
744
33
bool WebAssemblyLowerEmscriptenEHSjLj::runEHOnFunction(Function &F) {
745
33
  Module &M = *F.getParent();
746
33
  LLVMContext &C = F.getContext();
747
33
  IRBuilder<> IRB(C);
748
33
  bool Changed = false;
749
33
  SmallVector<Instruction *, 64> ToErase;
750
33
  SmallPtrSet<LandingPadInst *, 32> LandingPads;
751
33
  bool AllowExceptions =
752
33
      areAllExceptionsAllowed() || 
EHWhitelistSet.count(F.getName())2
;
753
33
754
90
  for (BasicBlock &BB : F) {
755
90
    auto *II = dyn_cast<InvokeInst>(BB.getTerminator());
756
90
    if (!II)
757
69
      continue;
758
21
    Changed = true;
759
21
    LandingPads.insert(II->getLandingPadInst());
760
21
    IRB.SetInsertPoint(II);
761
21
762
21
    bool NeedInvoke = AllowExceptions && 
canThrow(II->getCalledValue())20
;
763
21
    if (NeedInvoke) {
764
20
      // Wrap invoke with invoke wrapper and generate preamble/postamble
765
20
      Value *Threw = wrapInvoke(II);
766
20
      ToErase.push_back(II);
767
20
768
20
      // Insert a branch based on __THREW__ variable
769
20
      Value *Cmp = IRB.CreateICmpEQ(Threw, IRB.getInt32(1), "cmp");
770
20
      IRB.CreateCondBr(Cmp, II->getUnwindDest(), II->getNormalDest());
771
20
772
20
    } else {
773
1
      // This can't throw, and we don't need this invoke, just replace it with a
774
1
      // call+branch
775
1
      SmallVector<Value *, 16> Args(II->arg_begin(), II->arg_end());
776
1
      CallInst *NewCall =
777
1
          IRB.CreateCall(II->getFunctionType(), II->getCalledValue(), Args);
778
1
      NewCall->takeName(II);
779
1
      NewCall->setCallingConv(II->getCallingConv());
780
1
      NewCall->setDebugLoc(II->getDebugLoc());
781
1
      NewCall->setAttributes(II->getAttributes());
782
1
      II->replaceAllUsesWith(NewCall);
783
1
      ToErase.push_back(II);
784
1
785
1
      IRB.CreateBr(II->getNormalDest());
786
1
787
1
      // Remove any PHI node entries from the exception destination
788
1
      II->getUnwindDest()->removePredecessor(&BB);
789
1
    }
790
21
  }
791
33
792
33
  // Process resume instructions
793
90
  for (BasicBlock &BB : F) {
794
90
    // Scan the body of the basic block for resumes
795
341
    for (Instruction &I : BB) {
796
341
      auto *RI = dyn_cast<ResumeInst>(&I);
797
341
      if (!RI)
798
339
        continue;
799
2
800
2
      // Split the input into legal values
801
2
      Value *Input = RI->getValue();
802
2
      IRB.SetInsertPoint(RI);
803
2
      Value *Low = IRB.CreateExtractValue(Input, 0, "low");
804
2
      // Create a call to __resumeException function
805
2
      IRB.CreateCall(ResumeF, {Low});
806
2
      // Add a terminator to the block
807
2
      IRB.CreateUnreachable();
808
2
      ToErase.push_back(RI);
809
2
    }
810
90
  }
811
33
812
33
  // Process llvm.eh.typeid.for intrinsics
813
90
  for (BasicBlock &BB : F) {
814
346
    for (Instruction &I : BB) {
815
346
      auto *CI = dyn_cast<CallInst>(&I);
816
346
      if (!CI)
817
257
        continue;
818
89
      const Function *Callee = CI->getCalledFunction();
819
89
      if (!Callee)
820
6
        continue;
821
83
      if (Callee->getIntrinsicID() != Intrinsic::eh_typeid_for)
822
81
        continue;
823
2
824
2
      IRB.SetInsertPoint(CI);
825
2
      CallInst *NewCI =
826
2
          IRB.CreateCall(EHTypeIDF, CI->getArgOperand(0), "typeid");
827
2
      CI->replaceAllUsesWith(NewCI);
828
2
      ToErase.push_back(CI);
829
2
    }
830
90
  }
831
33
832
33
  // Look for orphan landingpads, can occur in blocks with no predecessors
833
90
  for (BasicBlock &BB : F) {
834
90
    Instruction *I = BB.getFirstNonPHI();
835
90
    if (auto *LPI = dyn_cast<LandingPadInst>(I))
836
13
      LandingPads.insert(LPI);
837
90
  }
838
33
839
33
  // Handle all the landingpad for this function together, as multiple invokes
840
33
  // may share a single lp
841
33
  for (LandingPadInst *LPI : LandingPads) {
842
13
    IRB.SetInsertPoint(LPI);
843
13
    SmallVector<Value *, 16> FMCArgs;
844
23
    for (unsigned I = 0, E = LPI->getNumClauses(); I < E; 
++I10
) {
845
10
      Constant *Clause = LPI->getClause(I);
846
10
      // As a temporary workaround for the lack of aggregate varargs support
847
10
      // in the interface between JS and wasm, break out filter operands into
848
10
      // their component elements.
849
10
      if (LPI->isFilter(I)) {
850
1
        auto *ATy = cast<ArrayType>(Clause->getType());
851
3
        for (unsigned J = 0, E = ATy->getNumElements(); J < E; 
++J2
) {
852
2
          Value *EV = IRB.CreateExtractValue(Clause, makeArrayRef(J), "filter");
853
2
          FMCArgs.push_back(EV);
854
2
        }
855
1
      } else
856
9
        FMCArgs.push_back(Clause);
857
10
    }
858
13
859
13
    // Create a call to __cxa_find_matching_catch_N function
860
13
    Function *FMCF = getFindMatchingCatch(M, FMCArgs.size());
861
13
    CallInst *FMCI = IRB.CreateCall(FMCF, FMCArgs, "fmc");
862
13
    Value *Undef = UndefValue::get(LPI->getType());
863
13
    Value *Pair0 = IRB.CreateInsertValue(Undef, FMCI, 0, "pair0");
864
13
    Value *TempRet0 = IRB.CreateCall(GetTempRet0Func, None, "tempret0");
865
13
    Value *Pair1 = IRB.CreateInsertValue(Pair0, TempRet0, 1, "pair1");
866
13
867
13
    LPI->replaceAllUsesWith(Pair1);
868
13
    ToErase.push_back(LPI);
869
13
  }
870
33
871
33
  // Erase everything we no longer need in this function
872
33
  for (Instruction *I : ToErase)
873
38
    I->eraseFromParent();
874
33
875
33
  return Changed;
876
33
}
877
878
6
bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) {
879
6
  Module &M = *F.getParent();
880
6
  LLVMContext &C = F.getContext();
881
6
  IRBuilder<> IRB(C);
882
6
  SmallVector<Instruction *, 64> ToErase;
883
6
  // Vector of %setjmpTable values
884
6
  std::vector<Instruction *> SetjmpTableInsts;
885
6
  // Vector of %setjmpTableSize values
886
6
  std::vector<Instruction *> SetjmpTableSizeInsts;
887
6
888
6
  // Setjmp preparation
889
6
890
6
  // This instruction effectively means %setjmpTableSize = 4.
891
6
  // We create this as an instruction intentionally, and we don't want to fold
892
6
  // this instruction to a constant 4, because this value will be used in
893
6
  // SSAUpdater.AddAvailableValue(...) later.
894
6
  BasicBlock &EntryBB = F.getEntryBlock();
895
6
  BinaryOperator *SetjmpTableSize = BinaryOperator::Create(
896
6
      Instruction::Add, IRB.getInt32(4), IRB.getInt32(0), "setjmpTableSize",
897
6
      &*EntryBB.getFirstInsertionPt());
898
6
  // setjmpTable = (int *) malloc(40);
899
6
  Instruction *SetjmpTable = CallInst::CreateMalloc(
900
6
      SetjmpTableSize, IRB.getInt32Ty(), IRB.getInt32Ty(), IRB.getInt32(40),
901
6
      nullptr, nullptr, "setjmpTable");
902
6
  // setjmpTable[0] = 0;
903
6
  IRB.SetInsertPoint(SetjmpTableSize);
904
6
  IRB.CreateStore(IRB.getInt32(0), SetjmpTable);
905
6
  SetjmpTableInsts.push_back(SetjmpTable);
906
6
  SetjmpTableSizeInsts.push_back(SetjmpTableSize);
907
6
908
6
  // Setjmp transformation
909
6
  std::vector<PHINode *> SetjmpRetPHIs;
910
6
  Function *SetjmpF = M.getFunction("setjmp");
911
16
  for (User *U : SetjmpF->users()) {
912
16
    auto *CI = dyn_cast<CallInst>(U);
913
16
    if (!CI)
914
0
      report_fatal_error("Does not support indirect calls to setjmp");
915
16
916
16
    BasicBlock *BB = CI->getParent();
917
16
    if (BB->getParent() != &F) // in other function
918
10
      continue;
919
6
920
6
    // The tail is everything right after the call, and will be reached once
921
6
    // when setjmp is called, and later when longjmp returns to the setjmp
922
6
    BasicBlock *Tail = SplitBlock(BB, CI->getNextNode());
923
6
    // Add a phi to the tail, which will be the output of setjmp, which
924
6
    // indicates if this is the first call or a longjmp back. The phi directly
925
6
    // uses the right value based on where we arrive from
926
6
    IRB.SetInsertPoint(Tail->getFirstNonPHI());
927
6
    PHINode *SetjmpRet = IRB.CreatePHI(IRB.getInt32Ty(), 2, "setjmp.ret");
928
6
929
6
    // setjmp initial call returns 0
930
6
    SetjmpRet->addIncoming(IRB.getInt32(0), BB);
931
6
    // The proper output is now this, not the setjmp call itself
932
6
    CI->replaceAllUsesWith(SetjmpRet);
933
6
    // longjmp returns to the setjmp will add themselves to this phi
934
6
    SetjmpRetPHIs.push_back(SetjmpRet);
935
6
936
6
    // Fix call target
937
6
    // Our index in the function is our place in the array + 1 to avoid index
938
6
    // 0, because index 0 means the longjmp is not ours to handle.
939
6
    IRB.SetInsertPoint(CI);
940
6
    Value *Args[] = {CI->getArgOperand(0), IRB.getInt32(SetjmpRetPHIs.size()),
941
6
                     SetjmpTable, SetjmpTableSize};
942
6
    Instruction *NewSetjmpTable =
943
6
        IRB.CreateCall(SaveSetjmpF, Args, "setjmpTable");
944
6
    Instruction *NewSetjmpTableSize =
945
6
        IRB.CreateCall(GetTempRet0Func, None, "setjmpTableSize");
946
6
    SetjmpTableInsts.push_back(NewSetjmpTable);
947
6
    SetjmpTableSizeInsts.push_back(NewSetjmpTableSize);
948
6
    ToErase.push_back(CI);
949
6
  }
950
6
951
6
  // Update each call that can longjmp so it can return to a setjmp where
952
6
  // relevant.
953
6
954
6
  // Because we are creating new BBs while processing and don't want to make
955
6
  // all these newly created BBs candidates again for longjmp processing, we
956
6
  // first make the vector of candidate BBs.
957
6
  std::vector<BasicBlock *> BBs;
958
6
  for (BasicBlock &BB : F)
959
16
    BBs.push_back(&BB);
960
6
961
6
  // BBs.size() will change within the loop, so we query it every time
962
27
  for (unsigned I = 0; I < BBs.size(); 
I++21
) {
963
21
    BasicBlock *BB = BBs[I];
964
130
    for (Instruction &I : *BB) {
965
130
      assert(!isa<InvokeInst>(&I));
966
130
      auto *CI = dyn_cast<CallInst>(&I);
967
130
      if (!CI)
968
96
        continue;
969
34
970
34
      const Value *Callee = CI->getCalledValue();
971
34
      if (!canLongjmp(M, Callee))
972
29
        continue;
973
5
974
5
      Value *Threw = nullptr;
975
5
      BasicBlock *Tail;
976
5
      if (Callee->getName().startswith(InvokePrefix)) {
977
1
        // If invoke wrapper has already been generated for this call in
978
1
        // previous EH phase, search for the load instruction
979
1
        // %__THREW__.val = __THREW__;
980
1
        // in postamble after the invoke wrapper call
981
1
        LoadInst *ThrewLI = nullptr;
982
1
        StoreInst *ThrewResetSI = nullptr;
983
1
        for (auto I = std::next(BasicBlock::iterator(CI)), IE = BB->end();
984
1
             I != IE; 
++I0
) {
985
1
          if (auto *LI = dyn_cast<LoadInst>(I))
986
1
            if (auto *GV = dyn_cast<GlobalVariable>(LI->getPointerOperand()))
987
1
              if (GV == ThrewGV) {
988
1
                Threw = ThrewLI = LI;
989
1
                break;
990
1
              }
991
1
        }
992
1
        // Search for the store instruction after the load above
993
1
        // __THREW__ = 0;
994
1
        for (auto I = std::next(BasicBlock::iterator(ThrewLI)), IE = BB->end();
995
1
             I != IE; 
++I0
) {
996
1
          if (auto *SI = dyn_cast<StoreInst>(I))
997
1
            if (auto *GV = dyn_cast<GlobalVariable>(SI->getPointerOperand()))
998
1
              if (GV == ThrewGV && SI->getValueOperand() == IRB.getInt32(0)) {
999
1
                ThrewResetSI = SI;
1000
1
                break;
1001
1
              }
1002
1
        }
1003
1
        assert(Threw && ThrewLI && "Cannot find __THREW__ load after invoke");
1004
1
        assert(ThrewResetSI && "Cannot find __THREW__ store after invoke");
1005
1
        Tail = SplitBlock(BB, ThrewResetSI->getNextNode());
1006
1
1007
4
      } else {
1008
4
        // Wrap call with invoke wrapper and generate preamble/postamble
1009
4
        Threw = wrapInvoke(CI);
1010
4
        ToErase.push_back(CI);
1011
4
        Tail = SplitBlock(BB, CI->getNextNode());
1012
4
      }
1013
5
1014
5
      // We need to replace the terminator in Tail - SplitBlock makes BB go
1015
5
      // straight to Tail, we need to check if a longjmp occurred, and go to the
1016
5
      // right setjmp-tail if so
1017
5
      ToErase.push_back(BB->getTerminator());
1018
5
1019
5
      // Generate a function call to testSetjmp function and preamble/postamble
1020
5
      // code to figure out (1) whether longjmp occurred (2) if longjmp
1021
5
      // occurred, which setjmp it corresponds to
1022
5
      Value *Label = nullptr;
1023
5
      Value *LongjmpResult = nullptr;
1024
5
      BasicBlock *EndBB = nullptr;
1025
5
      wrapTestSetjmp(BB, CI, Threw, SetjmpTable, SetjmpTableSize, Label,
1026
5
                     LongjmpResult, EndBB);
1027
5
      assert(Label && LongjmpResult && EndBB);
1028
5
1029
5
      // Create switch instruction
1030
5
      IRB.SetInsertPoint(EndBB);
1031
5
      SwitchInst *SI = IRB.CreateSwitch(Label, Tail, SetjmpRetPHIs.size());
1032
5
      // -1 means no longjmp happened, continue normally (will hit the default
1033
5
      // switch case). 0 means a longjmp that is not ours to handle, needs a
1034
5
      // rethrow. Otherwise the index is the same as the index in P+1 (to avoid
1035
5
      // 0).
1036
10
      for (unsigned I = 0; I < SetjmpRetPHIs.size(); 
I++5
) {
1037
5
        SI->addCase(IRB.getInt32(I + 1), SetjmpRetPHIs[I]->getParent());
1038
5
        SetjmpRetPHIs[I]->addIncoming(LongjmpResult, EndBB);
1039
5
      }
1040
5
1041
5
      // We are splitting the block here, and must continue to find other calls
1042
5
      // in the block - which is now split. so continue to traverse in the Tail
1043
5
      BBs.push_back(Tail);
1044
5
    }
1045
21
  }
1046
6
1047
6
  // Erase everything we no longer need in this function
1048
6
  for (Instruction *I : ToErase)
1049
15
    I->eraseFromParent();
1050
6
1051
6
  // Free setjmpTable buffer before each return instruction
1052
46
  for (BasicBlock &BB : F) {
1053
46
    Instruction *TI = BB.getTerminator();
1054
46
    if (isa<ReturnInst>(TI))
1055
3
      CallInst::CreateFree(SetjmpTable, TI);
1056
46
  }
1057
6
1058
6
  // Every call to saveSetjmp can change setjmpTable and setjmpTableSize
1059
6
  // (when buffer reallocation occurs)
1060
6
  // entry:
1061
6
  //   setjmpTableSize = 4;
1062
6
  //   setjmpTable = (int *) malloc(40);
1063
6
  //   setjmpTable[0] = 0;
1064
6
  // ...
1065
6
  // somebb:
1066
6
  //   setjmpTable = saveSetjmp(buf, label, setjmpTable, setjmpTableSize);
1067
6
  //   setjmpTableSize = getTempRet0();
1068
6
  // So we need to make sure the SSA for these variables is valid so that every
1069
6
  // saveSetjmp and testSetjmp calls have the correct arguments.
1070
6
  SSAUpdater SetjmpTableSSA;
1071
6
  SSAUpdater SetjmpTableSizeSSA;
1072
6
  SetjmpTableSSA.Initialize(Type::getInt32PtrTy(C), "setjmpTable");
1073
6
  SetjmpTableSizeSSA.Initialize(Type::getInt32Ty(C), "setjmpTableSize");
1074
6
  for (Instruction *I : SetjmpTableInsts)
1075
12
    SetjmpTableSSA.AddAvailableValue(I->getParent(), I);
1076
6
  for (Instruction *I : SetjmpTableSizeInsts)
1077
12
    SetjmpTableSizeSSA.AddAvailableValue(I->getParent(), I);
1078
6
1079
6
  for (auto UI = SetjmpTable->use_begin(), UE = SetjmpTable->use_end();
1080
26
       UI != UE;) {
1081
20
    // Grab the use before incrementing the iterator.
1082
20
    Use &U = *UI;
1083
20
    // Increment the iterator before removing the use from the list.
1084
20
    ++UI;
1085
20
    if (auto *I = dyn_cast<Instruction>(U.getUser()))
1086
20
      if (I->getParent() != &EntryBB)
1087
9
        SetjmpTableSSA.RewriteUse(U);
1088
20
  }
1089
6
  for (auto UI = SetjmpTableSize->use_begin(), UE = SetjmpTableSize->use_end();
1090
17
       UI != UE;) {
1091
11
    Use &U = *UI;
1092
11
    ++UI;
1093
11
    if (auto *I = dyn_cast<Instruction>(U.getUser()))
1094
11
      if (I->getParent() != &EntryBB)
1095
6
        SetjmpTableSizeSSA.RewriteUse(U);
1096
11
  }
1097
6
1098
6
  // Finally, our modifications to the cfg can break dominance of SSA variables.
1099
6
  // For example, in this code,
1100
6
  // if (x()) { .. setjmp() .. }
1101
6
  // if (y()) { .. longjmp() .. }
1102
6
  // We must split the longjmp block, and it can jump into the block splitted
1103
6
  // from setjmp one. But that means that when we split the setjmp block, it's
1104
6
  // first part no longer dominates its second part - there is a theoretically
1105
6
  // possible control flow path where x() is false, then y() is true and we
1106
6
  // reach the second part of the setjmp block, without ever reaching the first
1107
6
  // part. So, we rebuild SSA form here.
1108
6
  rebuildSSA(F);
1109
6
  return true;
1110
6
}