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