forked from OSchip/llvm-project
[WebAssembly] Misc. cosmetic changes in EH (NFC)
- Rename `wasm.catch` intrinsic to `wasm.catch.exn`, because we are planning to add a separate `wasm.catch.longjmp` intrinsic which returns two values. - Rename several variables - Remove an unnecessary parameter from `canLongjmp` and `isEmAsmCall` from LowerEmscriptenEHSjLj pass - Add `-verify-machineinstrs` in a test for a safety measure - Add more comments + fix some errors in comments - Replace `std::vector` with `SmallVector` for cases likely with small number of elements - Renamed `EnableEH`/`EnableSjLj` to `EnableEmEH`/`EnableEmSjLj`: We are soon going to add `EnableWasmSjLj`, so this makes the distincion clearer Reviewed By: tlively Differential Revision: https://reviews.llvm.org/D107405
This commit is contained in:
parent
ad25344620
commit
9bd02c433b
|
@ -50,7 +50,8 @@ def int_wasm_trunc_saturate_unsigned : Intrinsic<[llvm_anyint_ty],
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
// throw / rethrow
|
// throw / rethrow
|
||||||
// The immediate argument is an index to a tag, which is 0 for C++.
|
// The first immediate argument is an index to a tag, which is 0 for C++
|
||||||
|
// exception. The second argument is the thrown exception pointer.
|
||||||
def int_wasm_throw : Intrinsic<[], [llvm_i32_ty, llvm_ptr_ty],
|
def int_wasm_throw : Intrinsic<[], [llvm_i32_ty, llvm_ptr_ty],
|
||||||
[Throws, IntrNoReturn, ImmArg<ArgIndex<0>>]>;
|
[Throws, IntrNoReturn, ImmArg<ArgIndex<0>>]>;
|
||||||
def int_wasm_rethrow : Intrinsic<[], [], [Throws, IntrNoReturn]>;
|
def int_wasm_rethrow : Intrinsic<[], [], [Throws, IntrNoReturn]>;
|
||||||
|
@ -62,11 +63,12 @@ def int_wasm_get_exception : Intrinsic<[llvm_ptr_ty], [llvm_token_ty],
|
||||||
def int_wasm_get_ehselector : Intrinsic<[llvm_i32_ty], [llvm_token_ty],
|
def int_wasm_get_ehselector : Intrinsic<[llvm_i32_ty], [llvm_token_ty],
|
||||||
[IntrHasSideEffects]>;
|
[IntrHasSideEffects]>;
|
||||||
|
|
||||||
// wasm.catch returns the pointer to the exception object caught by wasm 'catch'
|
// wasm.catch.exn returns the pointer to the exception object caught by wasm
|
||||||
// instruction. This returns a single pointer, which is sufficient for C++
|
// 'catch' instruction. This returns a single pointer, which is the case for C++
|
||||||
// support. The immediate argument is an index to for a tag, which is 0 for C++.
|
// exceptions. The immediate argument is an index to for a tag, which is 0 for
|
||||||
def int_wasm_catch : Intrinsic<[llvm_ptr_ty], [llvm_i32_ty],
|
// C++ exceptions.
|
||||||
[IntrHasSideEffects, ImmArg<ArgIndex<0>>]>;
|
def int_wasm_catch_exn : Intrinsic<[llvm_ptr_ty], [llvm_i32_ty],
|
||||||
|
[IntrHasSideEffects, ImmArg<ArgIndex<0>>]>;
|
||||||
|
|
||||||
// WebAssembly EH must maintain the landingpads in the order assigned to them
|
// WebAssembly EH must maintain the landingpads in the order assigned to them
|
||||||
// by WasmEHPrepare pass to generate landingpad table in EHStreamer. This is
|
// by WasmEHPrepare pass to generate landingpad table in EHStreamer. This is
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
//
|
//
|
||||||
// - After:
|
// - After:
|
||||||
// catchpad ...
|
// catchpad ...
|
||||||
// exn = wasm.catch(WebAssembly::CPP_EXCEPTION);
|
// exn = wasm.catch.exn(WebAssembly::CPP_EXCEPTION);
|
||||||
// // Only add below in case it's not a single catch (...)
|
// // Only add below in case it's not a single catch (...)
|
||||||
// wasm.landingpad.index(index);
|
// wasm.landingpad.index(index);
|
||||||
// __wasm_lpad_context.lpad_index = index;
|
// __wasm_lpad_context.lpad_index = index;
|
||||||
|
@ -103,7 +103,7 @@ class WasmEHPrepare : public FunctionPass {
|
||||||
Function *LPadIndexF = nullptr; // wasm.landingpad.index() intrinsic
|
Function *LPadIndexF = nullptr; // wasm.landingpad.index() intrinsic
|
||||||
Function *LSDAF = nullptr; // wasm.lsda() intrinsic
|
Function *LSDAF = nullptr; // wasm.lsda() intrinsic
|
||||||
Function *GetExnF = nullptr; // wasm.get.exception() intrinsic
|
Function *GetExnF = nullptr; // wasm.get.exception() intrinsic
|
||||||
Function *CatchF = nullptr; // wasm.catch() intrinsic
|
Function *CatchF = nullptr; // wasm.catch.exn() intrinsic
|
||||||
Function *GetSelectorF = nullptr; // wasm.get.ehselector() intrinsic
|
Function *GetSelectorF = nullptr; // wasm.get.ehselector() intrinsic
|
||||||
FunctionCallee CallPersonalityF =
|
FunctionCallee CallPersonalityF =
|
||||||
nullptr; // _Unwind_CallPersonality() wrapper
|
nullptr; // _Unwind_CallPersonality() wrapper
|
||||||
|
@ -232,9 +232,9 @@ bool WasmEHPrepare::prepareEHPads(Function &F) {
|
||||||
GetExnF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_get_exception);
|
GetExnF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_get_exception);
|
||||||
GetSelectorF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_get_ehselector);
|
GetSelectorF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_get_ehselector);
|
||||||
|
|
||||||
// wasm.catch() will be lowered down to wasm 'catch' instruction in
|
// wasm.catch.exn() will be lowered down to wasm 'catch' instruction in
|
||||||
// instruction selection.
|
// instruction selection.
|
||||||
CatchF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_catch);
|
CatchF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_catch_exn);
|
||||||
|
|
||||||
// _Unwind_CallPersonality() wrapper function, which calls the personality
|
// _Unwind_CallPersonality() wrapper function, which calls the personality
|
||||||
CallPersonalityF = M.getOrInsertFunction(
|
CallPersonalityF = M.getOrInsertFunction(
|
||||||
|
@ -288,8 +288,8 @@ void WasmEHPrepare::prepareEHPad(BasicBlock *BB, bool NeedPersonality,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace wasm.get.exception intrinsic with wasm.catch intrinsic, which will
|
// Replace wasm.get.exception intrinsic with wasm.catch.exn intrinsic, which
|
||||||
// be lowered to wasm 'catch' instruction. We do this mainly because
|
// will be lowered to wasm 'catch' instruction. We do this mainly because
|
||||||
// instruction selection cannot handle wasm.get.exception intrinsic's token
|
// instruction selection cannot handle wasm.get.exception intrinsic's token
|
||||||
// argument.
|
// argument.
|
||||||
Instruction *CatchCI =
|
Instruction *CatchCI =
|
||||||
|
|
|
@ -51,8 +51,8 @@ using namespace llvm;
|
||||||
#define DEBUG_TYPE "asm-printer"
|
#define DEBUG_TYPE "asm-printer"
|
||||||
|
|
||||||
extern cl::opt<bool> WasmKeepRegisters;
|
extern cl::opt<bool> WasmKeepRegisters;
|
||||||
extern cl::opt<bool> EnableEmException;
|
extern cl::opt<bool> WasmEnableEmException;
|
||||||
extern cl::opt<bool> EnableEmSjLj;
|
extern cl::opt<bool> WasmEnableEmSjLj;
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// Helpers.
|
// Helpers.
|
||||||
|
@ -309,8 +309,9 @@ void WebAssemblyAsmPrinter::emitExternalDecls(const Module &M) {
|
||||||
// will discard it later if it turns out not to be necessary.
|
// will discard it later if it turns out not to be necessary.
|
||||||
auto Signature = signatureFromMVTs(Results, Params);
|
auto Signature = signatureFromMVTs(Results, Params);
|
||||||
bool InvokeDetected = false;
|
bool InvokeDetected = false;
|
||||||
auto *Sym = getMCSymbolForFunction(&F, EnableEmException || EnableEmSjLj,
|
auto *Sym =
|
||||||
Signature.get(), InvokeDetected);
|
getMCSymbolForFunction(&F, WasmEnableEmException || WasmEnableEmSjLj,
|
||||||
|
Signature.get(), InvokeDetected);
|
||||||
|
|
||||||
// Multiple functions can be mapped to the same invoke symbol. For
|
// Multiple functions can be mapped to the same invoke symbol. For
|
||||||
// example, two IR functions '__invoke_void_i8*' and '__invoke_void_i32'
|
// example, two IR functions '__invoke_void_i8*' and '__invoke_void_i32'
|
||||||
|
|
|
@ -1723,7 +1723,7 @@ SDValue WebAssemblyTargetLowering::LowerIntrinsic(SDValue Op,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
case Intrinsic::wasm_catch: {
|
case Intrinsic::wasm_catch_exn: {
|
||||||
SDValue SymNode = getCppExceptionSymNode(Op, 2, DAG);
|
SDValue SymNode = getCppExceptionSymNode(Op, 2, DAG);
|
||||||
return DAG.getNode(WebAssemblyISD::CATCH, DL,
|
return DAG.getNode(WebAssemblyISD::CATCH, DL,
|
||||||
{
|
{
|
||||||
|
|
|
@ -214,19 +214,19 @@ static cl::list<std::string>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
class WebAssemblyLowerEmscriptenEHSjLj final : public ModulePass {
|
class WebAssemblyLowerEmscriptenEHSjLj final : public ModulePass {
|
||||||
bool EnableEH; // Enable exception handling
|
bool EnableEmEH; // Enable Emscripten exception handling
|
||||||
bool EnableSjLj; // Enable setjmp/longjmp handling
|
bool EnableEmSjLj; // Enable Emscripten setjmp/longjmp handling
|
||||||
bool DoSjLj; // Whether we actually perform setjmp/longjmp handling
|
bool DoSjLj; // Whether we actually perform setjmp/longjmp handling
|
||||||
|
|
||||||
GlobalVariable *ThrewGV = nullptr;
|
GlobalVariable *ThrewGV = nullptr; // __THREW__ (Emscripten)
|
||||||
GlobalVariable *ThrewValueGV = nullptr;
|
GlobalVariable *ThrewValueGV = nullptr; // __threwValue (Emscripten)
|
||||||
Function *GetTempRet0Func = nullptr;
|
Function *GetTempRet0F = nullptr; // getTempRet0() (Emscripten)
|
||||||
Function *SetTempRet0Func = nullptr;
|
Function *SetTempRet0F = nullptr; // setTempRet0() (Emscripten)
|
||||||
Function *ResumeF = nullptr;
|
Function *ResumeF = nullptr; // __resumeException() (Emscripten)
|
||||||
Function *EHTypeIDF = nullptr;
|
Function *EHTypeIDF = nullptr; // llvm.eh.typeid.for() (intrinsic)
|
||||||
Function *EmLongjmpF = nullptr;
|
Function *EmLongjmpF = nullptr; // emscripten_longjmp() (Emscripten)
|
||||||
Function *SaveSetjmpF = nullptr;
|
Function *SaveSetjmpF = nullptr; // saveSetjmp() (Emscripten)
|
||||||
Function *TestSetjmpF = nullptr;
|
Function *TestSetjmpF = nullptr; // testSetjmp() (Emscripten)
|
||||||
|
|
||||||
// __cxa_find_matching_catch_N functions.
|
// __cxa_find_matching_catch_N functions.
|
||||||
// Indexed by the number of clauses in an original landingpad instruction.
|
// Indexed by the number of clauses in an original landingpad instruction.
|
||||||
|
@ -253,11 +253,11 @@ class WebAssemblyLowerEmscriptenEHSjLj final : public ModulePass {
|
||||||
Function *getInvokeWrapper(CallBase *CI);
|
Function *getInvokeWrapper(CallBase *CI);
|
||||||
|
|
||||||
bool areAllExceptionsAllowed() const { return EHAllowlistSet.empty(); }
|
bool areAllExceptionsAllowed() const { return EHAllowlistSet.empty(); }
|
||||||
bool canLongjmp(Module &M, const Value *Callee) const;
|
bool canLongjmp(const Value *Callee) const;
|
||||||
bool isEmAsmCall(Module &M, const Value *Callee) const;
|
bool isEmAsmCall(const Value *Callee) const;
|
||||||
bool supportsException(const Function *F) const {
|
bool supportsException(const Function *F) const {
|
||||||
return EnableEH && (areAllExceptionsAllowed() ||
|
return EnableEmEH && (areAllExceptionsAllowed() ||
|
||||||
EHAllowlistSet.count(std::string(F->getName())));
|
EHAllowlistSet.count(std::string(F->getName())));
|
||||||
}
|
}
|
||||||
|
|
||||||
void rebuildSSA(Function &F);
|
void rebuildSSA(Function &F);
|
||||||
|
@ -265,8 +265,9 @@ class WebAssemblyLowerEmscriptenEHSjLj final : public ModulePass {
|
||||||
public:
|
public:
|
||||||
static char ID;
|
static char ID;
|
||||||
|
|
||||||
WebAssemblyLowerEmscriptenEHSjLj(bool EnableEH = true, bool EnableSjLj = true)
|
WebAssemblyLowerEmscriptenEHSjLj(bool EnableEmEH = true,
|
||||||
: ModulePass(ID), EnableEH(EnableEH), EnableSjLj(EnableSjLj) {
|
bool EnableEmSjLj = true)
|
||||||
|
: ModulePass(ID), EnableEmEH(EnableEmEH), EnableEmSjLj(EnableEmSjLj) {
|
||||||
EHAllowlistSet.insert(EHAllowlist.begin(), EHAllowlist.end());
|
EHAllowlistSet.insert(EHAllowlist.begin(), EHAllowlist.end());
|
||||||
}
|
}
|
||||||
bool runOnModule(Module &M) override;
|
bool runOnModule(Module &M) override;
|
||||||
|
@ -282,9 +283,9 @@ INITIALIZE_PASS(WebAssemblyLowerEmscriptenEHSjLj, DEBUG_TYPE,
|
||||||
"WebAssembly Lower Emscripten Exceptions / Setjmp / Longjmp",
|
"WebAssembly Lower Emscripten Exceptions / Setjmp / Longjmp",
|
||||||
false, false)
|
false, false)
|
||||||
|
|
||||||
ModulePass *llvm::createWebAssemblyLowerEmscriptenEHSjLj(bool EnableEH,
|
ModulePass *llvm::createWebAssemblyLowerEmscriptenEHSjLj(bool EnableEmEH,
|
||||||
bool EnableSjLj) {
|
bool EnableEmSjLj) {
|
||||||
return new WebAssemblyLowerEmscriptenEHSjLj(EnableEH, EnableSjLj);
|
return new WebAssemblyLowerEmscriptenEHSjLj(EnableEmEH, EnableEmSjLj);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool canThrow(const Value *V) {
|
static bool canThrow(const Value *V) {
|
||||||
|
@ -504,8 +505,7 @@ Function *WebAssemblyLowerEmscriptenEHSjLj::getInvokeWrapper(CallBase *CI) {
|
||||||
return F;
|
return F;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WebAssemblyLowerEmscriptenEHSjLj::canLongjmp(Module &M,
|
bool WebAssemblyLowerEmscriptenEHSjLj::canLongjmp(const Value *Callee) const {
|
||||||
const Value *Callee) const {
|
|
||||||
if (auto *CalleeF = dyn_cast<Function>(Callee))
|
if (auto *CalleeF = dyn_cast<Function>(Callee))
|
||||||
if (CalleeF->isIntrinsic())
|
if (CalleeF->isIntrinsic())
|
||||||
return false;
|
return false;
|
||||||
|
@ -543,8 +543,7 @@ bool WebAssemblyLowerEmscriptenEHSjLj::canLongjmp(Module &M,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WebAssemblyLowerEmscriptenEHSjLj::isEmAsmCall(Module &M,
|
bool WebAssemblyLowerEmscriptenEHSjLj::isEmAsmCall(const Value *Callee) const {
|
||||||
const Value *Callee) const {
|
|
||||||
StringRef CalleeName = Callee->getName();
|
StringRef CalleeName = Callee->getName();
|
||||||
// This is an exhaustive list from Emscripten's <emscripten/em_asm.h>.
|
// This is an exhaustive list from Emscripten's <emscripten/em_asm.h>.
|
||||||
return CalleeName == "emscripten_asm_const_int" ||
|
return CalleeName == "emscripten_asm_const_int" ||
|
||||||
|
@ -558,7 +557,7 @@ bool WebAssemblyLowerEmscriptenEHSjLj::isEmAsmCall(Module &M,
|
||||||
// The code this generates is equivalent to the following JavaScript code:
|
// The code this generates is equivalent to the following JavaScript code:
|
||||||
// %__threwValue.val = __threwValue;
|
// %__threwValue.val = __threwValue;
|
||||||
// if (%__THREW__.val != 0 & %__threwValue.val != 0) {
|
// if (%__THREW__.val != 0 & %__threwValue.val != 0) {
|
||||||
// %label = _testSetjmp(mem[%__THREW__.val], setjmpTable, setjmpTableSize);
|
// %label = testSetjmp(mem[%__THREW__.val], setjmpTable, setjmpTableSize);
|
||||||
// if (%label == 0)
|
// if (%label == 0)
|
||||||
// emscripten_longjmp(%__THREW__.val, %__threwValue.val);
|
// emscripten_longjmp(%__THREW__.val, %__threwValue.val);
|
||||||
// setTempRet0(%__threwValue.val);
|
// setTempRet0(%__threwValue.val);
|
||||||
|
@ -591,7 +590,7 @@ void WebAssemblyLowerEmscriptenEHSjLj::wrapTestSetjmp(
|
||||||
Value *Cmp1 = IRB.CreateAnd(ThrewCmp, ThrewValueCmp, "cmp1");
|
Value *Cmp1 = IRB.CreateAnd(ThrewCmp, ThrewValueCmp, "cmp1");
|
||||||
IRB.CreateCondBr(Cmp1, ThenBB1, ElseBB1);
|
IRB.CreateCondBr(Cmp1, ThenBB1, ElseBB1);
|
||||||
|
|
||||||
// %label = _testSetjmp(mem[%__THREW__.val], _setjmpTable, _setjmpTableSize);
|
// %label = testSetjmp(mem[%__THREW__.val], setjmpTable, setjmpTableSize);
|
||||||
// if (%label == 0)
|
// if (%label == 0)
|
||||||
IRB.SetInsertPoint(ThenBB1);
|
IRB.SetInsertPoint(ThenBB1);
|
||||||
BasicBlock *ThenBB2 = BasicBlock::Create(C, "if.then2", F);
|
BasicBlock *ThenBB2 = BasicBlock::Create(C, "if.then2", F);
|
||||||
|
@ -612,7 +611,7 @@ void WebAssemblyLowerEmscriptenEHSjLj::wrapTestSetjmp(
|
||||||
|
|
||||||
// setTempRet0(%__threwValue.val);
|
// setTempRet0(%__threwValue.val);
|
||||||
IRB.SetInsertPoint(EndBB2);
|
IRB.SetInsertPoint(EndBB2);
|
||||||
IRB.CreateCall(SetTempRet0Func, ThrewValue);
|
IRB.CreateCall(SetTempRet0F, ThrewValue);
|
||||||
IRB.CreateBr(EndBB1);
|
IRB.CreateBr(EndBB1);
|
||||||
|
|
||||||
IRB.SetInsertPoint(ElseBB1);
|
IRB.SetInsertPoint(ElseBB1);
|
||||||
|
@ -628,7 +627,7 @@ void WebAssemblyLowerEmscriptenEHSjLj::wrapTestSetjmp(
|
||||||
// Output parameter assignment
|
// Output parameter assignment
|
||||||
Label = LabelPHI;
|
Label = LabelPHI;
|
||||||
EndBB = EndBB1;
|
EndBB = EndBB1;
|
||||||
LongjmpResult = IRB.CreateCall(GetTempRet0Func, None, "longjmp_result");
|
LongjmpResult = IRB.CreateCall(GetTempRet0F, None, "longjmp_result");
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebAssemblyLowerEmscriptenEHSjLj::rebuildSSA(Function &F) {
|
void WebAssemblyLowerEmscriptenEHSjLj::rebuildSSA(Function &F) {
|
||||||
|
@ -658,7 +657,7 @@ void WebAssemblyLowerEmscriptenEHSjLj::rebuildSSA(Function &F) {
|
||||||
// Replace uses of longjmp with emscripten_longjmp. emscripten_longjmp takes
|
// Replace uses of longjmp with emscripten_longjmp. emscripten_longjmp takes
|
||||||
// arguments of type {i32, i32} (wasm32) / {i64, i32} (wasm64) and longjmp takes
|
// 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
|
// {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.
|
// match. jmp_buf* will eventually be lowered to i32/i64 in the wasm backend.
|
||||||
static void replaceLongjmpWithEmscriptenLongjmp(Function *LongjmpF,
|
static void replaceLongjmpWithEmscriptenLongjmp(Function *LongjmpF,
|
||||||
Function *EmLongjmpF) {
|
Function *EmLongjmpF) {
|
||||||
Module *M = LongjmpF->getParent();
|
Module *M = LongjmpF->getParent();
|
||||||
|
@ -672,9 +671,9 @@ static void replaceLongjmpWithEmscriptenLongjmp(Function *LongjmpF,
|
||||||
auto *CI = dyn_cast<CallInst>(U);
|
auto *CI = dyn_cast<CallInst>(U);
|
||||||
if (CI && CI->getCalledFunction() == LongjmpF) {
|
if (CI && CI->getCalledFunction() == LongjmpF) {
|
||||||
IRB.SetInsertPoint(CI);
|
IRB.SetInsertPoint(CI);
|
||||||
Value *Jmpbuf =
|
Value *JmpBuf =
|
||||||
IRB.CreatePtrToInt(CI->getArgOperand(0), getAddrIntType(M), "jmpbuf");
|
IRB.CreatePtrToInt(CI->getArgOperand(0), getAddrIntType(M), "jmpbuf");
|
||||||
IRB.CreateCall(EmLongjmpF, {Jmpbuf, CI->getArgOperand(1)});
|
IRB.CreateCall(EmLongjmpF, {JmpBuf, CI->getArgOperand(1)});
|
||||||
ToErase.push_back(CI);
|
ToErase.push_back(CI);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -700,13 +699,13 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) {
|
||||||
Function *LongjmpF = M.getFunction("longjmp");
|
Function *LongjmpF = M.getFunction("longjmp");
|
||||||
bool SetjmpUsed = SetjmpF && !SetjmpF->use_empty();
|
bool SetjmpUsed = SetjmpF && !SetjmpF->use_empty();
|
||||||
bool LongjmpUsed = LongjmpF && !LongjmpF->use_empty();
|
bool LongjmpUsed = LongjmpF && !LongjmpF->use_empty();
|
||||||
DoSjLj = EnableSjLj && (SetjmpUsed || LongjmpUsed);
|
DoSjLj = EnableEmSjLj && (SetjmpUsed || LongjmpUsed);
|
||||||
|
|
||||||
auto *TPC = getAnalysisIfAvailable<TargetPassConfig>();
|
auto *TPC = getAnalysisIfAvailable<TargetPassConfig>();
|
||||||
assert(TPC && "Expected a TargetPassConfig");
|
assert(TPC && "Expected a TargetPassConfig");
|
||||||
auto &TM = TPC->getTM<WebAssemblyTargetMachine>();
|
auto &TM = TPC->getTM<WebAssemblyTargetMachine>();
|
||||||
|
|
||||||
if (EnableEH && TM.Options.ExceptionModel == ExceptionHandling::Wasm)
|
if (EnableEmEH && TM.Options.ExceptionModel == ExceptionHandling::Wasm)
|
||||||
report_fatal_error("-exception-model=wasm not allowed with "
|
report_fatal_error("-exception-model=wasm not allowed with "
|
||||||
"-enable-emscripten-cxx-exceptions");
|
"-enable-emscripten-cxx-exceptions");
|
||||||
|
|
||||||
|
@ -715,18 +714,18 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) {
|
||||||
// exception handling and setjmp/longjmp handling
|
// exception handling and setjmp/longjmp handling
|
||||||
ThrewGV = getGlobalVariable(M, getAddrIntType(&M), TM, "__THREW__");
|
ThrewGV = getGlobalVariable(M, getAddrIntType(&M), TM, "__THREW__");
|
||||||
ThrewValueGV = getGlobalVariable(M, IRB.getInt32Ty(), TM, "__threwValue");
|
ThrewValueGV = getGlobalVariable(M, IRB.getInt32Ty(), TM, "__threwValue");
|
||||||
GetTempRet0Func = getEmscriptenFunction(
|
GetTempRet0F = getEmscriptenFunction(
|
||||||
FunctionType::get(IRB.getInt32Ty(), false), "getTempRet0", &M);
|
FunctionType::get(IRB.getInt32Ty(), false), "getTempRet0", &M);
|
||||||
SetTempRet0Func = getEmscriptenFunction(
|
SetTempRet0F = getEmscriptenFunction(
|
||||||
FunctionType::get(IRB.getVoidTy(), IRB.getInt32Ty(), false),
|
FunctionType::get(IRB.getVoidTy(), IRB.getInt32Ty(), false),
|
||||||
"setTempRet0", &M);
|
"setTempRet0", &M);
|
||||||
GetTempRet0Func->setDoesNotThrow();
|
GetTempRet0F->setDoesNotThrow();
|
||||||
SetTempRet0Func->setDoesNotThrow();
|
SetTempRet0F->setDoesNotThrow();
|
||||||
|
|
||||||
bool Changed = false;
|
bool Changed = false;
|
||||||
|
|
||||||
// Function registration for exception handling
|
// Function registration for exception handling
|
||||||
if (EnableEH) {
|
if (EnableEmEH) {
|
||||||
// Register __resumeException function
|
// Register __resumeException function
|
||||||
FunctionType *ResumeFTy =
|
FunctionType *ResumeFTy =
|
||||||
FunctionType::get(IRB.getVoidTy(), IRB.getInt8PtrTy(), false);
|
FunctionType::get(IRB.getVoidTy(), IRB.getInt8PtrTy(), false);
|
||||||
|
@ -770,7 +769,7 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exception handling transformation
|
// Exception handling transformation
|
||||||
if (EnableEH) {
|
if (EnableEmEH) {
|
||||||
for (Function &F : M) {
|
for (Function &F : M) {
|
||||||
if (F.isDeclaration())
|
if (F.isDeclaration())
|
||||||
continue;
|
continue;
|
||||||
|
@ -836,9 +835,9 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runEHOnFunction(Function &F) {
|
||||||
// setjmp, it will be appropriately handled in runSjLjOnFunction. But even
|
// setjmp, it will be appropriately handled in runSjLjOnFunction. But even
|
||||||
// if the function does not contain setjmp calls, we shouldn't silently
|
// if the function does not contain setjmp calls, we shouldn't silently
|
||||||
// ignore longjmps; we should rethrow them so they can be correctly
|
// ignore longjmps; we should rethrow them so they can be correctly
|
||||||
// handled in somewhere up the call chain where setjmp is.
|
// handled in somewhere up the call chain where setjmp is. __THREW__'s
|
||||||
// __THREW__'s value is 0 when nothing happened, 1 when an exception is
|
// value is 0 when nothing happened, 1 when an exception is thrown, and
|
||||||
// thrown, other values when longjmp is thrown.
|
// other values when longjmp is thrown.
|
||||||
//
|
//
|
||||||
// if (%__THREW__.val == 0 || %__THREW__.val == 1)
|
// if (%__THREW__.val == 0 || %__THREW__.val == 1)
|
||||||
// goto %tail
|
// goto %tail
|
||||||
|
@ -851,7 +850,7 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runEHOnFunction(Function &F) {
|
||||||
//
|
//
|
||||||
// tail: ;; Nothing happened or an exception is thrown
|
// tail: ;; Nothing happened or an exception is thrown
|
||||||
// ... Continue exception handling ...
|
// ... Continue exception handling ...
|
||||||
if (DoSjLj && !SetjmpUsers.count(&F) && canLongjmp(M, Callee)) {
|
if (DoSjLj && !SetjmpUsers.count(&F) && canLongjmp(Callee)) {
|
||||||
BasicBlock *Tail = BasicBlock::Create(C, "tail", &F);
|
BasicBlock *Tail = BasicBlock::Create(C, "tail", &F);
|
||||||
BasicBlock *RethrowBB = BasicBlock::Create(C, "longjmp.rethrow", &F);
|
BasicBlock *RethrowBB = BasicBlock::Create(C, "longjmp.rethrow", &F);
|
||||||
Value *CmpEqOne =
|
Value *CmpEqOne =
|
||||||
|
@ -961,7 +960,7 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runEHOnFunction(Function &F) {
|
||||||
CallInst *FMCI = IRB.CreateCall(FMCF, FMCArgs, "fmc");
|
CallInst *FMCI = IRB.CreateCall(FMCF, FMCArgs, "fmc");
|
||||||
Value *Undef = UndefValue::get(LPI->getType());
|
Value *Undef = UndefValue::get(LPI->getType());
|
||||||
Value *Pair0 = IRB.CreateInsertValue(Undef, FMCI, 0, "pair0");
|
Value *Pair0 = IRB.CreateInsertValue(Undef, FMCI, 0, "pair0");
|
||||||
Value *TempRet0 = IRB.CreateCall(GetTempRet0Func, None, "tempret0");
|
Value *TempRet0 = IRB.CreateCall(GetTempRet0F, None, "tempret0");
|
||||||
Value *Pair1 = IRB.CreateInsertValue(Pair0, TempRet0, 1, "pair1");
|
Value *Pair1 = IRB.CreateInsertValue(Pair0, TempRet0, 1, "pair1");
|
||||||
|
|
||||||
LPI->replaceAllUsesWith(Pair1);
|
LPI->replaceAllUsesWith(Pair1);
|
||||||
|
@ -1002,9 +1001,9 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) {
|
||||||
IRBuilder<> IRB(C);
|
IRBuilder<> IRB(C);
|
||||||
SmallVector<Instruction *, 64> ToErase;
|
SmallVector<Instruction *, 64> ToErase;
|
||||||
// Vector of %setjmpTable values
|
// Vector of %setjmpTable values
|
||||||
std::vector<Instruction *> SetjmpTableInsts;
|
SmallVector<Instruction *, 4> SetjmpTableInsts;
|
||||||
// Vector of %setjmpTableSize values
|
// Vector of %setjmpTableSize values
|
||||||
std::vector<Instruction *> SetjmpTableSizeInsts;
|
SmallVector<Instruction *, 4> SetjmpTableSizeInsts;
|
||||||
|
|
||||||
// Setjmp preparation
|
// Setjmp preparation
|
||||||
|
|
||||||
|
@ -1012,11 +1011,11 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) {
|
||||||
// We create this as an instruction intentionally, and we don't want to fold
|
// We create this as an instruction intentionally, and we don't want to fold
|
||||||
// this instruction to a constant 4, because this value will be used in
|
// this instruction to a constant 4, because this value will be used in
|
||||||
// SSAUpdater.AddAvailableValue(...) later.
|
// SSAUpdater.AddAvailableValue(...) later.
|
||||||
BasicBlock &EntryBB = F.getEntryBlock();
|
BasicBlock *Entry = &F.getEntryBlock();
|
||||||
DebugLoc FirstDL = getOrCreateDebugLoc(&*EntryBB.begin(), F.getSubprogram());
|
DebugLoc FirstDL = getOrCreateDebugLoc(&*Entry->begin(), F.getSubprogram());
|
||||||
BinaryOperator *SetjmpTableSize = BinaryOperator::Create(
|
BinaryOperator *SetjmpTableSize =
|
||||||
Instruction::Add, IRB.getInt32(4), IRB.getInt32(0), "setjmpTableSize",
|
BinaryOperator::Create(Instruction::Add, IRB.getInt32(4), IRB.getInt32(0),
|
||||||
&*EntryBB.getFirstInsertionPt());
|
"setjmpTableSize", &*Entry->getFirstInsertionPt());
|
||||||
SetjmpTableSize->setDebugLoc(FirstDL);
|
SetjmpTableSize->setDebugLoc(FirstDL);
|
||||||
// setjmpTable = (int *) malloc(40);
|
// setjmpTable = (int *) malloc(40);
|
||||||
Instruction *SetjmpTable = CallInst::CreateMalloc(
|
Instruction *SetjmpTable = CallInst::CreateMalloc(
|
||||||
|
@ -1036,7 +1035,7 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) {
|
||||||
SetjmpTableSizeInsts.push_back(SetjmpTableSize);
|
SetjmpTableSizeInsts.push_back(SetjmpTableSize);
|
||||||
|
|
||||||
// Setjmp transformation
|
// Setjmp transformation
|
||||||
std::vector<PHINode *> SetjmpRetPHIs;
|
SmallVector<PHINode *, 4> SetjmpRetPHIs;
|
||||||
Function *SetjmpF = M.getFunction("setjmp");
|
Function *SetjmpF = M.getFunction("setjmp");
|
||||||
for (User *U : SetjmpF->users()) {
|
for (User *U : SetjmpF->users()) {
|
||||||
auto *CI = dyn_cast<CallInst>(U);
|
auto *CI = dyn_cast<CallInst>(U);
|
||||||
|
@ -1072,7 +1071,7 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) {
|
||||||
Instruction *NewSetjmpTable =
|
Instruction *NewSetjmpTable =
|
||||||
IRB.CreateCall(SaveSetjmpF, Args, "setjmpTable");
|
IRB.CreateCall(SaveSetjmpF, Args, "setjmpTable");
|
||||||
Instruction *NewSetjmpTableSize =
|
Instruction *NewSetjmpTableSize =
|
||||||
IRB.CreateCall(GetTempRet0Func, None, "setjmpTableSize");
|
IRB.CreateCall(GetTempRet0F, None, "setjmpTableSize");
|
||||||
SetjmpTableInsts.push_back(NewSetjmpTable);
|
SetjmpTableInsts.push_back(NewSetjmpTable);
|
||||||
SetjmpTableSizeInsts.push_back(NewSetjmpTableSize);
|
SetjmpTableSizeInsts.push_back(NewSetjmpTableSize);
|
||||||
ToErase.push_back(CI);
|
ToErase.push_back(CI);
|
||||||
|
@ -1098,9 +1097,9 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) {
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const Value *Callee = CI->getCalledOperand();
|
const Value *Callee = CI->getCalledOperand();
|
||||||
if (!canLongjmp(M, Callee))
|
if (!canLongjmp(Callee))
|
||||||
continue;
|
continue;
|
||||||
if (isEmAsmCall(M, Callee))
|
if (isEmAsmCall(Callee))
|
||||||
report_fatal_error("Cannot use EM_ASM* alongside setjmp/longjmp in " +
|
report_fatal_error("Cannot use EM_ASM* alongside setjmp/longjmp in " +
|
||||||
F.getName() +
|
F.getName() +
|
||||||
". Please consider using EM_JS, or move the "
|
". Please consider using EM_JS, or move the "
|
||||||
|
@ -1272,7 +1271,7 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) {
|
||||||
// Increment the iterator before removing the use from the list.
|
// Increment the iterator before removing the use from the list.
|
||||||
++UI;
|
++UI;
|
||||||
if (auto *I = dyn_cast<Instruction>(U.getUser()))
|
if (auto *I = dyn_cast<Instruction>(U.getUser()))
|
||||||
if (I->getParent() != &EntryBB)
|
if (I->getParent() != Entry)
|
||||||
SetjmpTableSSA.RewriteUse(U);
|
SetjmpTableSSA.RewriteUse(U);
|
||||||
}
|
}
|
||||||
for (auto UI = SetjmpTableSize->use_begin(), UE = SetjmpTableSize->use_end();
|
for (auto UI = SetjmpTableSize->use_begin(), UE = SetjmpTableSize->use_end();
|
||||||
|
@ -1280,7 +1279,7 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) {
|
||||||
Use &U = *UI;
|
Use &U = *UI;
|
||||||
++UI;
|
++UI;
|
||||||
if (auto *I = dyn_cast<Instruction>(U.getUser()))
|
if (auto *I = dyn_cast<Instruction>(U.getUser()))
|
||||||
if (I->getParent() != &EntryBB)
|
if (I->getParent() != Entry)
|
||||||
SetjmpTableSizeSSA.RewriteUse(U);
|
SetjmpTableSizeSSA.RewriteUse(U);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,8 +38,8 @@ cl::opt<bool>
|
||||||
" instruction output for test purposes only."),
|
" instruction output for test purposes only."),
|
||||||
cl::init(false));
|
cl::init(false));
|
||||||
|
|
||||||
extern cl::opt<bool> EnableEmException;
|
extern cl::opt<bool> WasmEnableEmException;
|
||||||
extern cl::opt<bool> EnableEmSjLj;
|
extern cl::opt<bool> WasmEnableEmSjLj;
|
||||||
|
|
||||||
static void removeRegisterOperands(const MachineInstr *MI, MCInst &OutMI);
|
static void removeRegisterOperands(const MachineInstr *MI, MCInst &OutMI);
|
||||||
|
|
||||||
|
@ -82,7 +82,8 @@ WebAssemblyMCInstLower::GetGlobalAddressSymbol(const MachineOperand &MO) const {
|
||||||
|
|
||||||
bool InvokeDetected = false;
|
bool InvokeDetected = false;
|
||||||
auto *WasmSym = Printer.getMCSymbolForFunction(
|
auto *WasmSym = Printer.getMCSymbolForFunction(
|
||||||
F, EnableEmException || EnableEmSjLj, Signature.get(), InvokeDetected);
|
F, WasmEnableEmException || WasmEnableEmSjLj, Signature.get(),
|
||||||
|
InvokeDetected);
|
||||||
WasmSym->setSignature(Signature.get());
|
WasmSym->setSignature(Signature.get());
|
||||||
Printer.addSignature(std::move(Signature));
|
Printer.addSignature(std::move(Signature));
|
||||||
WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
|
WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
|
||||||
|
|
|
@ -34,13 +34,13 @@ using namespace llvm;
|
||||||
#define DEBUG_TYPE "wasm"
|
#define DEBUG_TYPE "wasm"
|
||||||
|
|
||||||
// Emscripten's asm.js-style exception handling
|
// Emscripten's asm.js-style exception handling
|
||||||
cl::opt<bool> EnableEmException(
|
cl::opt<bool> WasmEnableEmException(
|
||||||
"enable-emscripten-cxx-exceptions",
|
"enable-emscripten-cxx-exceptions",
|
||||||
cl::desc("WebAssembly Emscripten-style exception handling"),
|
cl::desc("WebAssembly Emscripten-style exception handling"),
|
||||||
cl::init(false));
|
cl::init(false));
|
||||||
|
|
||||||
// Emscripten's asm.js-style setjmp/longjmp handling
|
// Emscripten's asm.js-style setjmp/longjmp handling
|
||||||
cl::opt<bool> EnableEmSjLj(
|
cl::opt<bool> WasmEnableEmSjLj(
|
||||||
"enable-emscripten-sjlj",
|
"enable-emscripten-sjlj",
|
||||||
cl::desc("WebAssembly Emscripten-style setjmp/longjmp handling"),
|
cl::desc("WebAssembly Emscripten-style setjmp/longjmp handling"),
|
||||||
cl::init(false));
|
cl::init(false));
|
||||||
|
@ -387,7 +387,7 @@ void WebAssemblyPassConfig::addIRPasses() {
|
||||||
// blocks. Lowering invokes when there is no EH support is done in
|
// blocks. Lowering invokes when there is no EH support is done in
|
||||||
// TargetPassConfig::addPassesToHandleExceptions, but this runs after this
|
// TargetPassConfig::addPassesToHandleExceptions, but this runs after this
|
||||||
// function and SjLj handling expects all invokes to be lowered before.
|
// function and SjLj handling expects all invokes to be lowered before.
|
||||||
if (!EnableEmException &&
|
if (!WasmEnableEmException &&
|
||||||
TM->Options.ExceptionModel == ExceptionHandling::None) {
|
TM->Options.ExceptionModel == ExceptionHandling::None) {
|
||||||
addPass(createLowerInvokePass());
|
addPass(createLowerInvokePass());
|
||||||
// The lower invoke pass may create unreachable code. Remove it in order not
|
// The lower invoke pass may create unreachable code. Remove it in order not
|
||||||
|
@ -396,9 +396,9 @@ void WebAssemblyPassConfig::addIRPasses() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle exceptions and setjmp/longjmp if enabled.
|
// Handle exceptions and setjmp/longjmp if enabled.
|
||||||
if (EnableEmException || EnableEmSjLj)
|
if (WasmEnableEmException || WasmEnableEmSjLj)
|
||||||
addPass(createWebAssemblyLowerEmscriptenEHSjLj(EnableEmException,
|
addPass(createWebAssemblyLowerEmscriptenEHSjLj(WasmEnableEmException,
|
||||||
EnableEmSjLj));
|
WasmEnableEmSjLj));
|
||||||
|
|
||||||
// Expand indirectbr instructions to switches.
|
// Expand indirectbr instructions to switches.
|
||||||
addPass(createIndirectBrExpandPass());
|
addPass(createIndirectBrExpandPass());
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
; RUN: opt < %s -wasm-lower-em-ehsjlj -S | FileCheck %s
|
; RUN: opt < %s -wasm-lower-em-ehsjlj -S | FileCheck %s
|
||||||
; RUN: llc < %s
|
; RUN: llc < %s -verify-machineinstrs
|
||||||
|
|
||||||
; Tests for cases when exception handling and setjmp/longjmp handling are mixed.
|
; Tests for cases when exception handling and setjmp/longjmp handling are mixed.
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ target triple = "wasm32-unknown-unknown"
|
||||||
%struct.Temp = type { i8 }
|
%struct.Temp = type { i8 }
|
||||||
|
|
||||||
; A single 'catch (int)' clause.
|
; A single 'catch (int)' clause.
|
||||||
; A wasm.catch() call, wasm.lsda() call, and personality call to generate a
|
; A wasm.catch.exn() call, wasm.lsda() call, and personality call to generate a
|
||||||
; selector should all be genereated after the catchpad.
|
; selector should all be genereated after the catchpad.
|
||||||
;
|
;
|
||||||
; void foo();
|
; void foo();
|
||||||
|
@ -37,7 +37,7 @@ catch.start: ; preds = %catch.dispatch
|
||||||
br i1 %matches, label %catch, label %rethrow
|
br i1 %matches, label %catch, label %rethrow
|
||||||
; CHECK: catch.start:
|
; CHECK: catch.start:
|
||||||
; CHECK-NEXT: %[[CATCHPAD:.*]] = catchpad
|
; CHECK-NEXT: %[[CATCHPAD:.*]] = catchpad
|
||||||
; CHECK-NEXT: %[[EXN:.*]] = call i8* @llvm.wasm.catch(i32 0)
|
; CHECK-NEXT: %[[EXN:.*]] = call i8* @llvm.wasm.catch.exn(i32 0)
|
||||||
; CHECK-NEXT: call void @llvm.wasm.landingpad.index(token %[[CATCHPAD]], i32 0)
|
; CHECK-NEXT: call void @llvm.wasm.landingpad.index(token %[[CATCHPAD]], i32 0)
|
||||||
; CHECK-NEXT: store i32 0, i32* getelementptr inbounds ({ i32, i8*, i32 }, { i32, i8*, i32 }* @__wasm_lpad_context, i32 0, i32 0)
|
; CHECK-NEXT: store i32 0, i32* getelementptr inbounds ({ i32, i8*, i32 }, { i32, i8*, i32 }* @__wasm_lpad_context, i32 0, i32 0)
|
||||||
; CHECK-NEXT: %[[LSDA:.*]] = call i8* @llvm.wasm.lsda()
|
; CHECK-NEXT: %[[LSDA:.*]] = call i8* @llvm.wasm.lsda()
|
||||||
|
@ -62,10 +62,10 @@ try.cont: ; preds = %entry, %catch
|
||||||
}
|
}
|
||||||
|
|
||||||
; Two try-catches.
|
; Two try-catches.
|
||||||
; For the catchpad with a single 'catch (...)', only a wasm.catch() call should
|
; For the catchpad with a single 'catch (...)', only a wasm.catch.exn() call
|
||||||
; be generated after the catchpad; wasm.landingpad.index() and personality call
|
; should be generated after the catchpad; wasm.landingpad.index() and
|
||||||
; should NOT be generated. For the other catchpad, the argument of
|
; personality call should NOT be generated. For the other catchpad, the argument
|
||||||
; wasm.landingpad.index() should be not 1 but 0.
|
; of wasm.landingpad.index() should be not 1 but 0.
|
||||||
;
|
;
|
||||||
; void foo();
|
; void foo();
|
||||||
; void test1() {
|
; void test1() {
|
||||||
|
|
Loading…
Reference in New Issue