mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2025-01-31 12:41:49 +01:00
[WebAssembly] Rename Emscripten EH functions
Renaming for some Emscripten EH functions has so far been done in wasm-emscripten-finalize tool in Binaryen. But recently we decided to make a compilation/linking path that does not rely on wasm-emscripten-finalize for modifications, so here we move that functionality to LLVM. Invoke wrappers are generated in LowerEmscriptenEHSjLj pass, but final wasm types are not available in the IR pass, we need to rename them at the end of the pipeline. This patch also removes uses of `emscripten_longjmp_jmpbuf` in LowerEmscriptenEHSjLj pass, replacing that with `emscripten_longjmp`. `emscripten_longjmp_jmpbuf` is lowered to `emscripten_longjmp`, but previously we generated calls to `emscripten_longjmp_jmpbuf` in LowerEmscriptenEHSjLj pass because it takes `jmp_buf*` instead of `i32`. But we were able use `ptrtoint` to make it use `emscripten_longjmp` directly here. Addresses: https://github.com/WebAssembly/binaryen/issues/3043 https://github.com/WebAssembly/binaryen/issues/3081 Companions: https://github.com/WebAssembly/binaryen/pull/3191 https://github.com/emscripten-core/emscripten/pull/12399 Reviewed By: dschuff, tlively, sbc100 Differential Revision: https://reviews.llvm.org/D88697
This commit is contained in:
parent
4940183748
commit
0ab2923b30
@ -49,6 +49,8 @@ using namespace llvm;
|
||||
#define DEBUG_TYPE "asm-printer"
|
||||
|
||||
extern cl::opt<bool> WasmKeepRegisters;
|
||||
extern cl::opt<bool> EnableEmException;
|
||||
extern cl::opt<bool> EnableEmSjLj;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Helpers.
|
||||
@ -81,10 +83,91 @@ WebAssemblyTargetStreamer *WebAssemblyAsmPrinter::getTargetStreamer() {
|
||||
return static_cast<WebAssemblyTargetStreamer *>(TS);
|
||||
}
|
||||
|
||||
// Emscripten exception handling helpers
|
||||
//
|
||||
// This converts invoke names generated by LowerEmscriptenEHSjLj to real names
|
||||
// that are expected by JavaScript glue code. The invoke names generated by
|
||||
// Emscripten JS glue code are based on their argument and return types; for
|
||||
// example, for a function that takes an i32 and returns nothing, it is
|
||||
// 'invoke_vi'. But the format of invoke generated by LowerEmscriptenEHSjLj pass
|
||||
// contains a mangled string generated from their IR types, for example,
|
||||
// "__invoke_void_%struct.mystruct*_int", because final wasm types are not
|
||||
// available in the IR pass. So we convert those names to the form that
|
||||
// Emscripten JS code expects.
|
||||
//
|
||||
// Refer to LowerEmscriptenEHSjLj pass for more details.
|
||||
|
||||
// Returns true if the given function name is an invoke name generated by
|
||||
// LowerEmscriptenEHSjLj pass.
|
||||
static bool isEmscriptenInvokeName(StringRef Name) {
|
||||
if (Name.front() == '"' && Name.back() == '"')
|
||||
Name = Name.substr(1, Name.size() - 2);
|
||||
return Name.startswith("__invoke_");
|
||||
}
|
||||
|
||||
// Returns a character that represents the given wasm value type in invoke
|
||||
// signatures.
|
||||
static char getInvokeSig(wasm::ValType VT) {
|
||||
switch (VT) {
|
||||
case wasm::ValType::I32:
|
||||
return 'i';
|
||||
case wasm::ValType::I64:
|
||||
return 'j';
|
||||
case wasm::ValType::F32:
|
||||
return 'f';
|
||||
case wasm::ValType::F64:
|
||||
return 'd';
|
||||
case wasm::ValType::V128:
|
||||
return 'V';
|
||||
case wasm::ValType::EXNREF:
|
||||
return 'E';
|
||||
case wasm::ValType::EXTERNREF:
|
||||
return 'X';
|
||||
}
|
||||
}
|
||||
|
||||
// Given the wasm signature, generate the invoke name in the format JS glue code
|
||||
// expects.
|
||||
static std::string getEmscriptenInvokeSymbolName(wasm::WasmSignature *Sig) {
|
||||
assert(Sig->Returns.size() <= 1);
|
||||
std::string Ret = "invoke_";
|
||||
if (!Sig->Returns.empty())
|
||||
for (auto VT : Sig->Returns)
|
||||
Ret += getInvokeSig(VT);
|
||||
else
|
||||
Ret += 'v';
|
||||
// Invokes' first argument is a pointer to the original function, so skip it
|
||||
for (unsigned I = 1, E = Sig->Params.size(); I < E; I++)
|
||||
Ret += getInvokeSig(Sig->Params[I]);
|
||||
return Ret;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// WebAssemblyAsmPrinter Implementation.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
MCSymbolWasm *WebAssemblyAsmPrinter::getMCSymbolForFunction(
|
||||
const Function *F, bool EnableEmEH, wasm::WasmSignature *Sig,
|
||||
bool &InvokeDetected) {
|
||||
MCSymbolWasm *WasmSym = nullptr;
|
||||
if (EnableEmEH && isEmscriptenInvokeName(F->getName())) {
|
||||
assert(Sig);
|
||||
InvokeDetected = true;
|
||||
if (Sig->Returns.size() > 1) {
|
||||
std::string Msg =
|
||||
"Emscripten EH/SjLj does not support multivalue returns: " +
|
||||
std::string(F->getName()) + ": " +
|
||||
WebAssembly::signatureToString(Sig);
|
||||
report_fatal_error(Msg);
|
||||
}
|
||||
WasmSym = cast<MCSymbolWasm>(
|
||||
GetExternalSymbolSymbol(getEmscriptenInvokeSymbolName(Sig)));
|
||||
} else {
|
||||
WasmSym = cast<MCSymbolWasm>(getSymbol(F));
|
||||
}
|
||||
return WasmSym;
|
||||
}
|
||||
|
||||
void WebAssemblyAsmPrinter::emitEndOfAsmFile(Module &M) {
|
||||
for (auto &It : OutContext.getSymbols()) {
|
||||
// Emit a .globaltype and .eventtype declaration.
|
||||
@ -95,6 +178,7 @@ void WebAssemblyAsmPrinter::emitEndOfAsmFile(Module &M) {
|
||||
getTargetStreamer()->emitEventType(Sym);
|
||||
}
|
||||
|
||||
DenseSet<MCSymbol *> InvokeSymbols;
|
||||
for (const auto &F : M) {
|
||||
if (F.isIntrinsic())
|
||||
continue;
|
||||
@ -104,31 +188,46 @@ void WebAssemblyAsmPrinter::emitEndOfAsmFile(Module &M) {
|
||||
SmallVector<MVT, 4> Results;
|
||||
SmallVector<MVT, 4> Params;
|
||||
computeSignatureVTs(F.getFunctionType(), &F, F, TM, Params, Results);
|
||||
auto *Sym = cast<MCSymbolWasm>(getSymbol(&F));
|
||||
// At this point these MCSymbols may or may not have been created already
|
||||
// and thus also contain a signature, but we need to get the signature
|
||||
// anyway here in case it is an invoke that has not yet been created. We
|
||||
// will discard it later if it turns out not to be necessary.
|
||||
auto Signature = signatureFromMVTs(Results, Params);
|
||||
bool InvokeDetected = false;
|
||||
auto *Sym = getMCSymbolForFunction(&F, EnableEmException || EnableEmSjLj,
|
||||
Signature.get(), InvokeDetected);
|
||||
|
||||
// Multiple functions can be mapped to the same invoke symbol. For
|
||||
// example, two IR functions '__invoke_void_i8*' and '__invoke_void_i32'
|
||||
// are both mapped to '__invoke_vi'. We keep them in a set once we emit an
|
||||
// Emscripten EH symbol so we don't emit the same symbol twice.
|
||||
if (InvokeDetected && !InvokeSymbols.insert(Sym).second)
|
||||
continue;
|
||||
|
||||
Sym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
|
||||
if (!Sym->getSignature()) {
|
||||
auto Signature = signatureFromMVTs(Results, Params);
|
||||
Sym->setSignature(Signature.get());
|
||||
addSignature(std::move(Signature));
|
||||
} else {
|
||||
// This symbol has already been created and had a signature. Discard it.
|
||||
Signature.reset();
|
||||
}
|
||||
// FIXME: this was originally intended for post-linking and was only used
|
||||
// for imports that were only called indirectly (i.e. s2wasm could not
|
||||
// infer the type from a call). With object files it applies to all
|
||||
// imports. so fix the names and the tests, or rethink how import
|
||||
// delcarations work in asm files.
|
||||
|
||||
getTargetStreamer()->emitFunctionType(Sym);
|
||||
|
||||
if (TM.getTargetTriple().isOSBinFormatWasm() &&
|
||||
F.hasFnAttribute("wasm-import-module")) {
|
||||
if (F.hasFnAttribute("wasm-import-module")) {
|
||||
StringRef Name =
|
||||
F.getFnAttribute("wasm-import-module").getValueAsString();
|
||||
Sym->setImportModule(storeName(Name));
|
||||
getTargetStreamer()->emitImportModule(Sym, Name);
|
||||
}
|
||||
if (TM.getTargetTriple().isOSBinFormatWasm() &&
|
||||
F.hasFnAttribute("wasm-import-name")) {
|
||||
if (F.hasFnAttribute("wasm-import-name")) {
|
||||
// If this is a converted Emscripten EH/SjLj symbol, we shouldn't use
|
||||
// the original function name but the converted symbol name.
|
||||
StringRef Name =
|
||||
F.getFnAttribute("wasm-import-name").getValueAsString();
|
||||
InvokeDetected
|
||||
? Sym->getName()
|
||||
: F.getFnAttribute("wasm-import-name").getValueAsString();
|
||||
Sym->setImportName(storeName(Name));
|
||||
getTargetStreamer()->emitImportName(Sym, Name);
|
||||
}
|
||||
@ -304,7 +403,6 @@ void WebAssemblyAsmPrinter::emitFunctionBodyStart() {
|
||||
addSignature(std::move(Signature));
|
||||
WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
|
||||
|
||||
// FIXME: clean up how params and results are emitted (use signatures)
|
||||
getTargetStreamer()->emitFunctionType(WasmSym);
|
||||
|
||||
// Emit the function index.
|
||||
|
@ -77,6 +77,9 @@ public:
|
||||
MVT getRegType(unsigned RegNo) const;
|
||||
std::string regToString(const MachineOperand &MO);
|
||||
WebAssemblyTargetStreamer *getTargetStreamer();
|
||||
MCSymbolWasm *getMCSymbolForFunction(const Function *F, bool EnableEmEH,
|
||||
wasm::WasmSignature *Sig,
|
||||
bool &InvokeDetected);
|
||||
};
|
||||
|
||||
} // end namespace llvm
|
||||
|
@ -140,8 +140,7 @@
|
||||
/// 1) Lower
|
||||
/// longjmp(buf, value)
|
||||
/// into
|
||||
/// emscripten_longjmp_jmpbuf(buf, value)
|
||||
/// emscripten_longjmp_jmpbuf will be lowered to emscripten_longjmp later.
|
||||
/// emscripten_longjmp(buf, value)
|
||||
///
|
||||
/// In case calls to setjmp() exists
|
||||
///
|
||||
@ -196,14 +195,9 @@
|
||||
/// 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
|
||||
/// case we just chain the longjmp to the caller. (Here we call
|
||||
/// emscripten_longjmp, which is different from emscripten_longjmp_jmpbuf.
|
||||
/// emscripten_longjmp_jmpbuf takes jmp_buf as its first argument, while
|
||||
/// emscripten_longjmp takes an int. Both of them will eventually be lowered
|
||||
/// to emscripten_longjmp in s2wasm, but here we need two signatures - we
|
||||
/// can't translate an int value to a jmp_buf.)
|
||||
/// Label -1 means no longjmp occurred. Otherwise we jump to the right
|
||||
/// post-setjmp BB based on the label.
|
||||
/// case we just chain the longjmp to the caller. Label -1 means no longjmp
|
||||
/// occurred. Otherwise we jump to the right post-setjmp BB based on the
|
||||
/// label.
|
||||
///
|
||||
///===----------------------------------------------------------------------===//
|
||||
|
||||
@ -241,7 +235,6 @@ class WebAssemblyLowerEmscriptenEHSjLj final : public ModulePass {
|
||||
Function *ResumeF = nullptr;
|
||||
Function *EHTypeIDF = nullptr;
|
||||
Function *EmLongjmpF = nullptr;
|
||||
Function *EmLongjmpJmpbufF = nullptr;
|
||||
Function *SaveSetjmpF = nullptr;
|
||||
Function *TestSetjmpF = nullptr;
|
||||
|
||||
@ -642,6 +635,30 @@ 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.
|
||||
static void replaceLongjmpWithEmscriptenLongjmp(Function *LongjmpF,
|
||||
Function *EmLongjmpF) {
|
||||
SmallVector<CallInst *, 8> ToErase;
|
||||
LLVMContext &C = LongjmpF->getParent()->getContext();
|
||||
IRBuilder<> IRB(C);
|
||||
for (User *U : LongjmpF->users()) {
|
||||
auto *CI = dyn_cast<CallInst>(U);
|
||||
if (!CI)
|
||||
report_fatal_error("Does not support indirect calls to longjmp");
|
||||
IRB.SetInsertPoint(CI);
|
||||
Value *Jmpbuf =
|
||||
IRB.CreatePtrToInt(CI->getArgOperand(0), IRB.getInt32Ty(), "jmpbuf");
|
||||
IRB.CreateCall(EmLongjmpF, {Jmpbuf, CI->getArgOperand(1)});
|
||||
ToErase.push_back(CI);
|
||||
}
|
||||
|
||||
for (auto *I : ToErase)
|
||||
I->eraseFromParent();
|
||||
}
|
||||
|
||||
bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) {
|
||||
LLVM_DEBUG(dbgs() << "********** Lower Emscripten EH & SjLj **********\n");
|
||||
|
||||
@ -654,6 +671,10 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) {
|
||||
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");
|
||||
|
||||
auto *TPC = getAnalysisIfAvailable<TargetPassConfig>();
|
||||
assert(TPC && "Expected a TargetPassConfig");
|
||||
auto &TM = TPC->getTM<WebAssemblyTargetMachine>();
|
||||
@ -696,22 +717,21 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) {
|
||||
if (DoSjLj) {
|
||||
Changed = true; // We have setjmp or longjmp somewhere
|
||||
|
||||
if (LongjmpF) {
|
||||
// Replace all uses of longjmp with emscripten_longjmp_jmpbuf, which is
|
||||
// defined in JS code
|
||||
EmLongjmpJmpbufF = getEmscriptenFunction(LongjmpF->getFunctionType(),
|
||||
"emscripten_longjmp_jmpbuf", &M);
|
||||
LongjmpF->replaceAllUsesWith(EmLongjmpJmpbufF);
|
||||
}
|
||||
// Register emscripten_longjmp function
|
||||
FunctionType *FTy = FunctionType::get(
|
||||
IRB.getVoidTy(), {IRB.getInt32Ty(), IRB.getInt32Ty()}, false);
|
||||
EmLongjmpF = getEmscriptenFunction(FTy, "emscripten_longjmp", &M);
|
||||
|
||||
if (LongjmpF)
|
||||
replaceLongjmpWithEmscriptenLongjmp(LongjmpF, EmLongjmpF);
|
||||
|
||||
if (SetjmpF) {
|
||||
// Register saveSetjmp function
|
||||
FunctionType *SetjmpFTy = SetjmpF->getFunctionType();
|
||||
FunctionType *FTy =
|
||||
FunctionType::get(Type::getInt32PtrTy(C),
|
||||
{SetjmpFTy->getParamType(0), IRB.getInt32Ty(),
|
||||
Type::getInt32PtrTy(C), IRB.getInt32Ty()},
|
||||
false);
|
||||
FTy = FunctionType::get(Type::getInt32PtrTy(C),
|
||||
{SetjmpFTy->getParamType(0), IRB.getInt32Ty(),
|
||||
Type::getInt32PtrTy(C), IRB.getInt32Ty()},
|
||||
false);
|
||||
SaveSetjmpF = getEmscriptenFunction(FTy, "saveSetjmp", &M);
|
||||
|
||||
// Register testSetjmp function
|
||||
@ -720,10 +740,6 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) {
|
||||
{IRB.getInt32Ty(), Type::getInt32PtrTy(C), IRB.getInt32Ty()}, false);
|
||||
TestSetjmpF = getEmscriptenFunction(FTy, "testSetjmp", &M);
|
||||
|
||||
FTy = FunctionType::get(IRB.getVoidTy(),
|
||||
{IRB.getInt32Ty(), IRB.getInt32Ty()}, false);
|
||||
EmLongjmpF = getEmscriptenFunction(FTy, "emscripten_longjmp", &M);
|
||||
|
||||
// Only traverse functions that uses setjmp in order not to insert
|
||||
// unnecessary prep / cleanup code in every function
|
||||
SmallPtrSet<Function *, 8> SetjmpUsers;
|
||||
|
@ -38,29 +38,34 @@ cl::opt<bool>
|
||||
" instruction output for test purposes only."),
|
||||
cl::init(false));
|
||||
|
||||
extern cl::opt<bool> EnableEmException;
|
||||
extern cl::opt<bool> EnableEmSjLj;
|
||||
|
||||
static void removeRegisterOperands(const MachineInstr *MI, MCInst &OutMI);
|
||||
|
||||
MCSymbol *
|
||||
WebAssemblyMCInstLower::GetGlobalAddressSymbol(const MachineOperand &MO) const {
|
||||
const GlobalValue *Global = MO.getGlobal();
|
||||
auto *WasmSym = cast<MCSymbolWasm>(Printer.getSymbol(Global));
|
||||
if (!isa<Function>(Global))
|
||||
return cast<MCSymbolWasm>(Printer.getSymbol(Global));
|
||||
|
||||
if (const auto *FuncTy = dyn_cast<FunctionType>(Global->getValueType())) {
|
||||
const MachineFunction &MF = *MO.getParent()->getParent()->getParent();
|
||||
const TargetMachine &TM = MF.getTarget();
|
||||
const Function &CurrentFunc = MF.getFunction();
|
||||
const auto *FuncTy = cast<FunctionType>(Global->getValueType());
|
||||
const MachineFunction &MF = *MO.getParent()->getParent()->getParent();
|
||||
const TargetMachine &TM = MF.getTarget();
|
||||
const Function &CurrentFunc = MF.getFunction();
|
||||
|
||||
SmallVector<MVT, 1> ResultMVTs;
|
||||
SmallVector<MVT, 4> ParamMVTs;
|
||||
const auto *const F = dyn_cast<Function>(Global);
|
||||
computeSignatureVTs(FuncTy, F, CurrentFunc, TM, ParamMVTs, ResultMVTs);
|
||||
|
||||
auto Signature = signatureFromMVTs(ResultMVTs, ParamMVTs);
|
||||
WasmSym->setSignature(Signature.get());
|
||||
Printer.addSignature(std::move(Signature));
|
||||
WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
|
||||
}
|
||||
SmallVector<MVT, 1> ResultMVTs;
|
||||
SmallVector<MVT, 4> ParamMVTs;
|
||||
const auto *const F = dyn_cast<Function>(Global);
|
||||
computeSignatureVTs(FuncTy, F, CurrentFunc, TM, ParamMVTs, ResultMVTs);
|
||||
auto Signature = signatureFromMVTs(ResultMVTs, ParamMVTs);
|
||||
|
||||
bool InvokeDetected = false;
|
||||
auto *WasmSym = Printer.getMCSymbolForFunction(
|
||||
F, EnableEmException || EnableEmSjLj, Signature.get(), InvokeDetected);
|
||||
WasmSym->setSignature(Signature.get());
|
||||
Printer.addSignature(std::move(Signature));
|
||||
WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
|
||||
return WasmSym;
|
||||
}
|
||||
|
||||
|
@ -34,13 +34,13 @@ using namespace llvm;
|
||||
#define DEBUG_TYPE "wasm"
|
||||
|
||||
// Emscripten's asm.js-style exception handling
|
||||
static cl::opt<bool> EnableEmException(
|
||||
cl::opt<bool> EnableEmException(
|
||||
"enable-emscripten-cxx-exceptions",
|
||||
cl::desc("WebAssembly Emscripten-style exception handling"),
|
||||
cl::init(false));
|
||||
|
||||
// Emscripten's asm.js-style setjmp/longjmp handling
|
||||
static cl::opt<bool> EnableEmSjLj(
|
||||
cl::opt<bool> EnableEmSjLj(
|
||||
"enable-emscripten-sjlj",
|
||||
cl::desc("WebAssembly Emscripten-style setjmp/longjmp handling"),
|
||||
cl::init(false));
|
||||
|
@ -153,12 +153,12 @@ define void @test_argument() {
|
||||
; CHECK-LABEL: test_invoke:
|
||||
; CHECK: i32.const $push[[L1:[0-9]+]]=, call_func{{$}}
|
||||
; CHECK-NEXT: i32.const $push[[L0:[0-9]+]]=, has_i32_ret{{$}}
|
||||
; CHECK-NEXT: call "__invoke_void_i32()*", $pop[[L1]], $pop[[L0]]{{$}}
|
||||
; CHECK-NEXT: call invoke_vi, $pop[[L1]], $pop[[L0]]{{$}}
|
||||
; CHECK: i32.const $push[[L3:[0-9]+]]=, call_func{{$}}
|
||||
; CHECK-NEXT: i32.const $push[[L2:[0-9]+]]=, has_i32_arg{{$}}
|
||||
; CHECK-NEXT: call "__invoke_void_i32()*", $pop[[L3]], $pop[[L2]]{{$}}
|
||||
; CHECK-NEXT: call invoke_vi, $pop[[L3]], $pop[[L2]]{{$}}
|
||||
; CHECK: i32.const $push[[L4:[0-9]+]]=, .Lhas_i32_arg_bitcast.2{{$}}
|
||||
; CHECK-NEXT: call __invoke_void, $pop[[L4]]{{$}}
|
||||
; CHECK-NEXT: call invoke_v, $pop[[L4]]{{$}}
|
||||
declare i32 @personality(...)
|
||||
define void @test_invoke() personality i32 (...)* @personality {
|
||||
entry:
|
||||
|
57
test/CodeGen/WebAssembly/lower-em-ehsjlj-multi-return.ll
Normal file
57
test/CodeGen/WebAssembly/lower-em-ehsjlj-multi-return.ll
Normal file
@ -0,0 +1,57 @@
|
||||
; RUN: not --crash llc < %s -enable-emscripten-cxx-exceptions -mattr=+multivalue 2>&1 | FileCheck %s --check-prefix=EH
|
||||
; RUN: not --crash llc < %s -enable-emscripten-sjlj -mattr=+multivalue 2>&1 | FileCheck %s --check-prefix=SJLJ
|
||||
|
||||
; Currently multivalue returning functions are not supported in Emscripten EH /
|
||||
; SjLj. Make sure they error out.
|
||||
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target triple = "wasm32-unknown-unknown"
|
||||
|
||||
%struct.__jmp_buf_tag = type { [6 x i32], i32, [32 x i32] }
|
||||
|
||||
define void @exception() personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
|
||||
entry:
|
||||
invoke {i32, i32} @foo(i32 3)
|
||||
to label %try.cont unwind label %lpad
|
||||
|
||||
lpad: ; preds = %entry
|
||||
%1 = landingpad { i8*, i32 }
|
||||
catch i8* null
|
||||
%2 = extractvalue { i8*, i32 } %1, 0
|
||||
%3 = extractvalue { i8*, i32 } %1, 1
|
||||
%4 = call i8* @__cxa_begin_catch(i8* %2) #2
|
||||
call void @__cxa_end_catch()
|
||||
br label %try.cont
|
||||
|
||||
try.cont: ; preds = %entry, %lpad
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @setjmp_longjmp() {
|
||||
entry:
|
||||
%buf = alloca [1 x %struct.__jmp_buf_tag], align 16
|
||||
%arraydecay = getelementptr inbounds [1 x %struct.__jmp_buf_tag], [1 x %struct.__jmp_buf_tag]* %buf, i32 0, i32 0
|
||||
%call = call i32 @setjmp(%struct.__jmp_buf_tag* %arraydecay) #0
|
||||
%arraydecay1 = getelementptr inbounds [1 x %struct.__jmp_buf_tag], [1 x %struct.__jmp_buf_tag]* %buf, i32 0, i32 0
|
||||
call {i32, i32} @foo(i32 3)
|
||||
call void @longjmp(%struct.__jmp_buf_tag* %arraydecay1, i32 1) #1
|
||||
unreachable
|
||||
}
|
||||
|
||||
declare {i32, i32} @foo(i32)
|
||||
declare i32 @__gxx_personality_v0(...)
|
||||
declare i8* @__cxa_begin_catch(i8*)
|
||||
declare void @__cxa_end_catch()
|
||||
; Function Attrs: returns_twice
|
||||
declare i32 @setjmp(%struct.__jmp_buf_tag*) #0
|
||||
; Function Attrs: noreturn
|
||||
declare void @longjmp(%struct.__jmp_buf_tag*, i32) #1
|
||||
declare i8* @malloc(i32)
|
||||
declare void @free(i8*)
|
||||
|
||||
attributes #0 = { returns_twice }
|
||||
attributes #1 = { noreturn }
|
||||
attributes #2 = { nounwind }
|
||||
|
||||
; EH: LLVM ERROR: Emscripten EH/SjLj does not support multivalue returns
|
||||
; SJLJ: LLVM ERROR: Emscripten EH/SjLj does not support multivalue returns
|
@ -1,20 +1,30 @@
|
||||
; RUN: llc < %s -enable-emscripten-cxx-exceptions | FileCheck %s --check-prefix=EH
|
||||
; RUN: llc < %s -enable-emscripten-sjlj | FileCheck %s --check-prefix=SJLJ
|
||||
; RUN: llc < %s | FileCheck %s --check-prefix=NONE
|
||||
; RUN: not --crash llc < %s -enable-emscripten-cxx-exceptions -mtriple=wasm64-unknown-unknown 2>&1 | FileCheck %s --check-prefix=WASM64-EH
|
||||
; RUN: not --crash llc < %s -enable-emscripten-sjlj -mtriple=wasm64-unknown-unknown 2>&1 | FileCheck %s --check-prefix=WASM64-SJLJ
|
||||
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target triple = "wasm32-unknown-unknown"
|
||||
|
||||
%struct.__jmp_buf_tag = type { [6 x i32], i32, [32 x i32] }
|
||||
|
||||
define hidden void @exception() personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
|
||||
define void @exception() personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
|
||||
; EH-LABEL: type exception,@function
|
||||
; NONE-LABEL: type exception,@function
|
||||
entry:
|
||||
invoke void @foo()
|
||||
invoke void @foo(i32 3)
|
||||
to label %invoke.cont unwind label %lpad
|
||||
; EH: call invoke_vi
|
||||
; EH-NOT: call __invoke_void_i32
|
||||
; NONE: call foo
|
||||
|
||||
invoke.cont:
|
||||
invoke void @bar()
|
||||
to label %try.cont unwind label %lpad
|
||||
; EH: call __invoke_void
|
||||
; NONE: call foo
|
||||
; EH: call invoke_v
|
||||
; EH-NOT: call __invoke_void
|
||||
; NONE: call bar
|
||||
|
||||
lpad: ; preds = %entry
|
||||
%0 = landingpad { i8*, i32 }
|
||||
@ -29,7 +39,7 @@ try.cont: ; preds = %entry, %lpad
|
||||
ret void
|
||||
}
|
||||
|
||||
define hidden void @setjmp_longjmp() {
|
||||
define void @setjmp_longjmp() {
|
||||
; SJLJ-LABEL: type setjmp_longjmp,@function
|
||||
; NONE-LABEL: type setjmp_longjmp,@function
|
||||
entry:
|
||||
@ -40,12 +50,30 @@ entry:
|
||||
call void @longjmp(%struct.__jmp_buf_tag* %arraydecay1, i32 1) #1
|
||||
unreachable
|
||||
; SJLJ: call saveSetjmp
|
||||
; SJLJ: i32.const emscripten_longjmp
|
||||
; SJLJ-NOT: i32.const emscripten_longjmp_jmpbuf
|
||||
; SJLJ: call invoke_vii
|
||||
; SJLJ-NOT: call "__invoke_void_%struct.__jmp_buf_tag*_i32"
|
||||
; SJLJ: call testSetjmp
|
||||
|
||||
; NONE: call setjmp
|
||||
; NONE: call longjmp
|
||||
}
|
||||
|
||||
declare void @foo()
|
||||
; Tests whether a user function with 'invoke_' prefix can be used
|
||||
declare void @invoke_ignoreme()
|
||||
define void @test_invoke_ignoreme() {
|
||||
; EH-LABEL: type test_invoke_ignoreme,@function
|
||||
; SJLJ-LABEL: type test_invoke_ignoreme,@function
|
||||
entry:
|
||||
call void @invoke_ignoreme()
|
||||
; EH: call invoke_ignoreme
|
||||
; SJLJ: call invoke_ignoreme
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @foo(i32)
|
||||
declare void @bar()
|
||||
declare i32 @__gxx_personality_v0(...)
|
||||
declare i8* @__cxa_begin_catch(i8*)
|
||||
declare void @__cxa_end_catch()
|
||||
@ -59,3 +87,20 @@ declare void @free(i8*)
|
||||
attributes #0 = { returns_twice }
|
||||
attributes #1 = { noreturn }
|
||||
attributes #2 = { nounwind }
|
||||
|
||||
; EH: .functype invoke_vi (i32, i32) -> ()
|
||||
; EH: .import_module invoke_vi, env
|
||||
; EH: .import_name invoke_vi, invoke_vi
|
||||
; EH-NOT: .functype __invoke_void_i32
|
||||
; EH-NOT: .import_module __invoke_void_i32
|
||||
; EH-NOT: .import_name __invoke_void_i32
|
||||
|
||||
; SJLJ: .functype emscripten_longjmp (i32, i32) -> ()
|
||||
; SJLJ: .import_module emscripten_longjmp, env
|
||||
; SJLJ: .import_name emscripten_longjmp, emscripten_longjmp
|
||||
; SJLJ-NOT: .functype emscripten_longjmp_jmpbuf
|
||||
; SJLJ-NOT: .import_module emscripten_longjmp_jmpbuf
|
||||
; SJLJ-NOT: .import_name emscripten_longjmp_jmpbuf
|
||||
|
||||
; WASM64-EH: LLVM ERROR: Emscripten EH/SjLj is not supported with wasm64 yet
|
||||
; WASM64-SJLJ: LLVM ERROR: Emscripten EH/SjLj is not supported with wasm64 yet
|
||||
|
@ -19,7 +19,7 @@ entry:
|
||||
; It needs to be the first argument (that's what we're testing here)
|
||||
; CHECK: i32.const $push[[FPTR:[0-9]+]]=, returns_struct
|
||||
; This is the sret stack region (as an offset from the stack pointer local)
|
||||
; CHECK: call "__invoke_{i32.i32}", $pop[[FPTR]]
|
||||
; CHECK: call invoke_vi, $pop[[FPTR]]
|
||||
%ret = call {i32, i32} @returns_struct()
|
||||
ret {i32, i32} %ret
|
||||
}
|
||||
|
@ -36,8 +36,9 @@ entry:
|
||||
; CHECK: entry.split:
|
||||
; CHECK-NEXT: phi i32 [ 0, %entry ], [ %[[LONGJMP_RESULT:.*]], %if.end ]
|
||||
; CHECK-NEXT: %[[ARRAYDECAY1:.*]] = getelementptr inbounds [1 x %struct.__jmp_buf_tag], [1 x %struct.__jmp_buf_tag]* %[[BUF]], i32 0, i32 0
|
||||
; CHECK-NEXT: %[[JMPBUF:.*]] = ptrtoint %struct.__jmp_buf_tag* %[[ARRAYDECAY1]] to i32
|
||||
; CHECK-NEXT: store i32 0, i32* @__THREW__
|
||||
; CHECK-NEXT: call cc{{.*}} void @"__invoke_void_%struct.__jmp_buf_tag*_i32"(void (%struct.__jmp_buf_tag*, i32)* @emscripten_longjmp_jmpbuf, %struct.__jmp_buf_tag* %[[ARRAYDECAY1]], i32 1)
|
||||
; CHECK-NEXT: call cc{{.*}} void @__invoke_void_i32_i32(void (i32, i32)* @emscripten_longjmp, i32 %[[JMPBUF]], i32 1)
|
||||
; CHECK-NEXT: %[[__THREW__VAL:.*]] = load i32, i32* @__THREW__
|
||||
; CHECK-NEXT: store i32 0, i32* @__THREW__
|
||||
; CHECK-NEXT: %[[CMP0:.*]] = icmp ne i32 %__THREW__.val, 0
|
||||
@ -187,8 +188,8 @@ entry:
|
||||
%arraydecay = getelementptr inbounds [1 x %struct.__jmp_buf_tag], [1 x %struct.__jmp_buf_tag]* %buf, i32 0, i32 0
|
||||
call void @longjmp(%struct.__jmp_buf_tag* %arraydecay, i32 5) #1
|
||||
unreachable
|
||||
; CHECK: %[[ARRAYDECAY:.*]] = getelementptr inbounds
|
||||
; CHECK-NEXT: call void @emscripten_longjmp_jmpbuf(%struct.__jmp_buf_tag* %[[ARRAYDECAY]], i32 5)
|
||||
; CHECK: %[[JMPBUF:.*]] = ptrtoint
|
||||
; CHECK-NEXT: call void @emscripten_longjmp(i32 %[[JMPBUF]], i32 5)
|
||||
}
|
||||
|
||||
; Test inline asm handling
|
||||
@ -224,7 +225,7 @@ entry:
|
||||
@buffer = global [1 x %struct.__jmp_buf_tag] zeroinitializer, align 16
|
||||
define void @longjmp_only() {
|
||||
entry:
|
||||
; CHECK: call void @emscripten_longjmp_jmpbuf
|
||||
; CHECK: call void @emscripten_longjmp
|
||||
call void @longjmp(%struct.__jmp_buf_tag* getelementptr inbounds ([1 x %struct.__jmp_buf_tag], [1 x %struct.__jmp_buf_tag]* @buffer, i32 0, i32 0), i32 1) #1
|
||||
unreachable
|
||||
}
|
||||
@ -265,28 +266,24 @@ declare void @free(i8*)
|
||||
; CHECK-DAG: declare void @setTempRet0(i32)
|
||||
; CHECK-DAG: declare i32* @saveSetjmp(%struct.__jmp_buf_tag*, i32, i32*, i32)
|
||||
; CHECK-DAG: declare i32 @testSetjmp(i32, i32*, i32)
|
||||
; CHECK-DAG: declare void @emscripten_longjmp_jmpbuf(%struct.__jmp_buf_tag*, i32)
|
||||
; CHECK-DAG: declare void @emscripten_longjmp(i32, i32)
|
||||
; CHECK-DAG: declare void @__invoke_void(void ()*)
|
||||
; CHECK-DAG: declare void @"__invoke_void_%struct.__jmp_buf_tag*_i32"(void (%struct.__jmp_buf_tag*, i32)*, %struct.__jmp_buf_tag*, i32)
|
||||
|
||||
attributes #0 = { returns_twice }
|
||||
attributes #1 = { noreturn }
|
||||
attributes #2 = { nounwind }
|
||||
attributes #3 = { allocsize(0) }
|
||||
; CHECK: attributes #{{[0-9]+}} = { nounwind "wasm-import-module"="env" "wasm-import-name"="getTempRet0" }
|
||||
; CHECK: attributes #{{[0-9]+}} = { nounwind "wasm-import-module"="env" "wasm-import-name"="setTempRet0" }
|
||||
; CHECK: attributes #{{[0-9]+}} = { "wasm-import-module"="env" "wasm-import-name"="__resumeException" }
|
||||
; CHECK: attributes #{{[0-9]+}} = { "wasm-import-module"="env" "wasm-import-name"="llvm_eh_typeid_for" }
|
||||
; CHECK: attributes #{{[0-9]+}} = { "wasm-import-module"="env" "wasm-import-name"="__invoke_void" }
|
||||
; CHECK: attributes #{{[0-9]+}} = { "wasm-import-module"="env" "wasm-import-name"="__cxa_find_matching_catch_3" }
|
||||
; CHECK: attributes #{{[0-9]+}} = { "wasm-import-module"="env" "wasm-import-name"="emscripten_longjmp_jmpbuf" }
|
||||
; CHECK: attributes #{{[0-9]+}} = { "wasm-import-module"="env" "wasm-import-name"="saveSetjmp" }
|
||||
; CHECK: attributes #{{[0-9]+}} = { "wasm-import-module"="env" "wasm-import-name"="testSetjmp" }
|
||||
; CHECK: attributes #{{[0-9]+}} = { "wasm-import-module"="env" "wasm-import-name"="emscripten_longjmp" }
|
||||
; CHECK: attributes #{{[0-9]+}} = { "wasm-import-module"="env" "wasm-import-name"="__invoke_i8*_i32_%struct.__jmp_buf_tag*" }
|
||||
; CHECK: attributes #{{[0-9]+}} = { "wasm-import-module"="env" "wasm-import-name"="__invoke_void_%struct.__jmp_buf_tag*_i32" }
|
||||
; CHECK: attributes #[[ALLOCSIZE_ATTR]] = { allocsize(1) }
|
||||
; CHECK-DAG: attributes #{{[0-9]+}} = { nounwind "wasm-import-module"="env" "wasm-import-name"="getTempRet0" }
|
||||
; CHECK-DAG: attributes #{{[0-9]+}} = { nounwind "wasm-import-module"="env" "wasm-import-name"="setTempRet0" }
|
||||
; CHECK-DAG: attributes #{{[0-9]+}} = { "wasm-import-module"="env" "wasm-import-name"="__resumeException" }
|
||||
; CHECK-DAG: attributes #{{[0-9]+}} = { "wasm-import-module"="env" "wasm-import-name"="llvm_eh_typeid_for" }
|
||||
; CHECK-DAG: attributes #{{[0-9]+}} = { "wasm-import-module"="env" "wasm-import-name"="__invoke_void" }
|
||||
; CHECK-DAG: attributes #{{[0-9]+}} = { "wasm-import-module"="env" "wasm-import-name"="__cxa_find_matching_catch_3" }
|
||||
; CHECK-DAG: attributes #{{[0-9]+}} = { "wasm-import-module"="env" "wasm-import-name"="saveSetjmp" }
|
||||
; CHECK-DAG: attributes #{{[0-9]+}} = { "wasm-import-module"="env" "wasm-import-name"="testSetjmp" }
|
||||
; CHECK-DAG: attributes #{{[0-9]+}} = { "wasm-import-module"="env" "wasm-import-name"="emscripten_longjmp" }
|
||||
; CHECK-DAG: attributes #{{[0-9]+}} = { "wasm-import-module"="env" "wasm-import-name"="__invoke_i8*_i32_%struct.__jmp_buf_tag*" }
|
||||
; CHECK-DAG: attributes #[[ALLOCSIZE_ATTR]] = { allocsize(1) }
|
||||
|
||||
!llvm.dbg.cu = !{!2}
|
||||
!llvm.module.flags = !{!0}
|
||||
|
Loading…
x
Reference in New Issue
Block a user