aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp')
-rw-r--r--llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp309
1 files changed, 205 insertions, 104 deletions
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp
index d3bbadf27478..599829a9e474 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp
@@ -13,14 +13,7 @@
///
/// To handle exceptions and setjmp/longjmps, this scheme relies on JavaScript's
/// try and catch syntax and relevant exception-related libraries implemented
-/// in JavaScript glue code that will be produced by Emscripten. This is similar
-/// to the current Emscripten asm.js exception handling in fastcomp. For
-/// fastcomp's EH / SjLj scheme, see these files in fastcomp LLVM branch:
-/// (Location: https://github.com/kripken/emscripten-fastcomp)
-/// lib/Target/JSBackend/NaCl/LowerEmExceptionsPass.cpp
-/// lib/Target/JSBackend/NaCl/LowerEmSetjmp.cpp
-/// lib/Target/JSBackend/JSBackend.cpp
-/// lib/Target/JSBackend/CallHandlers.h
+/// in JavaScript glue code that will be produced by Emscripten.
///
/// * Exception handling
/// This pass lowers invokes and landingpads into library functions in JS glue
@@ -50,25 +43,21 @@
/// In detail, this pass does following things:
///
/// 1) Assumes the existence of global variables: __THREW__, __threwValue
-/// __THREW__ and __threwValue will be set in invoke wrappers
-/// in JS glue code. For what invoke wrappers are, refer to 3). These
-/// variables are used for both exceptions and setjmp/longjmps.
+/// __THREW__ and __threwValue are defined in compiler-rt in Emscripten.
+/// These variables are used for both exceptions and setjmp/longjmps.
/// __THREW__ indicates whether an exception or a longjmp occurred or not. 0
/// means nothing occurred, 1 means an exception occurred, and other numbers
-/// mean a longjmp occurred. In the case of longjmp, __threwValue variable
+/// mean a longjmp occurred. In the case of longjmp, __THREW__ variable
/// indicates the corresponding setjmp buffer the longjmp corresponds to.
+/// __threwValue is 0 for exceptions, and the argument to longjmp in case of
+/// longjmp.
///
/// * Exception handling
///
/// 2) We assume the existence of setThrew and setTempRet0/getTempRet0 functions
-/// at link time.
-/// The global variables in 1) will exist in wasm address space,
-/// but their values should be set in JS code, so these functions
-/// as interfaces to JS glue code. These functions are equivalent to the
-/// following JS functions, which actually exist in asm.js version of JS
-/// library.
+/// at link time. setThrew exists in Emscripten's compiler-rt:
///
-/// function setThrew(threw, value) {
+/// void setThrew(uintptr_t threw, int value) {
/// if (__THREW__ == 0) {
/// __THREW__ = threw;
/// __threwValue = value;
@@ -76,7 +65,6 @@
/// }
//
/// setTempRet0 is called from __cxa_find_matching_catch() in JS glue code.
-///
/// In exception handling, getTempRet0 indicates the type of an exception
/// caught, and in setjmp/longjmp, it means the second argument to longjmp
/// function.
@@ -105,7 +93,7 @@
/// Module["dynCall_vi"](index,a1); // This calls original callee
/// } catch(e) {
/// if (typeof e !== 'number' && e !== 'longjmp') throw e;
-/// asm["setThrew"](1, 0); // setThrew is called here
+/// _setThrew(1, 0); // setThrew is called here
/// }
/// }
/// If an exception is thrown, __THREW__ will be set to true in a wrapper,
@@ -149,8 +137,8 @@
/// setjmpTableSize = 4;
/// setjmpTable = (int *) malloc(40);
/// setjmpTable[0] = 0;
-/// setjmpTable and setjmpTableSize are used in saveSetjmp() function in JS
-/// code.
+/// setjmpTable and setjmpTableSize are used to call saveSetjmp() function in
+/// Emscripten compiler-rt.
///
/// 3) Lower
/// setjmp(buf)
@@ -160,11 +148,11 @@
/// For each dynamic setjmp call, setjmpTable stores its ID (a number which
/// is incrementally assigned from 0) and its label (a unique number that
/// represents each callsite of setjmp). When we need more entries in
-/// setjmpTable, it is reallocated in saveSetjmp() in JS code and it will
-/// return the new table address, and assign the new table size in
-/// setTempRet0(). saveSetjmp also stores the setjmp's ID into the buffer
-/// buf. A BB with setjmp is split into two after setjmp call in order to
-/// make the post-setjmp BB the possible destination of longjmp BB.
+/// setjmpTable, it is reallocated in saveSetjmp() in Emscripten's
+/// compiler-rt and it will return the new table address, and assign the new
+/// table size in setTempRet0(). saveSetjmp also stores the setjmp's ID into
+/// the buffer buf. A BB with setjmp is split into two after setjmp call in
+/// order to make the post-setjmp BB the possible destination of longjmp BB.
///
///
/// 4) Lower every call that might longjmp into
@@ -172,12 +160,13 @@
/// call @__invoke_SIG(func, arg1, arg2)
/// %__THREW__.val = __THREW__;
/// __THREW__ = 0;
-/// if (%__THREW__.val != 0 & __threwValue != 0) {
+/// %__threwValue.val = __threwValue;
+/// if (%__THREW__.val != 0 & %__threwValue.val != 0) {
/// %label = testSetjmp(mem[%__THREW__.val], setjmpTable,
/// setjmpTableSize);
/// if (%label == 0)
-/// emscripten_longjmp(%__THREW__.val, __threwValue);
-/// setTempRet0(__threwValue);
+/// emscripten_longjmp(%__THREW__.val, %__threwValue.val);
+/// setTempRet0(%__threwValue.val);
/// } else {
/// %label = -1;
/// }
@@ -191,7 +180,7 @@
/// testSetjmp examines setjmpTable to see if there is a matching setjmp
/// call. After calling an invoke wrapper, if a longjmp occurred, __THREW__
/// will be the address of matching jmp_buf buffer and __threwValue be the
-/// second argument to longjmp. mem[__THREW__.val] is a setjmp ID that is
+/// second argument to longjmp. mem[%__THREW__.val] is a setjmp ID that is
/// stored in saveSetjmp. testSetjmp returns a setjmp label, a unique ID to
/// each setjmp callsite. Label 0 means this longjmp buffer does not
/// correspond to one of the setjmp callsites in this function, so in this
@@ -227,6 +216,7 @@ namespace {
class WebAssemblyLowerEmscriptenEHSjLj final : public ModulePass {
bool EnableEH; // Enable exception handling
bool EnableSjLj; // Enable setjmp/longjmp handling
+ bool DoSjLj; // Whether we actually perform setjmp/longjmp handling
GlobalVariable *ThrewGV = nullptr;
GlobalVariable *ThrewValueGV = nullptr;
@@ -245,6 +235,8 @@ class WebAssemblyLowerEmscriptenEHSjLj final : public ModulePass {
StringMap<Function *> InvokeWrappers;
// Set of allowed function names for exception handling
std::set<std::string> EHAllowlistSet;
+ // Functions that contains calls to setjmp
+ SmallPtrSet<Function *, 8> SetjmpUsers;
StringRef getPassName() const override {
return "WebAssembly Lower Emscripten Exceptions";
@@ -263,6 +255,10 @@ class WebAssemblyLowerEmscriptenEHSjLj final : public ModulePass {
bool areAllExceptionsAllowed() const { return EHAllowlistSet.empty(); }
bool canLongjmp(Module &M, const Value *Callee) const;
bool isEmAsmCall(Module &M, const Value *Callee) const;
+ bool supportsException(const Function *F) const {
+ return EnableEH && (areAllExceptionsAllowed() ||
+ EHAllowlistSet.count(std::string(F->getName())));
+ }
void rebuildSSA(Function &F);
@@ -298,7 +294,7 @@ static bool canThrow(const Value *V) {
return false;
StringRef Name = F->getName();
// leave setjmp and longjmp (mostly) alone, we process them properly later
- if (Name == "setjmp" || Name == "longjmp")
+ if (Name == "setjmp" || Name == "longjmp" || Name == "emscripten_longjmp")
return false;
return !F->doesNotThrow();
}
@@ -306,13 +302,12 @@ static bool canThrow(const Value *V) {
return true;
}
-// Get a global variable with the given name. If it doesn't exist declare it,
-// which will generate an import and asssumes that it will exist at link time.
-static GlobalVariable *getGlobalVariableI32(Module &M, IRBuilder<> &IRB,
- WebAssemblyTargetMachine &TM,
- const char *Name) {
- auto Int32Ty = IRB.getInt32Ty();
- auto *GV = dyn_cast<GlobalVariable>(M.getOrInsertGlobal(Name, Int32Ty));
+// Get a global variable with the given name. If it doesn't exist declare it,
+// which will generate an import and assume that it will exist at link time.
+static GlobalVariable *getGlobalVariable(Module &M, Type *Ty,
+ WebAssemblyTargetMachine &TM,
+ const char *Name) {
+ auto *GV = dyn_cast<GlobalVariable>(M.getOrInsertGlobal(Name, Ty));
if (!GV)
report_fatal_error(Twine("unable to create global: ") + Name);
@@ -368,6 +363,28 @@ static Function *getEmscriptenFunction(FunctionType *Ty, const Twine &Name,
return F;
}
+// Returns an integer type for the target architecture's address space.
+// i32 for wasm32 and i64 for wasm64.
+static Type *getAddrIntType(Module *M) {
+ IRBuilder<> IRB(M->getContext());
+ return IRB.getIntNTy(M->getDataLayout().getPointerSizeInBits());
+}
+
+// Returns an integer pointer type for the target architecture's address space.
+// i32* for wasm32 and i64* for wasm64.
+static Type *getAddrPtrType(Module *M) {
+ return Type::getIntNPtrTy(M->getContext(),
+ M->getDataLayout().getPointerSizeInBits());
+}
+
+// Returns an integer whose type is the integer type for the target's address
+// space. Returns (i32 C) for wasm32 and (i64 C) for wasm64, when C is the
+// integer.
+static Value *getAddrSizeInt(Module *M, uint64_t C) {
+ IRBuilder<> IRB(M->getContext());
+ return IRB.getIntN(M->getDataLayout().getPointerSizeInBits(), C);
+}
+
// Returns __cxa_find_matching_catch_N function, where N = NumClauses + 2.
// This is because a landingpad instruction contains two more arguments, a
// personality function and a cleanup bit, and __cxa_find_matching_catch_N
@@ -395,7 +412,8 @@ WebAssemblyLowerEmscriptenEHSjLj::getFindMatchingCatch(Module &M,
// Returns %__THREW__.val, which indicates whether an exception is thrown (or
// whether longjmp occurred), for future use.
Value *WebAssemblyLowerEmscriptenEHSjLj::wrapInvoke(CallBase *CI) {
- LLVMContext &C = CI->getModule()->getContext();
+ Module *M = CI->getModule();
+ LLVMContext &C = M->getContext();
// If we are calling a function that is noreturn, we must remove that
// attribute. The code we insert here does expect it to return, after we
@@ -411,7 +429,7 @@ Value *WebAssemblyLowerEmscriptenEHSjLj::wrapInvoke(CallBase *CI) {
// Pre-invoke
// __THREW__ = 0;
- IRB.CreateStore(IRB.getInt32(0), ThrewGV);
+ IRB.CreateStore(getAddrSizeInt(M, 0), ThrewGV);
// Invoke function wrapper in JavaScript
SmallVector<Value *, 16> Args;
@@ -459,8 +477,8 @@ Value *WebAssemblyLowerEmscriptenEHSjLj::wrapInvoke(CallBase *CI) {
// Post-invoke
// %__THREW__.val = __THREW__; __THREW__ = 0;
Value *Threw =
- IRB.CreateLoad(IRB.getInt32Ty(), ThrewGV, ThrewGV->getName() + ".val");
- IRB.CreateStore(IRB.getInt32(0), ThrewGV);
+ IRB.CreateLoad(getAddrIntType(M), ThrewGV, ThrewGV->getName() + ".val");
+ IRB.CreateStore(getAddrSizeInt(M, 0), ThrewGV);
return Threw;
}
@@ -505,7 +523,7 @@ bool WebAssemblyLowerEmscriptenEHSjLj::canLongjmp(Module &M,
if (CalleeName == "setjmp" || CalleeName == "malloc" || CalleeName == "free")
return false;
- // There are functions in JS glue code
+ // There are functions in Emscripten's JS glue code or compiler-rt
if (CalleeName == "__resumeException" || CalleeName == "llvm_eh_typeid_for" ||
CalleeName == "saveSetjmp" || CalleeName == "testSetjmp" ||
CalleeName == "getTempRet0" || CalleeName == "setTempRet0")
@@ -538,11 +556,12 @@ bool WebAssemblyLowerEmscriptenEHSjLj::isEmAsmCall(Module &M,
// Generate testSetjmp function call seqence with preamble and postamble.
// The code this generates is equivalent to the following JavaScript code:
-// if (%__THREW__.val != 0 & threwValue != 0) {
+// %__threwValue.val = __threwValue;
+// if (%__THREW__.val != 0 & %__threwValue.val != 0) {
// %label = _testSetjmp(mem[%__THREW__.val], setjmpTable, setjmpTableSize);
// if (%label == 0)
-// emscripten_longjmp(%__THREW__.val, threwValue);
-// setTempRet0(threwValue);
+// emscripten_longjmp(%__THREW__.val, %__threwValue.val);
+// setTempRet0(%__threwValue.val);
// } else {
// %label = -1;
// }
@@ -555,16 +574,17 @@ void WebAssemblyLowerEmscriptenEHSjLj::wrapTestSetjmp(
Value *SetjmpTableSize, Value *&Label, Value *&LongjmpResult,
BasicBlock *&EndBB) {
Function *F = BB->getParent();
- LLVMContext &C = BB->getModule()->getContext();
+ Module *M = F->getParent();
+ LLVMContext &C = M->getContext();
IRBuilder<> IRB(C);
IRB.SetCurrentDebugLocation(DL);
- // if (%__THREW__.val != 0 & threwValue != 0)
+ // if (%__THREW__.val != 0 & %__threwValue.val != 0)
IRB.SetInsertPoint(BB);
BasicBlock *ThenBB1 = BasicBlock::Create(C, "if.then1", F);
BasicBlock *ElseBB1 = BasicBlock::Create(C, "if.else1", F);
BasicBlock *EndBB1 = BasicBlock::Create(C, "if.end", F);
- Value *ThrewCmp = IRB.CreateICmpNE(Threw, IRB.getInt32(0));
+ Value *ThrewCmp = IRB.CreateICmpNE(Threw, getAddrSizeInt(M, 0));
Value *ThrewValue = IRB.CreateLoad(IRB.getInt32Ty(), ThrewValueGV,
ThrewValueGV->getName() + ".val");
Value *ThrewValueCmp = IRB.CreateICmpNE(ThrewValue, IRB.getInt32(0));
@@ -576,21 +596,21 @@ void WebAssemblyLowerEmscriptenEHSjLj::wrapTestSetjmp(
IRB.SetInsertPoint(ThenBB1);
BasicBlock *ThenBB2 = BasicBlock::Create(C, "if.then2", F);
BasicBlock *EndBB2 = BasicBlock::Create(C, "if.end2", F);
- Value *ThrewInt = IRB.CreateIntToPtr(Threw, Type::getInt32PtrTy(C),
- Threw->getName() + ".i32p");
- Value *LoadedThrew = IRB.CreateLoad(IRB.getInt32Ty(), ThrewInt,
- ThrewInt->getName() + ".loaded");
+ Value *ThrewPtr =
+ IRB.CreateIntToPtr(Threw, getAddrPtrType(M), Threw->getName() + ".p");
+ Value *LoadedThrew = IRB.CreateLoad(getAddrIntType(M), ThrewPtr,
+ ThrewPtr->getName() + ".loaded");
Value *ThenLabel = IRB.CreateCall(
TestSetjmpF, {LoadedThrew, SetjmpTable, SetjmpTableSize}, "label");
Value *Cmp2 = IRB.CreateICmpEQ(ThenLabel, IRB.getInt32(0));
IRB.CreateCondBr(Cmp2, ThenBB2, EndBB2);
- // emscripten_longjmp(%__THREW__.val, threwValue);
+ // emscripten_longjmp(%__THREW__.val, %__threwValue.val);
IRB.SetInsertPoint(ThenBB2);
IRB.CreateCall(EmLongjmpF, {Threw, ThrewValue});
IRB.CreateUnreachable();
- // setTempRet0(threwValue);
+ // setTempRet0(%__threwValue.val);
IRB.SetInsertPoint(EndBB2);
IRB.CreateCall(SetTempRet0Func, ThrewValue);
IRB.CreateBr(EndBB1);
@@ -636,11 +656,12 @@ void WebAssemblyLowerEmscriptenEHSjLj::rebuildSSA(Function &F) {
}
// Replace uses of longjmp with emscripten_longjmp. emscripten_longjmp takes
-// arguments of type {i32, i32} and longjmp takes {jmp_buf*, i32}, so we need a
-// ptrtoint instruction here to make the type match. jmp_buf* will eventually be
-// lowered to i32 in the wasm backend.
+// arguments of type {i32, i32} (wasm32) / {i64, i32} (wasm64) and longjmp takes
+// {jmp_buf*, i32}, so we need a ptrtoint instruction here to make the type
+// match. jmp_buf* will eventually be lowered to i32 in the wasm backend.
static void replaceLongjmpWithEmscriptenLongjmp(Function *LongjmpF,
Function *EmLongjmpF) {
+ Module *M = LongjmpF->getParent();
SmallVector<CallInst *, 8> ToErase;
LLVMContext &C = LongjmpF->getParent()->getContext();
IRBuilder<> IRB(C);
@@ -652,7 +673,7 @@ static void replaceLongjmpWithEmscriptenLongjmp(Function *LongjmpF,
if (CI && CI->getCalledFunction() == LongjmpF) {
IRB.SetInsertPoint(CI);
Value *Jmpbuf =
- IRB.CreatePtrToInt(CI->getArgOperand(0), IRB.getInt32Ty(), "jmpbuf");
+ IRB.CreatePtrToInt(CI->getArgOperand(0), getAddrIntType(M), "jmpbuf");
IRB.CreateCall(EmLongjmpF, {Jmpbuf, CI->getArgOperand(1)});
ToErase.push_back(CI);
}
@@ -679,21 +700,21 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) {
Function *LongjmpF = M.getFunction("longjmp");
bool SetjmpUsed = SetjmpF && !SetjmpF->use_empty();
bool LongjmpUsed = LongjmpF && !LongjmpF->use_empty();
- bool DoSjLj = EnableSjLj && (SetjmpUsed || LongjmpUsed);
-
- if ((EnableEH || DoSjLj) &&
- Triple(M.getTargetTriple()).getArch() == Triple::wasm64)
- report_fatal_error("Emscripten EH/SjLj is not supported with wasm64 yet");
+ DoSjLj = EnableSjLj && (SetjmpUsed || LongjmpUsed);
auto *TPC = getAnalysisIfAvailable<TargetPassConfig>();
assert(TPC && "Expected a TargetPassConfig");
auto &TM = TPC->getTM<WebAssemblyTargetMachine>();
+ if (EnableEH && TM.Options.ExceptionModel == ExceptionHandling::Wasm)
+ report_fatal_error("-exception-model=wasm not allowed with "
+ "-enable-emscripten-cxx-exceptions");
+
// Declare (or get) global variables __THREW__, __threwValue, and
// getTempRet0/setTempRet0 function which are used in common for both
// exception handling and setjmp/longjmp handling
- ThrewGV = getGlobalVariableI32(M, IRB, TM, "__THREW__");
- ThrewValueGV = getGlobalVariableI32(M, IRB, TM, "__threwValue");
+ ThrewGV = getGlobalVariable(M, getAddrIntType(&M), TM, "__THREW__");
+ ThrewValueGV = getGlobalVariable(M, IRB.getInt32Ty(), TM, "__threwValue");
GetTempRet0Func = getEmscriptenFunction(
FunctionType::get(IRB.getInt32Ty(), false), "getTempRet0", &M);
SetTempRet0Func = getEmscriptenFunction(
@@ -704,7 +725,7 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) {
bool Changed = false;
- // Exception handling
+ // Function registration for exception handling
if (EnableEH) {
// Register __resumeException function
FunctionType *ResumeFTy =
@@ -715,26 +736,15 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) {
FunctionType *EHTypeIDTy =
FunctionType::get(IRB.getInt32Ty(), IRB.getInt8PtrTy(), false);
EHTypeIDF = getEmscriptenFunction(EHTypeIDTy, "llvm_eh_typeid_for", &M);
-
- for (Function &F : M) {
- if (F.isDeclaration())
- continue;
- Changed |= runEHOnFunction(F);
- }
}
- // Setjmp/longjmp handling
+ // Function registration and data pre-gathering for setjmp/longjmp handling
if (DoSjLj) {
- Changed = true; // We have setjmp or longjmp somewhere
-
// Register emscripten_longjmp function
FunctionType *FTy = FunctionType::get(
- IRB.getVoidTy(), {IRB.getInt32Ty(), IRB.getInt32Ty()}, false);
+ IRB.getVoidTy(), {getAddrIntType(&M), IRB.getInt32Ty()}, false);
EmLongjmpF = getEmscriptenFunction(FTy, "emscripten_longjmp", &M);
- if (LongjmpF)
- replaceLongjmpWithEmscriptenLongjmp(LongjmpF, EmLongjmpF);
-
if (SetjmpF) {
// Register saveSetjmp function
FunctionType *SetjmpFTy = SetjmpF->getFunctionType();
@@ -747,19 +757,37 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) {
// Register testSetjmp function
FTy = FunctionType::get(
IRB.getInt32Ty(),
- {IRB.getInt32Ty(), Type::getInt32PtrTy(C), IRB.getInt32Ty()}, false);
+ {getAddrIntType(&M), Type::getInt32PtrTy(C), IRB.getInt32Ty()},
+ false);
TestSetjmpF = getEmscriptenFunction(FTy, "testSetjmp", &M);
- // Only traverse functions that uses setjmp in order not to insert
- // unnecessary prep / cleanup code in every function
- SmallPtrSet<Function *, 8> SetjmpUsers;
+ // Precompute setjmp users
for (User *U : SetjmpF->users()) {
auto *UI = cast<Instruction>(U);
SetjmpUsers.insert(UI->getFunction());
}
+ }
+ }
+
+ // Exception handling transformation
+ if (EnableEH) {
+ for (Function &F : M) {
+ if (F.isDeclaration())
+ continue;
+ Changed |= runEHOnFunction(F);
+ }
+ }
+
+ // Setjmp/longjmp handling transformation
+ if (DoSjLj) {
+ Changed = true; // We have setjmp or longjmp somewhere
+ if (LongjmpF)
+ replaceLongjmpWithEmscriptenLongjmp(LongjmpF, EmLongjmpF);
+ // Only traverse functions that uses setjmp in order not to insert
+ // unnecessary prep / cleanup code in every function
+ if (SetjmpF)
for (Function *F : SetjmpUsers)
runSjLjOnFunction(*F);
- }
}
if (!Changed) {
@@ -787,8 +815,6 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runEHOnFunction(Function &F) {
bool Changed = false;
SmallVector<Instruction *, 64> ToErase;
SmallPtrSet<LandingPadInst *, 32> LandingPads;
- bool AllowExceptions = areAllExceptionsAllowed() ||
- EHAllowlistSet.count(std::string(F.getName()));
for (BasicBlock &BB : F) {
auto *II = dyn_cast<InvokeInst>(BB.getTerminator());
@@ -798,14 +824,53 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runEHOnFunction(Function &F) {
LandingPads.insert(II->getLandingPadInst());
IRB.SetInsertPoint(II);
- bool NeedInvoke = AllowExceptions && canThrow(II->getCalledOperand());
+ const Value *Callee = II->getCalledOperand();
+ bool NeedInvoke = supportsException(&F) && canThrow(Callee);
if (NeedInvoke) {
// Wrap invoke with invoke wrapper and generate preamble/postamble
Value *Threw = wrapInvoke(II);
ToErase.push_back(II);
+ // If setjmp/longjmp handling is enabled, the thrown value can be not an
+ // exception but a longjmp. If the current function contains calls to
+ // setjmp, it will be appropriately handled in runSjLjOnFunction. But even
+ // if the function does not contain setjmp calls, we shouldn't silently
+ // ignore longjmps; we should rethrow them so they can be correctly
+ // handled in somewhere up the call chain where setjmp is.
+ // __THREW__'s value is 0 when nothing happened, 1 when an exception is
+ // thrown, other values when longjmp is thrown.
+ //
+ // if (%__THREW__.val == 0 || %__THREW__.val == 1)
+ // goto %tail
+ // else
+ // goto %longjmp.rethrow
+ //
+ // longjmp.rethrow: ;; This is longjmp. Rethrow it
+ // %__threwValue.val = __threwValue
+ // emscripten_longjmp(%__THREW__.val, %__threwValue.val);
+ //
+ // tail: ;; Nothing happened or an exception is thrown
+ // ... Continue exception handling ...
+ if (DoSjLj && !SetjmpUsers.count(&F) && canLongjmp(M, Callee)) {
+ BasicBlock *Tail = BasicBlock::Create(C, "tail", &F);
+ BasicBlock *RethrowBB = BasicBlock::Create(C, "longjmp.rethrow", &F);
+ Value *CmpEqOne =
+ IRB.CreateICmpEQ(Threw, getAddrSizeInt(&M, 1), "cmp.eq.one");
+ Value *CmpEqZero =
+ IRB.CreateICmpEQ(Threw, getAddrSizeInt(&M, 0), "cmp.eq.zero");
+ Value *Or = IRB.CreateOr(CmpEqZero, CmpEqOne, "or");
+ IRB.CreateCondBr(Or, Tail, RethrowBB);
+ IRB.SetInsertPoint(RethrowBB);
+ Value *ThrewValue = IRB.CreateLoad(IRB.getInt32Ty(), ThrewValueGV,
+ ThrewValueGV->getName() + ".val");
+ IRB.CreateCall(EmLongjmpF, {Threw, ThrewValue});
+
+ IRB.CreateUnreachable();
+ IRB.SetInsertPoint(Tail);
+ }
+
// Insert a branch based on __THREW__ variable
- Value *Cmp = IRB.CreateICmpEQ(Threw, IRB.getInt32(1), "cmp");
+ Value *Cmp = IRB.CreateICmpEQ(Threw, getAddrSizeInt(&M, 1), "cmp");
IRB.CreateCondBr(Cmp, II->getUnwindDest(), II->getNormalDest());
} else {
@@ -885,16 +950,9 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runEHOnFunction(Function &F) {
SmallVector<Value *, 16> FMCArgs;
for (unsigned I = 0, E = LPI->getNumClauses(); I < E; ++I) {
Constant *Clause = LPI->getClause(I);
- // As a temporary workaround for the lack of aggregate varargs support
- // in the interface between JS and wasm, break out filter operands into
- // their component elements.
- if (LPI->isFilter(I)) {
- auto *ATy = cast<ArrayType>(Clause->getType());
- for (unsigned J = 0, E = ATy->getNumElements(); J < E; ++J) {
- Value *EV = IRB.CreateExtractValue(Clause, makeArrayRef(J), "filter");
- FMCArgs.push_back(EV);
- }
- } else
+ // TODO Handle filters (= exception specifications).
+ // https://bugs.llvm.org/show_bug.cgi?id=50396
+ if (LPI->isCatch(I))
FMCArgs.push_back(Clause);
}
@@ -1071,12 +1129,15 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) {
// __THREW__ = 0;
for (auto I = std::next(BasicBlock::iterator(ThrewLI)), IE = BB->end();
I != IE; ++I) {
- if (auto *SI = dyn_cast<StoreInst>(I))
- if (auto *GV = dyn_cast<GlobalVariable>(SI->getPointerOperand()))
- if (GV == ThrewGV && SI->getValueOperand() == IRB.getInt32(0)) {
+ if (auto *SI = dyn_cast<StoreInst>(I)) {
+ if (auto *GV = dyn_cast<GlobalVariable>(SI->getPointerOperand())) {
+ if (GV == ThrewGV &&
+ SI->getValueOperand() == getAddrSizeInt(&M, 0)) {
ThrewResetSI = SI;
break;
}
+ }
+ }
}
assert(Threw && ThrewLI && "Cannot find __THREW__ load after invoke");
assert(ThrewResetSI && "Cannot find __THREW__ store after invoke");
@@ -1087,6 +1148,46 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) {
Threw = wrapInvoke(CI);
ToErase.push_back(CI);
Tail = SplitBlock(BB, CI->getNextNode());
+
+ // If exception handling is enabled, the thrown value can be not a
+ // longjmp but an exception, in which case we shouldn't silently ignore
+ // exceptions; we should rethrow them.
+ // __THREW__'s value is 0 when nothing happened, 1 when an exception is
+ // thrown, other values when longjmp is thrown.
+ //
+ // if (%__THREW__.val == 1)
+ // goto %eh.rethrow
+ // else
+ // goto %normal
+ //
+ // eh.rethrow: ;; Rethrow exception
+ // %exn = call @__cxa_find_matching_catch_2() ;; Retrieve thrown ptr
+ // __resumeException(%exn)
+ //
+ // normal:
+ // <-- Insertion point. Will insert sjlj handling code from here
+ // goto %tail
+ //
+ // tail:
+ // ...
+ if (supportsException(&F) && canThrow(Callee)) {
+ IRB.SetInsertPoint(CI);
+ // We will add a new conditional branch. So remove the branch created
+ // when we split the BB
+ ToErase.push_back(BB->getTerminator());
+ BasicBlock *NormalBB = BasicBlock::Create(C, "normal", &F);
+ BasicBlock *RethrowBB = BasicBlock::Create(C, "eh.rethrow", &F);
+ Value *CmpEqOne =
+ IRB.CreateICmpEQ(Threw, getAddrSizeInt(&M, 1), "cmp.eq.one");
+ IRB.CreateCondBr(CmpEqOne, RethrowBB, NormalBB);
+ IRB.SetInsertPoint(RethrowBB);
+ CallInst *Exn = IRB.CreateCall(getFindMatchingCatch(M, 0), {}, "exn");
+ IRB.CreateCall(ResumeF, {Exn});
+ IRB.CreateUnreachable();
+ IRB.SetInsertPoint(NormalBB);
+ IRB.CreateBr(Tail);
+ BB = NormalBB; // New insertion point to insert testSetjmp()
+ }
}
// We need to replace the terminator in Tail - SplitBlock makes BB go