From caf4c18b0450eaecabbf985de5ea7dbbed83bbe5 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Fri, 9 Feb 2024 15:49:55 +0900 Subject: [PATCH 01/28] [WebAssembly] Implement an alternative translation for -wasm-enable-sjlj Instead of maintaining per-function-invocation malloc()'ed tables to track which functions each label belongs to, store the equivalent info in jump buffers (jmp_buf) themselves. Also, use a less emscripten-looking ABI symbols: saveSetjmp -> __wasm_sjlj_setjmp testSetjmp -> __wasm_sjlj_test getTempRet0 -> (removed) __wasm_longjmp -> __wasm_sjlj_longjmp Enabled with: -mllvm -wasm-enable-sjlj -mllvm -experimental-wasm-enable-alt-sjlj (-experimental-wasm-enable-alt-sjlj is the new option this change introduces.) While I want to use this for WASI, it should work for emscripten as well. An example runtime and a few tests: https://github.com/yamt/garbage/tree/wasm-sjlj-alt2/wasm/longjmp Discussion: https://docs.google.com/document/d/1ZvTPT36K5jjiedF8MCXbEmYjULJjI723aOAks1IdLLg/edit --- clang/lib/Driver/ToolChains/WebAssembly.cpp | 14 ++ .../MCTargetDesc/WebAssemblyMCTargetDesc.cpp | 3 + .../MCTargetDesc/WebAssemblyMCTargetDesc.h | 1 + .../WebAssemblyLowerEmscriptenEHSjLj.cpp | 174 +++++++++++------- .../WebAssembly/WebAssemblyTargetMachine.cpp | 4 + 5 files changed, 131 insertions(+), 65 deletions(-) diff --git a/clang/lib/Driver/ToolChains/WebAssembly.cpp b/clang/lib/Driver/ToolChains/WebAssembly.cpp index b7c6efab83e80..ea5807983f465 100644 --- a/clang/lib/Driver/ToolChains/WebAssembly.cpp +++ b/clang/lib/Driver/ToolChains/WebAssembly.cpp @@ -409,6 +409,20 @@ void WebAssembly::addClangTargetOptions(const ArgList &DriverArgs, // Backend needs '-exception-model=wasm' to use Wasm EH instructions CC1Args.push_back("-exception-model=wasm"); } + + if (Opt.starts_with("-experimental-wasm-enable-alt-sjlj")) { + // '-mllvm -experimental-wasm-enable-alt-sjlj' should be used with + // '-mllvm -wasm-enable-sjlj' + bool HasWasmEnableSjlj = false; + for (const Arg *A : DriverArgs.filtered(options::OPT_mllvm)) { + if (StringRef(A->getValue(0)) == "-wasm-enable-sjlj") + HasWasmEnableSjlj = true; + } + if (!HasWasmEnableSjlj) + getDriver().Diag(diag::err_drv_argument_only_allowed_with) + << "-mllvm -experimental-wasm-enable-alt-sjlj" + << "-mllvm -wasm-enable-sjlj"; + } } } diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp index e8f58a19d25e3..7f15742367be0 100644 --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp @@ -54,6 +54,9 @@ cl::opt // setjmp/longjmp handling using wasm EH instrutions cl::opt WebAssembly::WasmEnableSjLj( "wasm-enable-sjlj", cl::desc("WebAssembly setjmp/longjmp handling")); +cl::opt WebAssembly::WasmEnableAltSjLj( + "experimental-wasm-enable-alt-sjlj", + cl::desc("Use experimental alternate ABI for --wasm-enable-sjlj")); static MCAsmInfo *createMCAsmInfo(const MCRegisterInfo & /*MRI*/, const Triple &TT, diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h index 15aeaaeb8c4a4..d23de9d407d89 100644 --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h @@ -44,6 +44,7 @@ extern cl::opt WasmEnableEmEH; // asm.js-style EH extern cl::opt WasmEnableEmSjLj; // asm.js-style SjLJ extern cl::opt WasmEnableEH; // EH using Wasm EH instructions extern cl::opt WasmEnableSjLj; // SjLj using Wasm EH instructions +extern cl::opt WasmEnableAltSjLj; // Alt ABI for WasmEnableSjLj enum OperandType { /// Basic block label in a branch construct. diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp index 0427fe473f8e1..b616c437b68d8 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp @@ -300,6 +300,7 @@ class WebAssemblyLowerEmscriptenEHSjLj final : public ModulePass { bool EnableEmEH; // Enable Emscripten exception handling bool EnableEmSjLj; // Enable Emscripten setjmp/longjmp handling bool EnableWasmSjLj; // Enable Wasm setjmp/longjmp handling + bool EnableWasmAltSjLj; // Alt ABI for EnableWasmSjLj bool DoSjLj; // Whether we actually perform setjmp/longjmp handling GlobalVariable *ThrewGV = nullptr; // __THREW__ (Emscripten) @@ -368,7 +369,8 @@ class WebAssemblyLowerEmscriptenEHSjLj final : public ModulePass { WebAssemblyLowerEmscriptenEHSjLj() : ModulePass(ID), EnableEmEH(WebAssembly::WasmEnableEmEH), EnableEmSjLj(WebAssembly::WasmEnableEmSjLj), - EnableWasmSjLj(WebAssembly::WasmEnableSjLj) { + EnableWasmSjLj(WebAssembly::WasmEnableSjLj), + EnableWasmAltSjLj(WebAssembly::WasmEnableAltSjLj) { assert(!(EnableEmSjLj && EnableWasmSjLj) && "Two SjLj modes cannot be turned on at the same time"); assert(!(EnableEmEH && EnableWasmSjLj) && @@ -619,6 +621,7 @@ static bool canLongjmp(const Value *Callee) { // 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 == "__wasm_sjlj_setjmp" || CalleeName == "__wasm_sjlj_test" || CalleeName == "getTempRet0" || CalleeName == "setTempRet0") return false; @@ -999,7 +1002,11 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) { // Register __wasm_longjmp function, which calls __builtin_wasm_longjmp. FunctionType *FTy = FunctionType::get( IRB.getVoidTy(), {Int8PtrTy, IRB.getInt32Ty()}, false); - WasmLongjmpF = getEmscriptenFunction(FTy, "__wasm_longjmp", &M); + if (EnableWasmAltSjLj) { + WasmLongjmpF = getEmscriptenFunction(FTy, "__wasm_sjlj_longjmp", &M); + } else { + WasmLongjmpF = getEmscriptenFunction(FTy, "__wasm_longjmp", &M); + } WasmLongjmpF->addFnAttr(Attribute::NoReturn); } @@ -1007,17 +1014,30 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) { Type *Int8PtrTy = IRB.getPtrTy(); Type *Int32PtrTy = IRB.getPtrTy(); Type *Int32Ty = IRB.getInt32Ty(); - // Register saveSetjmp function - FunctionType *SetjmpFTy = SetjmpF->getFunctionType(); - FunctionType *FTy = FunctionType::get( - Int32PtrTy, - {SetjmpFTy->getParamType(0), Int32Ty, Int32PtrTy, Int32Ty}, false); - SaveSetjmpF = getEmscriptenFunction(FTy, "saveSetjmp", &M); // Register testSetjmp function - FTy = FunctionType::get(Int32Ty, - {getAddrIntType(&M), Int32PtrTy, Int32Ty}, false); - TestSetjmpF = getEmscriptenFunction(FTy, "testSetjmp", &M); + if (EnableWasmAltSjLj) { + // Register saveSetjmp function + FunctionType *SetjmpFTy = SetjmpF->getFunctionType(); + FunctionType *FTy = FunctionType::get( + IRB.getVoidTy(), {SetjmpFTy->getParamType(0), Int32Ty, Int32PtrTy}, + false); + SaveSetjmpF = getEmscriptenFunction(FTy, "__wasm_sjlj_setjmp", &M); + + FTy = FunctionType::get(Int32Ty, {Int32PtrTy, Int32PtrTy}, false); + TestSetjmpF = getEmscriptenFunction(FTy, "__wasm_sjlj_test", &M); + } else { + // Register saveSetjmp function + FunctionType *SetjmpFTy = SetjmpF->getFunctionType(); + FunctionType *FTy = FunctionType::get( + Int32PtrTy, + {SetjmpFTy->getParamType(0), Int32Ty, Int32PtrTy, Int32Ty}, false); + SaveSetjmpF = getEmscriptenFunction(FTy, "saveSetjmp", &M); + + FTy = FunctionType::get( + Int32Ty, {getAddrIntType(&M), Int32PtrTy, Int32Ty}, false); + TestSetjmpF = getEmscriptenFunction(FTy, "testSetjmp", &M); + } // wasm.catch() will be lowered down to wasm 'catch' instruction in // instruction selection. @@ -1291,19 +1311,29 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) { Type *IntPtrTy = getAddrIntType(&M); Constant *size = ConstantInt::get(IntPtrTy, 40); IRB.SetInsertPoint(SetjmpTableSize); - auto *SetjmpTable = IRB.CreateMalloc(IntPtrTy, IRB.getInt32Ty(), size, - nullptr, nullptr, "setjmpTable"); - SetjmpTable->setDebugLoc(FirstDL); - // CallInst::CreateMalloc may return a bitcast instruction if the result types - // mismatch. We need to set the debug loc for the original call too. - auto *MallocCall = SetjmpTable->stripPointerCasts(); - if (auto *MallocCallI = dyn_cast(MallocCall)) { - MallocCallI->setDebugLoc(FirstDL); + Instruction *SetjmpTable; + if (EnableWasmAltSjLj) { + // This alloca'ed pointer is used by the runtime to identify function + // inovactions. It's just for pointer comparisons. It will never + // be dereferenced. + SetjmpTable = IRB.CreateAlloca(IRB.getInt32Ty()); + SetjmpTable->setDebugLoc(FirstDL); + SetjmpTableInsts.push_back(SetjmpTable); + } else { + SetjmpTable = IRB.CreateMalloc(IntPtrTy, IRB.getInt32Ty(), size, nullptr, + nullptr, "setjmpTable"); + SetjmpTable->setDebugLoc(FirstDL); + // CallInst::CreateMalloc may return a bitcast instruction if the result + // types mismatch. We need to set the debug loc for the original call too. + auto *MallocCall = SetjmpTable->stripPointerCasts(); + if (auto *MallocCallI = dyn_cast(MallocCall)) { + MallocCallI->setDebugLoc(FirstDL); + } + // setjmpTable[0] = 0; + IRB.CreateStore(IRB.getInt32(0), SetjmpTable); + SetjmpTableInsts.push_back(SetjmpTable); + SetjmpTableSizeInsts.push_back(SetjmpTableSize); } - // setjmpTable[0] = 0; - IRB.CreateStore(IRB.getInt32(0), SetjmpTable); - SetjmpTableInsts.push_back(SetjmpTable); - SetjmpTableSizeInsts.push_back(SetjmpTableSize); // Setjmp transformation SmallVector SetjmpRetPHIs; @@ -1349,14 +1379,20 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) { // Our index in the function is our place in the array + 1 to avoid index // 0, because index 0 means the longjmp is not ours to handle. IRB.SetInsertPoint(CI); - Value *Args[] = {CI->getArgOperand(0), IRB.getInt32(SetjmpRetPHIs.size()), - SetjmpTable, SetjmpTableSize}; - Instruction *NewSetjmpTable = - IRB.CreateCall(SaveSetjmpF, Args, "setjmpTable"); - Instruction *NewSetjmpTableSize = - IRB.CreateCall(GetTempRet0F, std::nullopt, "setjmpTableSize"); - SetjmpTableInsts.push_back(NewSetjmpTable); - SetjmpTableSizeInsts.push_back(NewSetjmpTableSize); + if (EnableWasmAltSjLj) { + Value *Args[] = {CI->getArgOperand(0), IRB.getInt32(SetjmpRetPHIs.size()), + SetjmpTable}; + IRB.CreateCall(SaveSetjmpF, Args); + } else { + Value *Args[] = {CI->getArgOperand(0), IRB.getInt32(SetjmpRetPHIs.size()), + SetjmpTable, SetjmpTableSize}; + Instruction *NewSetjmpTable = + IRB.CreateCall(SaveSetjmpF, Args, "setjmpTable"); + Instruction *NewSetjmpTableSize = + IRB.CreateCall(GetTempRet0F, std::nullopt, "setjmpTableSize"); + SetjmpTableInsts.push_back(NewSetjmpTable); + SetjmpTableSizeInsts.push_back(NewSetjmpTableSize); + } ToErase.push_back(CI); } @@ -1372,38 +1408,40 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) { for (Instruction *I : ToErase) I->eraseFromParent(); - // Free setjmpTable buffer before each return instruction + function-exiting - // call - SmallVector ExitingInsts; - for (BasicBlock &BB : F) { - Instruction *TI = BB.getTerminator(); - if (isa(TI)) - ExitingInsts.push_back(TI); - // Any 'call' instruction with 'noreturn' attribute exits the function at - // this point. If this throws but unwinds to another EH pad within this - // function instead of exiting, this would have been an 'invoke', which - // happens if we use Wasm EH or Wasm SjLJ. - for (auto &I : BB) { - if (auto *CI = dyn_cast(&I)) { - bool IsNoReturn = CI->hasFnAttr(Attribute::NoReturn); - if (Function *CalleeF = CI->getCalledFunction()) - IsNoReturn |= CalleeF->hasFnAttribute(Attribute::NoReturn); - if (IsNoReturn) - ExitingInsts.push_back(&I); + if (!EnableWasmAltSjLj) { + // Free setjmpTable buffer before each return instruction + function-exiting + // call + SmallVector ExitingInsts; + for (BasicBlock &BB : F) { + Instruction *TI = BB.getTerminator(); + if (isa(TI)) + ExitingInsts.push_back(TI); + // Any 'call' instruction with 'noreturn' attribute exits the function at + // this point. If this throws but unwinds to another EH pad within this + // function instead of exiting, this would have been an 'invoke', which + // happens if we use Wasm EH or Wasm SjLJ. + for (auto &I : BB) { + if (auto *CI = dyn_cast(&I)) { + bool IsNoReturn = CI->hasFnAttr(Attribute::NoReturn); + if (Function *CalleeF = CI->getCalledFunction()) + IsNoReturn |= CalleeF->hasFnAttribute(Attribute::NoReturn); + if (IsNoReturn) + ExitingInsts.push_back(&I); + } } } - } - for (auto *I : ExitingInsts) { - DebugLoc DL = getOrCreateDebugLoc(I, F.getSubprogram()); - // If this existing instruction is a call within a catchpad, we should add - // it as "funclet" to the operand bundle of 'free' call - SmallVector Bundles; - if (auto *CB = dyn_cast(I)) - if (auto Bundle = CB->getOperandBundle(LLVMContext::OB_funclet)) - Bundles.push_back(OperandBundleDef(*Bundle)); - IRB.SetInsertPoint(I); - auto *Free = IRB.CreateFree(SetjmpTable, Bundles); - Free->setDebugLoc(DL); + for (auto *I : ExitingInsts) { + DebugLoc DL = getOrCreateDebugLoc(I, F.getSubprogram()); + // If this existing instruction is a call within a catchpad, we should add + // it as "funclet" to the operand bundle of 'free' call + SmallVector Bundles; + if (auto *CB = dyn_cast(I)) + if (auto Bundle = CB->getOperandBundle(LLVMContext::OB_funclet)) + Bundles.push_back(OperandBundleDef(*Bundle)); + IRB.SetInsertPoint(I); + auto *Free = IRB.CreateFree(SetjmpTable, Bundles); + Free->setDebugLoc(DL); + } } // Every call to saveSetjmp can change setjmpTable and setjmpTableSize @@ -1738,10 +1776,16 @@ void WebAssemblyLowerEmscriptenEHSjLj::handleLongjmpableCallsForWasmSjLj( BasicBlock *ThenBB = BasicBlock::Create(C, "if.then", &F); BasicBlock *EndBB = BasicBlock::Create(C, "if.end", &F); Value *EnvP = IRB.CreateBitCast(Env, getAddrPtrType(&M), "env.p"); - Value *SetjmpID = IRB.CreateLoad(getAddrIntType(&M), EnvP, "setjmp.id"); - Value *Label = - IRB.CreateCall(TestSetjmpF, {SetjmpID, SetjmpTable, SetjmpTableSize}, - OperandBundleDef("funclet", CatchPad), "label"); + Value *Label; + if (EnableWasmAltSjLj) { + Label = IRB.CreateCall(TestSetjmpF, {EnvP, SetjmpTable}, + OperandBundleDef("funclet", CatchPad), "label"); + } else { + Value *SetjmpID = IRB.CreateLoad(getAddrIntType(&M), EnvP, "setjmp.id"); + Label = + IRB.CreateCall(TestSetjmpF, {SetjmpID, SetjmpTable, SetjmpTableSize}, + OperandBundleDef("funclet", CatchPad), "label"); + } Value *Cmp = IRB.CreateICmpEQ(Label, IRB.getInt32(0)); IRB.CreateCondBr(Cmp, ThenBB, EndBB); diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp index 70685b2e3bb2d..6db019034028b 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp @@ -370,6 +370,7 @@ FunctionPass *WebAssemblyPassConfig::createTargetRegisterAllocator(bool) { return nullptr; // No reg alloc } +using WebAssembly::WasmEnableAltSjLj; using WebAssembly::WasmEnableEH; using WebAssembly::WasmEnableEmEH; using WebAssembly::WasmEnableEmSjLj; @@ -405,6 +406,9 @@ static void basicCheckForEHAndSjLj(TargetMachine *TM) { report_fatal_error( "-exception-model=wasm only allowed with at least one of " "-wasm-enable-eh or -wasm-enable-sjlj"); + if (!WasmEnableSjLj && WasmEnableAltSjLj) + report_fatal_error("-experimental-wasm-enable-alt-sjlj only allowed with " + "-wasm-enable-sjlj"); // You can't enable two modes of EH at the same time if (WasmEnableEmEH && WasmEnableEH) From e03effdf62fdd852f2caaa6bbfa6e0683df3aa45 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Fri, 8 Mar 2024 09:49:52 +0900 Subject: [PATCH 02/28] WebAssemblyLowerEmscriptenEHSjLj.cpp: move a comment to appropriate places --- .../Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp index b616c437b68d8..7c599717c55a0 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp @@ -1015,7 +1015,6 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) { Type *Int32PtrTy = IRB.getPtrTy(); Type *Int32Ty = IRB.getInt32Ty(); - // Register testSetjmp function if (EnableWasmAltSjLj) { // Register saveSetjmp function FunctionType *SetjmpFTy = SetjmpF->getFunctionType(); @@ -1024,6 +1023,7 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) { false); SaveSetjmpF = getEmscriptenFunction(FTy, "__wasm_sjlj_setjmp", &M); + // Register testSetjmp function FTy = FunctionType::get(Int32Ty, {Int32PtrTy, Int32PtrTy}, false); TestSetjmpF = getEmscriptenFunction(FTy, "__wasm_sjlj_test", &M); } else { @@ -1034,6 +1034,7 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) { {SetjmpFTy->getParamType(0), Int32Ty, Int32PtrTy, Int32Ty}, false); SaveSetjmpF = getEmscriptenFunction(FTy, "saveSetjmp", &M); + // Register testSetjmp function FTy = FunctionType::get( Int32Ty, {getAddrIntType(&M), Int32PtrTy, Int32Ty}, false); TestSetjmpF = getEmscriptenFunction(FTy, "testSetjmp", &M); From f8dfddd8ebb814e82b554c7ad0aa3e5085963a37 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Fri, 8 Mar 2024 10:22:45 +0900 Subject: [PATCH 03/28] WebAssemblyLowerEmscriptenEHSjLj.cpp: comment --- .../WebAssemblyLowerEmscriptenEHSjLj.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp index 7c599717c55a0..5ac0e4f24cece 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp @@ -265,6 +265,21 @@ /// __wasm_longjmp(%env, %val) /// catchret to %setjmp.dispatch /// +/// * Wasm setjmp / longjmp handling (with -experimental-wasm-enable-alt-sjlj) +/// +/// The translation is basically same as what we do for +/// "Wasm setjmp / longjmp handling" w/o -experimental-wasm-enable-alt-sjlj. +/// +/// The differences are: +/// +/// - We do not use malloc'ed tables. +/// +/// - On the entry of setjmp-calling functions, we initialize a pointer +/// to identify the function invocation using alloc(). +/// +/// - We use simpler ABI functions with different names. +/// (prefixed with "__wasm_sjlj_") +/// ///===----------------------------------------------------------------------===// #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" From df806bcaadf5ac368d0d5f6960425c6586052b18 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Mon, 11 Mar 2024 11:14:49 +0900 Subject: [PATCH 04/28] clang/lib/Driver/ToolChains/WebAssembly.cpp: fix whitespace in a comment --- clang/lib/Driver/ToolChains/WebAssembly.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/Driver/ToolChains/WebAssembly.cpp b/clang/lib/Driver/ToolChains/WebAssembly.cpp index ea5807983f465..8d49eaf901b33 100644 --- a/clang/lib/Driver/ToolChains/WebAssembly.cpp +++ b/clang/lib/Driver/ToolChains/WebAssembly.cpp @@ -411,7 +411,7 @@ void WebAssembly::addClangTargetOptions(const ArgList &DriverArgs, } if (Opt.starts_with("-experimental-wasm-enable-alt-sjlj")) { - // '-mllvm -experimental-wasm-enable-alt-sjlj' should be used with + // '-mllvm -experimental-wasm-enable-alt-sjlj' should be used with // '-mllvm -wasm-enable-sjlj' bool HasWasmEnableSjlj = false; for (const Arg *A : DriverArgs.filtered(options::OPT_mllvm)) { From 32e02277526f868b64c45fec956072ba75ac4a62 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Mon, 11 Mar 2024 11:15:35 +0900 Subject: [PATCH 05/28] WebAssemblyMCTargetDesc.cpp: fix a typo in a description --- .../Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp index 7f15742367be0..4371318f4443c 100644 --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp @@ -56,7 +56,7 @@ cl::opt WebAssembly::WasmEnableSjLj( "wasm-enable-sjlj", cl::desc("WebAssembly setjmp/longjmp handling")); cl::opt WebAssembly::WasmEnableAltSjLj( "experimental-wasm-enable-alt-sjlj", - cl::desc("Use experimental alternate ABI for --wasm-enable-sjlj")); + cl::desc("Use experimental alternate ABI for -wasm-enable-sjlj")); static MCAsmInfo *createMCAsmInfo(const MCRegisterInfo & /*MRI*/, const Triple &TT, From a77188f8398506a73df424d74911ea52dee690c6 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Mon, 11 Mar 2024 19:05:34 +0900 Subject: [PATCH 06/28] move some SSAUpdate stuff in !EnableWasmAltSjLj block --- .../WebAssemblyLowerEmscriptenEHSjLj.cpp | 60 +++++++++---------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp index 5ac0e4f24cece..8adb898f53ac8 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp @@ -1458,37 +1458,37 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) { auto *Free = IRB.CreateFree(SetjmpTable, Bundles); Free->setDebugLoc(DL); } - } - // Every call to saveSetjmp can change setjmpTable and setjmpTableSize - // (when buffer reallocation occurs) - // entry: - // setjmpTableSize = 4; - // setjmpTable = (int *) malloc(40); - // setjmpTable[0] = 0; - // ... - // somebb: - // setjmpTable = saveSetjmp(env, label, setjmpTable, setjmpTableSize); - // setjmpTableSize = getTempRet0(); - // So we need to make sure the SSA for these variables is valid so that every - // saveSetjmp and testSetjmp calls have the correct arguments. - SSAUpdater SetjmpTableSSA; - SSAUpdater SetjmpTableSizeSSA; - SetjmpTableSSA.Initialize(PointerType::get(C, 0), "setjmpTable"); - SetjmpTableSizeSSA.Initialize(Type::getInt32Ty(C), "setjmpTableSize"); - for (Instruction *I : SetjmpTableInsts) - SetjmpTableSSA.AddAvailableValue(I->getParent(), I); - for (Instruction *I : SetjmpTableSizeInsts) - SetjmpTableSizeSSA.AddAvailableValue(I->getParent(), I); - - for (auto &U : make_early_inc_range(SetjmpTable->uses())) - if (auto *I = dyn_cast(U.getUser())) - if (I->getParent() != Entry) - SetjmpTableSSA.RewriteUse(U); - for (auto &U : make_early_inc_range(SetjmpTableSize->uses())) - if (auto *I = dyn_cast(U.getUser())) - if (I->getParent() != Entry) - SetjmpTableSizeSSA.RewriteUse(U); + // Every call to saveSetjmp can change setjmpTable and setjmpTableSize + // (when buffer reallocation occurs) + // entry: + // setjmpTableSize = 4; + // setjmpTable = (int *) malloc(40); + // setjmpTable[0] = 0; + // ... + // somebb: + // setjmpTable = saveSetjmp(env, label, setjmpTable, setjmpTableSize); + // setjmpTableSize = getTempRet0(); + // So we need to make sure the SSA for these variables is valid so that + // every saveSetjmp and testSetjmp calls have the correct arguments. + SSAUpdater SetjmpTableSSA; + SSAUpdater SetjmpTableSizeSSA; + SetjmpTableSSA.Initialize(PointerType::get(C, 0), "setjmpTable"); + SetjmpTableSizeSSA.Initialize(Type::getInt32Ty(C), "setjmpTableSize"); + for (Instruction *I : SetjmpTableInsts) + SetjmpTableSSA.AddAvailableValue(I->getParent(), I); + for (Instruction *I : SetjmpTableSizeInsts) + SetjmpTableSizeSSA.AddAvailableValue(I->getParent(), I); + + for (auto &U : make_early_inc_range(SetjmpTable->uses())) + if (auto *I = dyn_cast(U.getUser())) + if (I->getParent() != Entry) + SetjmpTableSSA.RewriteUse(U); + for (auto &U : make_early_inc_range(SetjmpTableSize->uses())) + if (auto *I = dyn_cast(U.getUser())) + if (I->getParent() != Entry) + SetjmpTableSizeSSA.RewriteUse(U); + } // Finally, our modifications to the cfg can break dominance of SSA variables. // For example, in this code, From 6ce14ec4b1ae0cfc02b03ff9186748afd390166c Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Mon, 11 Mar 2024 13:18:12 +0900 Subject: [PATCH 07/28] WebAssemblyLowerEmscriptenEHSjLj: move some code to !EnableWasmAltSjLj block --- .../WebAssemblyLowerEmscriptenEHSjLj.cpp | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp index 8adb898f53ac8..66497da849ff3 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp @@ -1311,24 +1311,14 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) { // Setjmp preparation - // This instruction effectively means %setjmpTableSize = 4. - // 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 - // SSAUpdater.AddAvailableValue(...) later. BasicBlock *Entry = &F.getEntryBlock(); DebugLoc FirstDL = getOrCreateDebugLoc(&*Entry->begin(), F.getSubprogram()); SplitBlock(Entry, &*Entry->getFirstInsertionPt()); - BinaryOperator *SetjmpTableSize = BinaryOperator::Create( - Instruction::Add, IRB.getInt32(4), IRB.getInt32(0), "setjmpTableSize", - Entry->getTerminator()->getIterator()); - SetjmpTableSize->setDebugLoc(FirstDL); - // setjmpTable = (int *) malloc(40); - Type *IntPtrTy = getAddrIntType(&M); - Constant *size = ConstantInt::get(IntPtrTy, 40); - IRB.SetInsertPoint(SetjmpTableSize); + BinaryOperator *SetjmpTableSize; Instruction *SetjmpTable; if (EnableWasmAltSjLj) { + IRB.SetInsertPoint(Entry->getTerminator()->getIterator()); // This alloca'ed pointer is used by the runtime to identify function // inovactions. It's just for pointer comparisons. It will never // be dereferenced. @@ -1336,6 +1326,18 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) { SetjmpTable->setDebugLoc(FirstDL); SetjmpTableInsts.push_back(SetjmpTable); } else { + // This instruction effectively means %setjmpTableSize = 4. + // 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 + // SSAUpdater.AddAvailableValue(...) later. + SetjmpTableSize = BinaryOperator::Create(Instruction::Add, IRB.getInt32(4), + IRB.getInt32(0), "setjmpTableSize", + Entry->getTerminator()->getIterator()); + SetjmpTableSize->setDebugLoc(FirstDL); + IRB.SetInsertPoint(SetjmpTableSize); + // setjmpTable = (int *) malloc(40); + Type *IntPtrTy = getAddrIntType(&M); + Constant *size = ConstantInt::get(IntPtrTy, 40); SetjmpTable = IRB.CreateMalloc(IntPtrTy, IRB.getInt32Ty(), size, nullptr, nullptr, "setjmpTable"); SetjmpTable->setDebugLoc(FirstDL); From 15f8fda18861747a708635e84c7ae958e88a6b97 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Wed, 13 Mar 2024 13:25:01 +0900 Subject: [PATCH 08/28] remove -mllvm -experimental-wasm-enable-alt-sjlj --- clang/lib/Driver/ToolChains/WebAssembly.cpp | 14 -------------- .../MCTargetDesc/WebAssemblyMCTargetDesc.cpp | 3 --- .../MCTargetDesc/WebAssemblyMCTargetDesc.h | 1 - .../WebAssembly/WebAssemblyTargetMachine.cpp | 4 ---- 4 files changed, 22 deletions(-) diff --git a/clang/lib/Driver/ToolChains/WebAssembly.cpp b/clang/lib/Driver/ToolChains/WebAssembly.cpp index 8d49eaf901b33..b7c6efab83e80 100644 --- a/clang/lib/Driver/ToolChains/WebAssembly.cpp +++ b/clang/lib/Driver/ToolChains/WebAssembly.cpp @@ -409,20 +409,6 @@ void WebAssembly::addClangTargetOptions(const ArgList &DriverArgs, // Backend needs '-exception-model=wasm' to use Wasm EH instructions CC1Args.push_back("-exception-model=wasm"); } - - if (Opt.starts_with("-experimental-wasm-enable-alt-sjlj")) { - // '-mllvm -experimental-wasm-enable-alt-sjlj' should be used with - // '-mllvm -wasm-enable-sjlj' - bool HasWasmEnableSjlj = false; - for (const Arg *A : DriverArgs.filtered(options::OPT_mllvm)) { - if (StringRef(A->getValue(0)) == "-wasm-enable-sjlj") - HasWasmEnableSjlj = true; - } - if (!HasWasmEnableSjlj) - getDriver().Diag(diag::err_drv_argument_only_allowed_with) - << "-mllvm -experimental-wasm-enable-alt-sjlj" - << "-mllvm -wasm-enable-sjlj"; - } } } diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp index 4371318f4443c..e8f58a19d25e3 100644 --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp @@ -54,9 +54,6 @@ cl::opt // setjmp/longjmp handling using wasm EH instrutions cl::opt WebAssembly::WasmEnableSjLj( "wasm-enable-sjlj", cl::desc("WebAssembly setjmp/longjmp handling")); -cl::opt WebAssembly::WasmEnableAltSjLj( - "experimental-wasm-enable-alt-sjlj", - cl::desc("Use experimental alternate ABI for -wasm-enable-sjlj")); static MCAsmInfo *createMCAsmInfo(const MCRegisterInfo & /*MRI*/, const Triple &TT, diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h index d23de9d407d89..15aeaaeb8c4a4 100644 --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h @@ -44,7 +44,6 @@ extern cl::opt WasmEnableEmEH; // asm.js-style EH extern cl::opt WasmEnableEmSjLj; // asm.js-style SjLJ extern cl::opt WasmEnableEH; // EH using Wasm EH instructions extern cl::opt WasmEnableSjLj; // SjLj using Wasm EH instructions -extern cl::opt WasmEnableAltSjLj; // Alt ABI for WasmEnableSjLj enum OperandType { /// Basic block label in a branch construct. diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp index 6db019034028b..70685b2e3bb2d 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp @@ -370,7 +370,6 @@ FunctionPass *WebAssemblyPassConfig::createTargetRegisterAllocator(bool) { return nullptr; // No reg alloc } -using WebAssembly::WasmEnableAltSjLj; using WebAssembly::WasmEnableEH; using WebAssembly::WasmEnableEmEH; using WebAssembly::WasmEnableEmSjLj; @@ -406,9 +405,6 @@ static void basicCheckForEHAndSjLj(TargetMachine *TM) { report_fatal_error( "-exception-model=wasm only allowed with at least one of " "-wasm-enable-eh or -wasm-enable-sjlj"); - if (!WasmEnableSjLj && WasmEnableAltSjLj) - report_fatal_error("-experimental-wasm-enable-alt-sjlj only allowed with " - "-wasm-enable-sjlj"); // You can't enable two modes of EH at the same time if (WasmEnableEmEH && WasmEnableEH) From 2b1f35f60d9a283d7896a1d3f6cec5fd8dab5452 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Wed, 13 Mar 2024 13:40:33 +0900 Subject: [PATCH 09/28] remove -mllvm -experimental-wasm-enable-alt-sjlj --- .../WebAssemblyLowerEmscriptenEHSjLj.cpp | 26 ++++++------------- 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp index 66497da849ff3..9781e3a4be6f9 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp @@ -315,7 +315,6 @@ class WebAssemblyLowerEmscriptenEHSjLj final : public ModulePass { bool EnableEmEH; // Enable Emscripten exception handling bool EnableEmSjLj; // Enable Emscripten setjmp/longjmp handling bool EnableWasmSjLj; // Enable Wasm setjmp/longjmp handling - bool EnableWasmAltSjLj; // Alt ABI for EnableWasmSjLj bool DoSjLj; // Whether we actually perform setjmp/longjmp handling GlobalVariable *ThrewGV = nullptr; // __THREW__ (Emscripten) @@ -384,8 +383,7 @@ class WebAssemblyLowerEmscriptenEHSjLj final : public ModulePass { WebAssemblyLowerEmscriptenEHSjLj() : ModulePass(ID), EnableEmEH(WebAssembly::WasmEnableEmEH), EnableEmSjLj(WebAssembly::WasmEnableEmSjLj), - EnableWasmSjLj(WebAssembly::WasmEnableSjLj), - EnableWasmAltSjLj(WebAssembly::WasmEnableAltSjLj) { + EnableWasmSjLj(WebAssembly::WasmEnableSjLj) { assert(!(EnableEmSjLj && EnableWasmSjLj) && "Two SjLj modes cannot be turned on at the same time"); assert(!(EnableEmEH && EnableWasmSjLj) && @@ -1017,7 +1015,7 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) { // Register __wasm_longjmp function, which calls __builtin_wasm_longjmp. FunctionType *FTy = FunctionType::get( IRB.getVoidTy(), {Int8PtrTy, IRB.getInt32Ty()}, false); - if (EnableWasmAltSjLj) { + if (EnableWasmSjLj) { WasmLongjmpF = getEmscriptenFunction(FTy, "__wasm_sjlj_longjmp", &M); } else { WasmLongjmpF = getEmscriptenFunction(FTy, "__wasm_longjmp", &M); @@ -1030,7 +1028,7 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) { Type *Int32PtrTy = IRB.getPtrTy(); Type *Int32Ty = IRB.getInt32Ty(); - if (EnableWasmAltSjLj) { + if (EnableWasmSjLj) { // Register saveSetjmp function FunctionType *SetjmpFTy = SetjmpF->getFunctionType(); FunctionType *FTy = FunctionType::get( @@ -1317,7 +1315,7 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) { BinaryOperator *SetjmpTableSize; Instruction *SetjmpTable; - if (EnableWasmAltSjLj) { + if (EnableWasmSjLj) { IRB.SetInsertPoint(Entry->getTerminator()->getIterator()); // This alloca'ed pointer is used by the runtime to identify function // inovactions. It's just for pointer comparisons. It will never @@ -1397,7 +1395,7 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) { // Our index in the function is our place in the array + 1 to avoid index // 0, because index 0 means the longjmp is not ours to handle. IRB.SetInsertPoint(CI); - if (EnableWasmAltSjLj) { + if (EnableWasmSjLj) { Value *Args[] = {CI->getArgOperand(0), IRB.getInt32(SetjmpRetPHIs.size()), SetjmpTable}; IRB.CreateCall(SaveSetjmpF, Args); @@ -1426,7 +1424,7 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) { for (Instruction *I : ToErase) I->eraseFromParent(); - if (!EnableWasmAltSjLj) { + if (!EnableWasmSjLj) { // Free setjmpTable buffer before each return instruction + function-exiting // call SmallVector ExitingInsts; @@ -1794,16 +1792,8 @@ void WebAssemblyLowerEmscriptenEHSjLj::handleLongjmpableCallsForWasmSjLj( BasicBlock *ThenBB = BasicBlock::Create(C, "if.then", &F); BasicBlock *EndBB = BasicBlock::Create(C, "if.end", &F); Value *EnvP = IRB.CreateBitCast(Env, getAddrPtrType(&M), "env.p"); - Value *Label; - if (EnableWasmAltSjLj) { - Label = IRB.CreateCall(TestSetjmpF, {EnvP, SetjmpTable}, - OperandBundleDef("funclet", CatchPad), "label"); - } else { - Value *SetjmpID = IRB.CreateLoad(getAddrIntType(&M), EnvP, "setjmp.id"); - Label = - IRB.CreateCall(TestSetjmpF, {SetjmpID, SetjmpTable, SetjmpTableSize}, - OperandBundleDef("funclet", CatchPad), "label"); - } + Value *Label = IRB.CreateCall(TestSetjmpF, {EnvP, SetjmpTable}, + OperandBundleDef("funclet", CatchPad), "label"); Value *Cmp = IRB.CreateICmpEQ(Label, IRB.getInt32(0)); IRB.CreateCondBr(Cmp, ThenBB, EndBB); From 13a142a2140a88523de85ad26a897cf183199b41 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Wed, 13 Mar 2024 13:49:38 +0900 Subject: [PATCH 10/28] handleLongjmpableCallsForWasmSjLj: simplify a bit --- .../WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp index 9781e3a4be6f9..dcf944da37ae3 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp @@ -355,7 +355,6 @@ class WebAssemblyLowerEmscriptenEHSjLj final : public ModulePass { SmallVectorImpl &SetjmpRetPHIs); void handleLongjmpableCallsForWasmSjLj(Function &F, InstVector &SetjmpTableInsts, - InstVector &SetjmpTableSizeInsts, SmallVectorImpl &SetjmpRetPHIs); Function *getFindMatchingCatch(Module &M, unsigned NumClauses); @@ -1417,8 +1416,7 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) { handleLongjmpableCallsForEmscriptenSjLj( F, SetjmpTableInsts, SetjmpTableSizeInsts, SetjmpRetPHIs); else // EnableWasmSjLj - handleLongjmpableCallsForWasmSjLj(F, SetjmpTableInsts, SetjmpTableSizeInsts, - SetjmpRetPHIs); + handleLongjmpableCallsForWasmSjLj(F, SetjmpTableInsts, SetjmpRetPHIs); // Erase everything we no longer need in this function for (Instruction *I : ToErase) @@ -1712,7 +1710,7 @@ static BasicBlock *getCleanupRetUnwindDest(const CleanupPadInst *CPI) { // BBs. Refer to 4) of "Wasm setjmp/longjmp handling" section in the comments at // top of the file for details. void WebAssemblyLowerEmscriptenEHSjLj::handleLongjmpableCallsForWasmSjLj( - Function &F, InstVector &SetjmpTableInsts, InstVector &SetjmpTableSizeInsts, + Function &F, InstVector &SetjmpTableInsts, SmallVectorImpl &SetjmpRetPHIs) { Module &M = *F.getParent(); LLVMContext &C = F.getContext(); @@ -1739,7 +1737,6 @@ void WebAssemblyLowerEmscriptenEHSjLj::handleLongjmpableCallsForWasmSjLj( // Arbitrarily use the ones defined in the beginning of the function. // SSAUpdater will later update them to the correct values. Instruction *SetjmpTable = *SetjmpTableInsts.begin(); - Instruction *SetjmpTableSize = *SetjmpTableSizeInsts.begin(); // Add setjmp.dispatch BB right after the entry block. Because we have // initialized setjmpTable/setjmpTableSize in the entry block and split the From 3151476f53ac6b13152652557f07537411ea2f9c Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Wed, 13 Mar 2024 13:50:56 +0900 Subject: [PATCH 11/28] rename abi functions --- .../WebAssemblyLowerEmscriptenEHSjLj.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp index dcf944da37ae3..20cc72845885c 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp @@ -278,7 +278,6 @@ /// to identify the function invocation using alloc(). /// /// - We use simpler ABI functions with different names. -/// (prefixed with "__wasm_sjlj_") /// ///===----------------------------------------------------------------------===// @@ -633,7 +632,7 @@ static bool canLongjmp(const Value *Callee) { // 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 == "__wasm_sjlj_setjmp" || CalleeName == "__wasm_sjlj_test" || + CalleeName == "__wasm_setjmp" || CalleeName == "__wasm_setjmp_test" || CalleeName == "getTempRet0" || CalleeName == "setTempRet0") return false; @@ -1014,11 +1013,7 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) { // Register __wasm_longjmp function, which calls __builtin_wasm_longjmp. FunctionType *FTy = FunctionType::get( IRB.getVoidTy(), {Int8PtrTy, IRB.getInt32Ty()}, false); - if (EnableWasmSjLj) { - WasmLongjmpF = getEmscriptenFunction(FTy, "__wasm_sjlj_longjmp", &M); - } else { - WasmLongjmpF = getEmscriptenFunction(FTy, "__wasm_longjmp", &M); - } + WasmLongjmpF = getEmscriptenFunction(FTy, "__wasm_longjmp", &M); WasmLongjmpF->addFnAttr(Attribute::NoReturn); } @@ -1033,11 +1028,11 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) { FunctionType *FTy = FunctionType::get( IRB.getVoidTy(), {SetjmpFTy->getParamType(0), Int32Ty, Int32PtrTy}, false); - SaveSetjmpF = getEmscriptenFunction(FTy, "__wasm_sjlj_setjmp", &M); + SaveSetjmpF = getEmscriptenFunction(FTy, "__wasm_setjmp", &M); // Register testSetjmp function FTy = FunctionType::get(Int32Ty, {Int32PtrTy, Int32PtrTy}, false); - TestSetjmpF = getEmscriptenFunction(FTy, "__wasm_sjlj_test", &M); + TestSetjmpF = getEmscriptenFunction(FTy, "__wasm_setjmp_test", &M); } else { // Register saveSetjmp function FunctionType *SetjmpFTy = SetjmpF->getFunctionType(); From 7deaee4c924a999548760d060674357f289fa020 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Wed, 13 Mar 2024 14:32:06 +0900 Subject: [PATCH 12/28] llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp: comment --- .../WebAssemblyLowerEmscriptenEHSjLj.cpp | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp index 20cc72845885c..00c6d82ac4c3c 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp @@ -201,6 +201,20 @@ /// 2) and 3): The same as 2) and 3) in Emscripten SjLj. /// (setjmpTable/setjmpTableSize initialization + setjmp callsite /// transformation) +/// functionInvocationId +/// +/// 2) In the function entry that calls setjmp, initialize +/// functionInvocationId as follows: +/// +/// functionInvocationId = alloca() +/// +/// 3) Lower +/// setjmp(env) +/// into +/// __wasm_setjmp(env, label, functionInvocationId) +/// +/// 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) Create a catchpad with a wasm.catch() intrinsic, which returns the value /// thrown by __wasm_longjmp function. In Emscripten library, we have this @@ -232,12 +246,12 @@ /// function, we jump to the beginning of the function, which contains a switch /// to each post-setjmp BB. Again, in Emscripten SjLj, this switch is added for /// every longjmpable callsite; in Wasm SjLj we do this only once at the top of -/// the function. (after setjmpTable/setjmpTableSize initialization) +/// the function. (after functionInvocationId initialization) /// /// The below is the pseudocode for what we have described /// /// entry: -/// Initialize setjmpTable and setjmpTableSize +/// Initialize functionInvocationId /// /// setjmp.dispatch: /// switch %label { @@ -260,25 +274,11 @@ /// %longjmp.args = wasm.catch() ;; struct __WasmLongjmpArgs /// %env = load 'env' field from __WasmLongjmpArgs /// %val = load 'val' field from __WasmLongjmpArgs -/// %label = testSetjmp(mem[%env], setjmpTable, setjmpTableSize); +/// %label = __wasm_setjmp_test(%env, functionInvocationId); /// if (%label == 0) /// __wasm_longjmp(%env, %val) /// catchret to %setjmp.dispatch /// -/// * Wasm setjmp / longjmp handling (with -experimental-wasm-enable-alt-sjlj) -/// -/// The translation is basically same as what we do for -/// "Wasm setjmp / longjmp handling" w/o -experimental-wasm-enable-alt-sjlj. -/// -/// The differences are: -/// -/// - We do not use malloc'ed tables. -/// -/// - On the entry of setjmp-calling functions, we initialize a pointer -/// to identify the function invocation using alloc(). -/// -/// - We use simpler ABI functions with different names. -/// ///===----------------------------------------------------------------------===// #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" From df1664972b845eafc4414de330f9e8e9f856f590 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Wed, 13 Mar 2024 14:44:55 +0900 Subject: [PATCH 13/28] handleLongjmpableCallsForWasmSjLj: simplify a bit --- .../WebAssemblyLowerEmscriptenEHSjLj.cpp | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp index 00c6d82ac4c3c..04eb9081199f6 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp @@ -353,7 +353,8 @@ class WebAssemblyLowerEmscriptenEHSjLj final : public ModulePass { InstVector &SetjmpTableSizeInsts, SmallVectorImpl &SetjmpRetPHIs); void - handleLongjmpableCallsForWasmSjLj(Function &F, InstVector &SetjmpTableInsts, + handleLongjmpableCallsForWasmSjLj(Function &F, + Instruction *FunctionInvocationId, SmallVectorImpl &SetjmpRetPHIs); Function *getFindMatchingCatch(Module &M, unsigned NumClauses); @@ -1309,14 +1310,14 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) { BinaryOperator *SetjmpTableSize; Instruction *SetjmpTable; + Instruction *FunctionInvocationId; if (EnableWasmSjLj) { IRB.SetInsertPoint(Entry->getTerminator()->getIterator()); // This alloca'ed pointer is used by the runtime to identify function // inovactions. It's just for pointer comparisons. It will never // be dereferenced. - SetjmpTable = IRB.CreateAlloca(IRB.getInt32Ty()); - SetjmpTable->setDebugLoc(FirstDL); - SetjmpTableInsts.push_back(SetjmpTable); + FunctionInvocationId = IRB.CreateAlloca(IRB.getInt32Ty()); + FunctionInvocationId->setDebugLoc(FirstDL); } else { // This instruction effectively means %setjmpTableSize = 4. // We create this as an instruction intentionally, and we don't want to fold @@ -1411,7 +1412,7 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) { handleLongjmpableCallsForEmscriptenSjLj( F, SetjmpTableInsts, SetjmpTableSizeInsts, SetjmpRetPHIs); else // EnableWasmSjLj - handleLongjmpableCallsForWasmSjLj(F, SetjmpTableInsts, SetjmpRetPHIs); + handleLongjmpableCallsForWasmSjLj(F, FunctionInvocationId, SetjmpRetPHIs); // Erase everything we no longer need in this function for (Instruction *I : ToErase) @@ -1705,7 +1706,7 @@ static BasicBlock *getCleanupRetUnwindDest(const CleanupPadInst *CPI) { // BBs. Refer to 4) of "Wasm setjmp/longjmp handling" section in the comments at // top of the file for details. void WebAssemblyLowerEmscriptenEHSjLj::handleLongjmpableCallsForWasmSjLj( - Function &F, InstVector &SetjmpTableInsts, + Function &F, Instruction *FunctionInvocationId, SmallVectorImpl &SetjmpRetPHIs) { Module &M = *F.getParent(); LLVMContext &C = F.getContext(); @@ -1729,10 +1730,6 @@ void WebAssemblyLowerEmscriptenEHSjLj::handleLongjmpableCallsForWasmSjLj( DebugLoc FirstDL = getOrCreateDebugLoc(&*Entry->begin(), F.getSubprogram()); IRB.SetCurrentDebugLocation(FirstDL); - // Arbitrarily use the ones defined in the beginning of the function. - // SSAUpdater will later update them to the correct values. - Instruction *SetjmpTable = *SetjmpTableInsts.begin(); - // Add setjmp.dispatch BB right after the entry block. Because we have // initialized setjmpTable/setjmpTableSize in the entry block and split the // rest into another BB, here 'OrigEntry' is the function's original entry @@ -1777,14 +1774,14 @@ void WebAssemblyLowerEmscriptenEHSjLj::handleLongjmpableCallsForWasmSjLj( // int val = __wasm_longjmp_args.val; Instruction *Val = IRB.CreateLoad(IRB.getInt32Ty(), ValField, "val"); - // %label = testSetjmp(mem[%env], setjmpTable, setjmpTableSize); + // %label = __wasm_setjmp_test(%env, functionInvocatinoId); // if (%label == 0) // __wasm_longjmp(%env, %val) // catchret to %setjmp.dispatch BasicBlock *ThenBB = BasicBlock::Create(C, "if.then", &F); BasicBlock *EndBB = BasicBlock::Create(C, "if.end", &F); Value *EnvP = IRB.CreateBitCast(Env, getAddrPtrType(&M), "env.p"); - Value *Label = IRB.CreateCall(TestSetjmpF, {EnvP, SetjmpTable}, + Value *Label = IRB.CreateCall(TestSetjmpF, {EnvP, FunctionInvocationId}, OperandBundleDef("funclet", CatchPad), "label"); Value *Cmp = IRB.CreateICmpEQ(Label, IRB.getInt32(0)); IRB.CreateCondBr(Cmp, ThenBB, EndBB); From 7cdd8e0b337bf917c455f57bc5096e874a67bbca Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Wed, 13 Mar 2024 15:06:09 +0900 Subject: [PATCH 14/28] WebAssemblyLowerEmscriptenEHSjLj.cpp: fix a botch in FunctionIvocationId rename --- .../lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp index 04eb9081199f6..448de86b62fe8 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp @@ -1392,7 +1392,7 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) { IRB.SetInsertPoint(CI); if (EnableWasmSjLj) { Value *Args[] = {CI->getArgOperand(0), IRB.getInt32(SetjmpRetPHIs.size()), - SetjmpTable}; + FunctionInvocationId}; IRB.CreateCall(SaveSetjmpF, Args); } else { Value *Args[] = {CI->getArgOperand(0), IRB.getInt32(SetjmpRetPHIs.size()), From 35a109910256647d3c9308d718b5c1998f35228d Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Wed, 13 Mar 2024 15:38:16 +0900 Subject: [PATCH 15/28] give an IR level name to function invocation id --- .../lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp index 448de86b62fe8..045be27c4a836 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp @@ -1316,7 +1316,7 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) { // This alloca'ed pointer is used by the runtime to identify function // inovactions. It's just for pointer comparisons. It will never // be dereferenced. - FunctionInvocationId = IRB.CreateAlloca(IRB.getInt32Ty()); + FunctionInvocationId = IRB.CreateAlloca(IRB.getInt32Ty(), nullptr, "functionInvocationId"); FunctionInvocationId->setDebugLoc(FirstDL); } else { // This instruction effectively means %setjmpTableSize = 4. From 68833b05771c6107d67de68639f12ea807f27c20 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Wed, 13 Mar 2024 15:38:56 +0900 Subject: [PATCH 16/28] llvm/test/CodeGen/WebAssembly/lower-wasm-sjlj.ll: update --- .../CodeGen/WebAssembly/lower-wasm-sjlj.ll | 22 +++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/llvm/test/CodeGen/WebAssembly/lower-wasm-sjlj.ll b/llvm/test/CodeGen/WebAssembly/lower-wasm-sjlj.ll index b8d2230fac9f5..82c04e24b72f1 100644 --- a/llvm/test/CodeGen/WebAssembly/lower-wasm-sjlj.ll +++ b/llvm/test/CodeGen/WebAssembly/lower-wasm-sjlj.ll @@ -25,16 +25,12 @@ entry: unreachable ; CHECK: entry: -; CHECK-NEXT: %setjmpTable = tail call ptr @malloc([[PTR]] 40) -; CHECK-NEXT: store i32 0, ptr %setjmpTable, align 4 -; CHECK-NEXT: %setjmpTableSize = add i32 4, 0 +; CHECK-NEXT: %functionInvocationId = alloca i32, align 4 ; CHECK-NEXT: br label %setjmp.dispatch ; CHECK: setjmp.dispatch: ; CHECK-NEXT: %[[VAL2:.*]] = phi i32 [ %val, %if.end ], [ undef, %entry ] ; CHECK-NEXT: %[[BUF:.*]] = phi ptr [ %[[BUF2:.*]], %if.end ], [ undef, %entry ] -; CHECK-NEXT: %[[SETJMPTABLESIZE2:.*]] = phi i32 [ %[[SETJMPTABLESIZE3:.*]], %if.end ], [ %setjmpTableSize, %entry ] -; CHECK-NEXT: %[[SETJMPTABLE2:.*]] = phi ptr [ %[[SETJMPTABLE3:.*]], %if.end ], [ %setjmpTable, %entry ] ; CHECK-NEXT: %label.phi = phi i32 [ %label, %if.end ], [ -1, %entry ] ; CHECK-NEXT: switch i32 %label.phi, label %entry.split [ ; CHECK-NEXT: i32 1, label %entry.split.split @@ -42,14 +38,11 @@ entry: ; CHECK: entry.split: ; CHECK-NEXT: %buf = alloca [1 x %struct.__jmp_buf_tag], align 16 -; CHECK-NEXT: %[[SETJMPTABLE4:.*]] = call ptr @saveSetjmp(ptr %buf, i32 1, ptr %[[SETJMPTABLE2]], i32 %[[SETJMPTABLESIZE2]]) -; CHECK-NEXT: %[[SETJMPTABLESIZE4:.*]] = call i32 @getTempRet0() +; CHECK-NEXT: call void @__wasm_setjmp(ptr %buf, i32 1, ptr %functionInvocationId) ; CHECK-NEXT: br label %entry.split.split ; CHECK: entry.split.split: ; CHECK-NEXT: %[[BUF2]] = phi ptr [ %[[BUF]], %setjmp.dispatch ], [ %buf, %entry.split ] -; CHECK-NEXT: %[[SETJMPTABLESIZE3]] = phi i32 [ %[[SETJMPTABLESIZE4]], %entry.split ], [ %[[SETJMPTABLESIZE2]], %setjmp.dispatch ] -; CHECK-NEXT: %[[SETJMPTABLE3]] = phi ptr [ %[[SETJMPTABLE4]], %entry.split ], [ %[[SETJMPTABLE2]], %setjmp.dispatch ] ; CHECK-NEXT: %setjmp.ret = phi i32 [ 0, %entry.split ], [ %[[VAL2]], %setjmp.dispatch ] ; CHECK-NEXT: invoke void @__wasm_longjmp(ptr %[[BUF2]], i32 1) ; CHECK-NEXT: to label %.noexc unwind label %catch.dispatch.longjmp @@ -67,13 +60,11 @@ entry: ; CHECK-NEXT: %val_gep = getelementptr { ptr, i32 }, ptr %thrown, i32 0, i32 1 ; CHECK-NEXT: %env = load ptr, ptr %env_gep, align {{.*}} ; CHECK-NEXT: %val = load i32, ptr %val_gep, align 4 -; CHECK-NEXT: %setjmp.id = load [[PTR]], ptr %env, align {{.*}} -; CHECK-NEXT: %label = call i32 @testSetjmp([[PTR]] %setjmp.id, ptr %[[SETJMPTABLE3]], i32 %[[SETJMPTABLESIZE3]]) [ "funclet"(token %1) ] +; CHECK-NEXT: %label = call i32 @__wasm_setjmp_test(ptr %env, ptr %functionInvocationId) [ "funclet"(token %1) ] ; CHECK-NEXT: %2 = icmp eq i32 %label, 0 ; CHECK-NEXT: br i1 %2, label %if.then, label %if.end ; CHECK: if.then: -; CHECK-NEXT: tail call void @free(ptr %[[SETJMPTABLE3]]) [ "funclet"(token %1) ] ; CHECK-NEXT: call void @__wasm_longjmp(ptr %env, i32 %val) [ "funclet"(token %1) ] ; CHECK-NEXT: unreachable @@ -142,10 +133,9 @@ declare ptr @__cxa_begin_catch(ptr) declare void @__cxa_end_catch() declare void @free(ptr) -; JS glue function declarations -; CHECK-DAG: declare i32 @getTempRet0() -; CHECK-DAG: declare ptr @saveSetjmp(ptr, i32, ptr, i32) -; CHECK-DAG: declare i32 @testSetjmp([[PTR]], ptr, i32) +; Runtime glue function declarations +; CHECK-DAG: declare void @__wasm_setjmp(ptr, i32, ptr) +; CHECK-DAG: declare i32 @__wasm_setjmp_test(ptr, ptr) ; CHECK-DAG: declare void @__wasm_longjmp(ptr, i32) attributes #0 = { returns_twice } From ccfd3ed5517f951335781df6de52fb1f832bc4dc Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Wed, 13 Mar 2024 15:45:48 +0900 Subject: [PATCH 17/28] llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj.ll: update --- llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj.ll | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj.ll b/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj.ll index 25471eb50081b..bd8db83a0e57e 100644 --- a/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj.ll +++ b/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj.ll @@ -108,8 +108,8 @@ catch: ; preds = %catch.start call void @__cxa_end_catch() [ "funclet"(token %2) ] catchret from %2 to label %catchret.dest ; CHECK: catch: ; preds = %catch.start -; CHECK-NEXT: %exn = load ptr, ptr %exn.slot15, align 4 -; CHECK-NEXT: %5 = call ptr @__cxa_begin_catch(ptr %exn) #7 [ "funclet"(token %2) ] +; CHECK-NEXT: %exn = load ptr, ptr %exn.slot6, align 4 +; CHECK-NEXT: %5 = call ptr @__cxa_begin_catch(ptr %exn) #6 [ "funclet"(token %2) ] ; CHECK-NEXT: invoke void @__cxa_end_catch() [ "funclet"(token %2) ] ; CHECK-NEXT: to label %.noexc unwind label %catch.dispatch.longjmp @@ -265,7 +265,7 @@ ehcleanup: ; preds = %entry ; (cleanuppad), whose parent is 'none', so we should unwind directly to ; %catch.dispatch.longjmp. %call2 = call noundef ptr @_ZN4TempD2Ev(ptr noundef %t) #2 [ "funclet"(token %0) ] -; CHECK: %call13 = invoke {{.*}} ptr @_ZN4TempD2Ev(ptr +; CHECK: %call11 = invoke {{.*}} ptr @_ZN4TempD2Ev(ptr ; CHECK-NEXT: to label {{.*}} unwind label %catch.dispatch.longjmp cleanupret from %0 unwind to caller } From a28f85beb916883861180b3b4363c5ddb42be415 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Wed, 13 Mar 2024 15:49:18 +0900 Subject: [PATCH 18/28] WebAssemblyLowerEmscriptenEHSjLj.cpp: comment --- .../Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp index 045be27c4a836..92536dd8003c4 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp @@ -198,11 +198,6 @@ /// /// If there are calls to setjmp() /// -/// 2) and 3): The same as 2) and 3) in Emscripten SjLj. -/// (setjmpTable/setjmpTableSize initialization + setjmp callsite -/// transformation) -/// functionInvocationId -/// /// 2) In the function entry that calls setjmp, initialize /// functionInvocationId as follows: /// From 0c1acd84e6fed816ed958287b7a44a8848084b61 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Wed, 13 Mar 2024 15:50:32 +0900 Subject: [PATCH 19/28] format --- .../Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp index 92536dd8003c4..920f3e7a57003 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp @@ -1311,7 +1311,8 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) { // This alloca'ed pointer is used by the runtime to identify function // inovactions. It's just for pointer comparisons. It will never // be dereferenced. - FunctionInvocationId = IRB.CreateAlloca(IRB.getInt32Ty(), nullptr, "functionInvocationId"); + FunctionInvocationId = + IRB.CreateAlloca(IRB.getInt32Ty(), nullptr, "functionInvocationId"); FunctionInvocationId->setDebugLoc(FirstDL); } else { // This instruction effectively means %setjmpTableSize = 4. From ae2b1f229cd92fecc166bb2618a80cfc6926f2cf Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Wed, 13 Mar 2024 15:59:37 +0900 Subject: [PATCH 20/28] WebAssemblyLowerEmscriptenEHSjLj.cpp: comment --- .../WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp index 920f3e7a57003..3d9eccb421989 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp @@ -318,8 +318,9 @@ class WebAssemblyLowerEmscriptenEHSjLj final : public ModulePass { Function *ResumeF = nullptr; // __resumeException() (Emscripten) Function *EHTypeIDF = nullptr; // llvm.eh.typeid.for() (intrinsic) Function *EmLongjmpF = nullptr; // emscripten_longjmp() (Emscripten) - Function *SaveSetjmpF = nullptr; // saveSetjmp() (Emscripten) - Function *TestSetjmpF = nullptr; // testSetjmp() (Emscripten) + Function *SaveSetjmpF = nullptr; // saveSetjmp()/__wasm_setjmp() (Emscripten) + Function *TestSetjmpF = + nullptr; // testSetjmp()/__wasm_setjmp_test() (Emscripten) Function *WasmLongjmpF = nullptr; // __wasm_longjmp() (Emscripten) Function *CatchF = nullptr; // wasm.catch() (intrinsic) @@ -1019,14 +1020,14 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) { Type *Int32Ty = IRB.getInt32Ty(); if (EnableWasmSjLj) { - // Register saveSetjmp function + // Register __wasm_setjmp function FunctionType *SetjmpFTy = SetjmpF->getFunctionType(); FunctionType *FTy = FunctionType::get( IRB.getVoidTy(), {SetjmpFTy->getParamType(0), Int32Ty, Int32PtrTy}, false); SaveSetjmpF = getEmscriptenFunction(FTy, "__wasm_setjmp", &M); - // Register testSetjmp function + // Register __wasm_setjmp_test function FTy = FunctionType::get(Int32Ty, {Int32PtrTy, Int32PtrTy}, false); TestSetjmpF = getEmscriptenFunction(FTy, "__wasm_setjmp_test", &M); } else { From 68cceec25c2792a85216b69564c9ca7a1f718e76 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Fri, 15 Mar 2024 11:57:20 +0900 Subject: [PATCH 21/28] WebAssemblyLowerEmscriptenEHSjLj.cpp: adapt emscripten sjlj as well --- .../WebAssemblyLowerEmscriptenEHSjLj.cpp | 259 ++++-------------- 1 file changed, 51 insertions(+), 208 deletions(-) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp index 3d9eccb421989..b7c62eab7b145 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp @@ -129,27 +129,18 @@ /// /// If there are calls to setjmp() /// -/// 2) In the function entry that calls setjmp, initialize setjmpTable and -/// sejmpTableSize as follows: -/// setjmpTableSize = 4; -/// setjmpTable = (int *) malloc(40); -/// setjmpTable[0] = 0; -/// setjmpTable and setjmpTableSize are used to call saveSetjmp() function in -/// Emscripten compiler-rt. +/// 2) In the function entry that calls setjmp, initialize +/// functionInvocationId as follows: +/// +/// functionInvocationId = alloca() /// /// 3) Lower /// setjmp(env) /// into -/// setjmpTable = saveSetjmp(env, label, setjmpTable, setjmpTableSize); -/// setjmpTableSize = getTempRet0(); -/// 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 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 'env'. 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. +/// __wasm_setjmp(env, label, functionInvocationId) +/// +/// 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 /// __THREW__ = 0; @@ -196,20 +187,9 @@ /// into /// __wasm_longjmp(env, val) /// -/// If there are calls to setjmp() -/// -/// 2) In the function entry that calls setjmp, initialize -/// functionInvocationId as follows: -/// -/// functionInvocationId = alloca() -/// -/// 3) Lower -/// setjmp(env) -/// into -/// __wasm_setjmp(env, label, functionInvocationId) -/// -/// 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. +/// 2) and 3): The same as 2) and 3) in Emscripten SjLj. +/// (setjmpTable/setjmpTableSize initialization + setjmp callsite +/// transformation) /// /// 4) Create a catchpad with a wasm.catch() intrinsic, which returns the value /// thrown by __wasm_longjmp function. In Emscripten library, we have this @@ -318,9 +298,8 @@ class WebAssemblyLowerEmscriptenEHSjLj final : public ModulePass { Function *ResumeF = nullptr; // __resumeException() (Emscripten) Function *EHTypeIDF = nullptr; // llvm.eh.typeid.for() (intrinsic) Function *EmLongjmpF = nullptr; // emscripten_longjmp() (Emscripten) - Function *SaveSetjmpF = nullptr; // saveSetjmp()/__wasm_setjmp() (Emscripten) - Function *TestSetjmpF = - nullptr; // testSetjmp()/__wasm_setjmp_test() (Emscripten) + Function *SaveSetjmpF = nullptr; // __wasm_setjmp() (Emscripten) + Function *TestSetjmpF = nullptr; // __wasm_setjmp_test() (Emscripten) Function *WasmLongjmpF = nullptr; // __wasm_longjmp() (Emscripten) Function *CatchF = nullptr; // wasm.catch() (intrinsic) @@ -345,8 +324,7 @@ class WebAssemblyLowerEmscriptenEHSjLj final : public ModulePass { bool runEHOnFunction(Function &F); bool runSjLjOnFunction(Function &F); void handleLongjmpableCallsForEmscriptenSjLj( - Function &F, InstVector &SetjmpTableInsts, - InstVector &SetjmpTableSizeInsts, + Function &F, Instruction *FunctionInvocationId, SmallVectorImpl &SetjmpRetPHIs); void handleLongjmpableCallsForWasmSjLj(Function &F, @@ -356,7 +334,7 @@ class WebAssemblyLowerEmscriptenEHSjLj final : public ModulePass { Value *wrapInvoke(CallBase *CI); void wrapTestSetjmp(BasicBlock *BB, DebugLoc DL, Value *Threw, - Value *SetjmpTable, Value *SetjmpTableSize, Value *&Label, + Value *FunctionInvocationId, Value *&Label, Value *&LongjmpResult, BasicBlock *&CallEmLongjmpBB, PHINode *&CallEmLongjmpBBThrewPHI, PHINode *&CallEmLongjmpBBThrewValuePHI, @@ -628,7 +606,6 @@ static bool canLongjmp(const Value *Callee) { // 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 == "__wasm_setjmp" || CalleeName == "__wasm_setjmp_test" || CalleeName == "getTempRet0" || CalleeName == "setTempRet0") return false; @@ -702,7 +679,7 @@ static bool isEmAsmCall(const Value *Callee) { // The code this generates is equivalent to the following JavaScript code: // %__threwValue.val = __threwValue; // if (%__THREW__.val != 0 & %__threwValue.val != 0) { -// %label = testSetjmp(mem[%__THREW__.val], setjmpTable, setjmpTableSize); +// %label = __wasm_setjmp_test(%__THREW__.val, functionInvocationId); // if (%label == 0) // emscripten_longjmp(%__THREW__.val, %__threwValue.val); // setTempRet0(%__threwValue.val); @@ -714,10 +691,10 @@ static bool isEmAsmCall(const Value *Callee) { // As output parameters. returns %label, %longjmp_result, and the BB the last // instruction (%longjmp_result = ...) is in. void WebAssemblyLowerEmscriptenEHSjLj::wrapTestSetjmp( - BasicBlock *BB, DebugLoc DL, Value *Threw, Value *SetjmpTable, - Value *SetjmpTableSize, Value *&Label, Value *&LongjmpResult, - BasicBlock *&CallEmLongjmpBB, PHINode *&CallEmLongjmpBBThrewPHI, - PHINode *&CallEmLongjmpBBThrewValuePHI, BasicBlock *&EndBB) { + BasicBlock *BB, DebugLoc DL, Value *Threw, Value *FunctionInvocationId, + Value *&Label, Value *&LongjmpResult, BasicBlock *&CallEmLongjmpBB, + PHINode *&CallEmLongjmpBBThrewPHI, PHINode *&CallEmLongjmpBBThrewValuePHI, + BasicBlock *&EndBB) { Function *F = BB->getParent(); Module *M = F->getParent(); LLVMContext &C = M->getContext(); @@ -754,7 +731,7 @@ void WebAssemblyLowerEmscriptenEHSjLj::wrapTestSetjmp( CallEmLongjmpBBThrewValuePHI->addIncoming(ThrewValue, ThenBB1); } - // %label = testSetjmp(mem[%__THREW__.val], setjmpTable, setjmpTableSize); + // %label = __wasm_setjmp_test(%__THREW__.val, functionInvocationId); // if (%label == 0) IRB.SetInsertPoint(ThenBB1); BasicBlock *EndBB2 = BasicBlock::Create(C, "if.end2", F); @@ -762,8 +739,8 @@ void WebAssemblyLowerEmscriptenEHSjLj::wrapTestSetjmp( 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 *ThenLabel = + IRB.CreateCall(TestSetjmpF, {LoadedThrew, FunctionInvocationId}, "label"); Value *Cmp2 = IRB.CreateICmpEQ(ThenLabel, IRB.getInt32(0)); IRB.CreateCondBr(Cmp2, CallEmLongjmpBB, EndBB2); @@ -1019,30 +996,16 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) { Type *Int32PtrTy = IRB.getPtrTy(); Type *Int32Ty = IRB.getInt32Ty(); - if (EnableWasmSjLj) { - // Register __wasm_setjmp function - FunctionType *SetjmpFTy = SetjmpF->getFunctionType(); - FunctionType *FTy = FunctionType::get( - IRB.getVoidTy(), {SetjmpFTy->getParamType(0), Int32Ty, Int32PtrTy}, - false); - SaveSetjmpF = getEmscriptenFunction(FTy, "__wasm_setjmp", &M); - - // Register __wasm_setjmp_test function - FTy = FunctionType::get(Int32Ty, {Int32PtrTy, Int32PtrTy}, false); - TestSetjmpF = getEmscriptenFunction(FTy, "__wasm_setjmp_test", &M); - } else { - // Register saveSetjmp function - FunctionType *SetjmpFTy = SetjmpF->getFunctionType(); - FunctionType *FTy = FunctionType::get( - Int32PtrTy, - {SetjmpFTy->getParamType(0), Int32Ty, Int32PtrTy, Int32Ty}, false); - SaveSetjmpF = getEmscriptenFunction(FTy, "saveSetjmp", &M); - - // Register testSetjmp function - FTy = FunctionType::get( - Int32Ty, {getAddrIntType(&M), Int32PtrTy, Int32Ty}, false); - TestSetjmpF = getEmscriptenFunction(FTy, "testSetjmp", &M); - } + // Register __wasm_setjmp function + FunctionType *SetjmpFTy = SetjmpF->getFunctionType(); + FunctionType *FTy = FunctionType::get( + IRB.getVoidTy(), {SetjmpFTy->getParamType(0), Int32Ty, Int32PtrTy}, + false); + SaveSetjmpF = getEmscriptenFunction(FTy, "__wasm_setjmp", &M); + + // Register __wasm_setjmp_test function + FTy = FunctionType::get(Int32Ty, {Int32PtrTy, Int32PtrTy}, false); + TestSetjmpF = getEmscriptenFunction(FTy, "__wasm_setjmp_test", &M); // wasm.catch() will be lowered down to wasm 'catch' instruction in // instruction selection. @@ -1293,10 +1256,6 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) { LLVMContext &C = F.getContext(); IRBuilder<> IRB(C); SmallVector ToErase; - // Vector of %setjmpTable values - SmallVector SetjmpTableInsts; - // Vector of %setjmpTableSize values - SmallVector SetjmpTableSizeInsts; // Setjmp preparation @@ -1304,44 +1263,14 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) { DebugLoc FirstDL = getOrCreateDebugLoc(&*Entry->begin(), F.getSubprogram()); SplitBlock(Entry, &*Entry->getFirstInsertionPt()); - BinaryOperator *SetjmpTableSize; - Instruction *SetjmpTable; Instruction *FunctionInvocationId; - if (EnableWasmSjLj) { - IRB.SetInsertPoint(Entry->getTerminator()->getIterator()); - // This alloca'ed pointer is used by the runtime to identify function - // inovactions. It's just for pointer comparisons. It will never - // be dereferenced. - FunctionInvocationId = - IRB.CreateAlloca(IRB.getInt32Ty(), nullptr, "functionInvocationId"); - FunctionInvocationId->setDebugLoc(FirstDL); - } else { - // This instruction effectively means %setjmpTableSize = 4. - // 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 - // SSAUpdater.AddAvailableValue(...) later. - SetjmpTableSize = BinaryOperator::Create(Instruction::Add, IRB.getInt32(4), - IRB.getInt32(0), "setjmpTableSize", - Entry->getTerminator()->getIterator()); - SetjmpTableSize->setDebugLoc(FirstDL); - IRB.SetInsertPoint(SetjmpTableSize); - // setjmpTable = (int *) malloc(40); - Type *IntPtrTy = getAddrIntType(&M); - Constant *size = ConstantInt::get(IntPtrTy, 40); - SetjmpTable = IRB.CreateMalloc(IntPtrTy, IRB.getInt32Ty(), size, nullptr, - nullptr, "setjmpTable"); - SetjmpTable->setDebugLoc(FirstDL); - // CallInst::CreateMalloc may return a bitcast instruction if the result - // types mismatch. We need to set the debug loc for the original call too. - auto *MallocCall = SetjmpTable->stripPointerCasts(); - if (auto *MallocCallI = dyn_cast(MallocCall)) { - MallocCallI->setDebugLoc(FirstDL); - } - // setjmpTable[0] = 0; - IRB.CreateStore(IRB.getInt32(0), SetjmpTable); - SetjmpTableInsts.push_back(SetjmpTable); - SetjmpTableSizeInsts.push_back(SetjmpTableSize); - } + IRB.SetInsertPoint(Entry->getTerminator()->getIterator()); + // This alloca'ed pointer is used by the runtime to identify function + // inovactions. It's just for pointer comparisons. It will never + // be dereferenced. + FunctionInvocationId = + IRB.CreateAlloca(IRB.getInt32Ty(), nullptr, "functionInvocationId"); + FunctionInvocationId->setDebugLoc(FirstDL); // Setjmp transformation SmallVector SetjmpRetPHIs; @@ -1387,27 +1316,16 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) { // Our index in the function is our place in the array + 1 to avoid index // 0, because index 0 means the longjmp is not ours to handle. IRB.SetInsertPoint(CI); - if (EnableWasmSjLj) { - Value *Args[] = {CI->getArgOperand(0), IRB.getInt32(SetjmpRetPHIs.size()), - FunctionInvocationId}; - IRB.CreateCall(SaveSetjmpF, Args); - } else { - Value *Args[] = {CI->getArgOperand(0), IRB.getInt32(SetjmpRetPHIs.size()), - SetjmpTable, SetjmpTableSize}; - Instruction *NewSetjmpTable = - IRB.CreateCall(SaveSetjmpF, Args, "setjmpTable"); - Instruction *NewSetjmpTableSize = - IRB.CreateCall(GetTempRet0F, std::nullopt, "setjmpTableSize"); - SetjmpTableInsts.push_back(NewSetjmpTable); - SetjmpTableSizeInsts.push_back(NewSetjmpTableSize); - } + Value *Args[] = {CI->getArgOperand(0), IRB.getInt32(SetjmpRetPHIs.size()), + FunctionInvocationId}; + IRB.CreateCall(SaveSetjmpF, Args); ToErase.push_back(CI); } // Handle longjmpable calls. if (EnableEmSjLj) - handleLongjmpableCallsForEmscriptenSjLj( - F, SetjmpTableInsts, SetjmpTableSizeInsts, SetjmpRetPHIs); + handleLongjmpableCallsForEmscriptenSjLj(F, FunctionInvocationId, + SetjmpRetPHIs); else // EnableWasmSjLj handleLongjmpableCallsForWasmSjLj(F, FunctionInvocationId, SetjmpRetPHIs); @@ -1415,72 +1333,6 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) { for (Instruction *I : ToErase) I->eraseFromParent(); - if (!EnableWasmSjLj) { - // Free setjmpTable buffer before each return instruction + function-exiting - // call - SmallVector ExitingInsts; - for (BasicBlock &BB : F) { - Instruction *TI = BB.getTerminator(); - if (isa(TI)) - ExitingInsts.push_back(TI); - // Any 'call' instruction with 'noreturn' attribute exits the function at - // this point. If this throws but unwinds to another EH pad within this - // function instead of exiting, this would have been an 'invoke', which - // happens if we use Wasm EH or Wasm SjLJ. - for (auto &I : BB) { - if (auto *CI = dyn_cast(&I)) { - bool IsNoReturn = CI->hasFnAttr(Attribute::NoReturn); - if (Function *CalleeF = CI->getCalledFunction()) - IsNoReturn |= CalleeF->hasFnAttribute(Attribute::NoReturn); - if (IsNoReturn) - ExitingInsts.push_back(&I); - } - } - } - for (auto *I : ExitingInsts) { - DebugLoc DL = getOrCreateDebugLoc(I, F.getSubprogram()); - // If this existing instruction is a call within a catchpad, we should add - // it as "funclet" to the operand bundle of 'free' call - SmallVector Bundles; - if (auto *CB = dyn_cast(I)) - if (auto Bundle = CB->getOperandBundle(LLVMContext::OB_funclet)) - Bundles.push_back(OperandBundleDef(*Bundle)); - IRB.SetInsertPoint(I); - auto *Free = IRB.CreateFree(SetjmpTable, Bundles); - Free->setDebugLoc(DL); - } - - // Every call to saveSetjmp can change setjmpTable and setjmpTableSize - // (when buffer reallocation occurs) - // entry: - // setjmpTableSize = 4; - // setjmpTable = (int *) malloc(40); - // setjmpTable[0] = 0; - // ... - // somebb: - // setjmpTable = saveSetjmp(env, label, setjmpTable, setjmpTableSize); - // setjmpTableSize = getTempRet0(); - // So we need to make sure the SSA for these variables is valid so that - // every saveSetjmp and testSetjmp calls have the correct arguments. - SSAUpdater SetjmpTableSSA; - SSAUpdater SetjmpTableSizeSSA; - SetjmpTableSSA.Initialize(PointerType::get(C, 0), "setjmpTable"); - SetjmpTableSizeSSA.Initialize(Type::getInt32Ty(C), "setjmpTableSize"); - for (Instruction *I : SetjmpTableInsts) - SetjmpTableSSA.AddAvailableValue(I->getParent(), I); - for (Instruction *I : SetjmpTableSizeInsts) - SetjmpTableSizeSSA.AddAvailableValue(I->getParent(), I); - - for (auto &U : make_early_inc_range(SetjmpTable->uses())) - if (auto *I = dyn_cast(U.getUser())) - if (I->getParent() != Entry) - SetjmpTableSSA.RewriteUse(U); - for (auto &U : make_early_inc_range(SetjmpTableSize->uses())) - if (auto *I = dyn_cast(U.getUser())) - if (I->getParent() != Entry) - SetjmpTableSizeSSA.RewriteUse(U); - } - // Finally, our modifications to the cfg can break dominance of SSA variables. // For example, in this code, // if (x()) { .. setjmp() .. } @@ -1499,21 +1351,13 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) { // setjmp. Refer to 4) of "Emscripten setjmp/longjmp handling" section in the // comments at top of the file for details. void WebAssemblyLowerEmscriptenEHSjLj::handleLongjmpableCallsForEmscriptenSjLj( - Function &F, InstVector &SetjmpTableInsts, InstVector &SetjmpTableSizeInsts, + Function &F, Instruction *FunctionInvocationId, SmallVectorImpl &SetjmpRetPHIs) { Module &M = *F.getParent(); LLVMContext &C = F.getContext(); IRBuilder<> IRB(C); SmallVector ToErase; - // We need to pass setjmpTable and setjmpTableSize to testSetjmp function. - // These values are defined in the beginning of the function and also in each - // setjmp callsite, but we don't know which values we should use at this - // point. So here we arbitraily use the ones defined in the beginning of the - // function, and SSAUpdater will later update them to the correct values. - Instruction *SetjmpTable = *SetjmpTableInsts.begin(); - Instruction *SetjmpTableSize = *SetjmpTableSizeInsts.begin(); - // call.em.longjmp BB that will be shared within the function. BasicBlock *CallEmLongjmpBB = nullptr; // PHI node for the loaded value of __THREW__ global variable in @@ -1661,10 +1505,9 @@ void WebAssemblyLowerEmscriptenEHSjLj::handleLongjmpableCallsForEmscriptenSjLj( Value *Label = nullptr; Value *LongjmpResult = nullptr; BasicBlock *EndBB = nullptr; - wrapTestSetjmp(BB, CI->getDebugLoc(), Threw, SetjmpTable, SetjmpTableSize, - Label, LongjmpResult, CallEmLongjmpBB, - CallEmLongjmpBBThrewPHI, CallEmLongjmpBBThrewValuePHI, - EndBB); + wrapTestSetjmp(BB, CI->getDebugLoc(), Threw, FunctionInvocationId, Label, + LongjmpResult, CallEmLongjmpBB, CallEmLongjmpBBThrewPHI, + CallEmLongjmpBBThrewValuePHI, EndBB); assert(Label && LongjmpResult && EndBB); // Create switch instruction @@ -1728,12 +1571,12 @@ void WebAssemblyLowerEmscriptenEHSjLj::handleLongjmpableCallsForWasmSjLj( IRB.SetCurrentDebugLocation(FirstDL); // Add setjmp.dispatch BB right after the entry block. Because we have - // initialized setjmpTable/setjmpTableSize in the entry block and split the + // initialized functionInvocationId in the entry block and split the // rest into another BB, here 'OrigEntry' is the function's original entry // block before the transformation. // // entry: - // setjmpTable / setjmpTableSize initialization + // functionInvocationId initialization // setjmp.dispatch: // switch will be inserted here later // entry.split: (OrigEntry) From e6b37dc24d3802e778ca03873de436fb9f12fbf5 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Fri, 15 Mar 2024 12:15:14 +0900 Subject: [PATCH 22/28] fix __wasm_setjmp_test invocation --- .../Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp index b7c62eab7b145..8f72e6d7898e2 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp @@ -737,10 +737,8 @@ void WebAssemblyLowerEmscriptenEHSjLj::wrapTestSetjmp( BasicBlock *EndBB2 = BasicBlock::Create(C, "if.end2", F); 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, FunctionInvocationId}, "label"); + IRB.CreateCall(TestSetjmpF, {ThrewPtr, FunctionInvocationId}, "label"); Value *Cmp2 = IRB.CreateICmpEQ(ThenLabel, IRB.getInt32(0)); IRB.CreateCondBr(Cmp2, CallEmLongjmpBB, EndBB2); From a89b0d82d1d09fdff8fdd322ea3bdf52d0259190 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Fri, 15 Mar 2024 12:16:43 +0900 Subject: [PATCH 23/28] rename variables SaveSetjmpF -> WasmSetjmpF TestSetjmpF -> WasmSetjmpTestF --- .../WebAssemblyLowerEmscriptenEHSjLj.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp index 8f72e6d7898e2..c483644eb1e1d 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp @@ -298,8 +298,8 @@ class WebAssemblyLowerEmscriptenEHSjLj final : public ModulePass { Function *ResumeF = nullptr; // __resumeException() (Emscripten) Function *EHTypeIDF = nullptr; // llvm.eh.typeid.for() (intrinsic) Function *EmLongjmpF = nullptr; // emscripten_longjmp() (Emscripten) - Function *SaveSetjmpF = nullptr; // __wasm_setjmp() (Emscripten) - Function *TestSetjmpF = nullptr; // __wasm_setjmp_test() (Emscripten) + Function *WasmSetjmpF = nullptr; // __wasm_setjmp() (Emscripten) + Function *WasmSetjmpTestF = nullptr; // __wasm_setjmp_test() (Emscripten) Function *WasmLongjmpF = nullptr; // __wasm_longjmp() (Emscripten) Function *CatchF = nullptr; // wasm.catch() (intrinsic) @@ -737,8 +737,8 @@ void WebAssemblyLowerEmscriptenEHSjLj::wrapTestSetjmp( BasicBlock *EndBB2 = BasicBlock::Create(C, "if.end2", F); Value *ThrewPtr = IRB.CreateIntToPtr(Threw, getAddrPtrType(M), Threw->getName() + ".p"); - Value *ThenLabel = - IRB.CreateCall(TestSetjmpF, {ThrewPtr, FunctionInvocationId}, "label"); + Value *ThenLabel = IRB.CreateCall(WasmSetjmpTestF, + {ThrewPtr, FunctionInvocationId}, "label"); Value *Cmp2 = IRB.CreateICmpEQ(ThenLabel, IRB.getInt32(0)); IRB.CreateCondBr(Cmp2, CallEmLongjmpBB, EndBB2); @@ -999,11 +999,11 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) { FunctionType *FTy = FunctionType::get( IRB.getVoidTy(), {SetjmpFTy->getParamType(0), Int32Ty, Int32PtrTy}, false); - SaveSetjmpF = getEmscriptenFunction(FTy, "__wasm_setjmp", &M); + WasmSetjmpF = getEmscriptenFunction(FTy, "__wasm_setjmp", &M); // Register __wasm_setjmp_test function FTy = FunctionType::get(Int32Ty, {Int32PtrTy, Int32PtrTy}, false); - TestSetjmpF = getEmscriptenFunction(FTy, "__wasm_setjmp_test", &M); + WasmSetjmpTestF = getEmscriptenFunction(FTy, "__wasm_setjmp_test", &M); // wasm.catch() will be lowered down to wasm 'catch' instruction in // instruction selection. @@ -1049,7 +1049,7 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) { if (V && V->use_empty()) V->eraseFromParent(); for (auto *V : {GetTempRet0F, SetTempRet0F, ResumeF, EHTypeIDF, EmLongjmpF, - SaveSetjmpF, TestSetjmpF, WasmLongjmpF, CatchF}) + WasmSetjmpF, WasmSetjmpTestF, WasmLongjmpF, CatchF}) if (V && V->use_empty()) V->eraseFromParent(); @@ -1316,7 +1316,7 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) { IRB.SetInsertPoint(CI); Value *Args[] = {CI->getArgOperand(0), IRB.getInt32(SetjmpRetPHIs.size()), FunctionInvocationId}; - IRB.CreateCall(SaveSetjmpF, Args); + IRB.CreateCall(WasmSetjmpF, Args); ToErase.push_back(CI); } @@ -1619,7 +1619,7 @@ void WebAssemblyLowerEmscriptenEHSjLj::handleLongjmpableCallsForWasmSjLj( BasicBlock *ThenBB = BasicBlock::Create(C, "if.then", &F); BasicBlock *EndBB = BasicBlock::Create(C, "if.end", &F); Value *EnvP = IRB.CreateBitCast(Env, getAddrPtrType(&M), "env.p"); - Value *Label = IRB.CreateCall(TestSetjmpF, {EnvP, FunctionInvocationId}, + Value *Label = IRB.CreateCall(WasmSetjmpTestF, {EnvP, FunctionInvocationId}, OperandBundleDef("funclet", CatchPad), "label"); Value *Cmp = IRB.CreateICmpEQ(Label, IRB.getInt32(0)); IRB.CreateCondBr(Cmp, ThenBB, EndBB); From d14eecafb1a3716df60c815e3a89f80c1c9001c5 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Fri, 15 Mar 2024 12:20:40 +0900 Subject: [PATCH 24/28] comment --- .../lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp index c483644eb1e1d..18a94900e910e 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp @@ -187,6 +187,8 @@ /// into /// __wasm_longjmp(env, val) /// +/// If there are calls to setjmp() +/// /// 2) and 3): The same as 2) and 3) in Emscripten SjLj. /// (setjmpTable/setjmpTableSize initialization + setjmp callsite /// transformation) From 6f54497f57a52b7743f7e3cbde2413d7dceab99a Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Fri, 15 Mar 2024 12:27:41 +0900 Subject: [PATCH 25/28] comment --- .../Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp index 18a94900e910e..591585fd565bc 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp @@ -132,7 +132,10 @@ /// 2) In the function entry that calls setjmp, initialize /// functionInvocationId as follows: /// -/// functionInvocationId = alloca() +/// functionInvocationId = alloca(4) +/// +/// Note: the alloca size is not important as this pointer is +/// merely used for pointer comparisions. /// /// 3) Lower /// setjmp(env) From db00c9b047f092f6cd3c135d1f04a9b26be6f43a Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Fri, 15 Mar 2024 14:59:30 +0900 Subject: [PATCH 26/28] update llvm/test/CodeGen/WebAssembly/lower-em-*.ll --- .../WebAssembly/lower-em-ehsjlj-options.ll | 4 +- .../CodeGen/WebAssembly/lower-em-ehsjlj.ll | 5 +-- .../WebAssembly/lower-em-sjlj-alias.ll | 2 +- .../WebAssembly/lower-em-sjlj-debuginfo.ll | 35 ++---------------- .../test/CodeGen/WebAssembly/lower-em-sjlj.ll | 37 ++++++------------- 5 files changed, 19 insertions(+), 64 deletions(-) diff --git a/llvm/test/CodeGen/WebAssembly/lower-em-ehsjlj-options.ll b/llvm/test/CodeGen/WebAssembly/lower-em-ehsjlj-options.ll index aa4d87756c87d..4a63c812d6ae9 100644 --- a/llvm/test/CodeGen/WebAssembly/lower-em-ehsjlj-options.ll +++ b/llvm/test/CodeGen/WebAssembly/lower-em-ehsjlj-options.ll @@ -59,12 +59,12 @@ entry: %call = call i32 @setjmp(ptr %buf) #0 call void @longjmp(ptr %buf, i32 1) #1 unreachable -; SJLJ: call saveSetjmp +; SJLJ: call __wasm_setjmp ; SJLJ: i32.const emscripten_longjmp ; SJLJ-NOT: i32.const emscripten_longjmp_jmpbuf ; SJLJ: call invoke_vii ; SJLJ-NOT: call "__invoke_void_ptr_i32" -; SJLJ: call testSetjmp +; SJLJ: call __wasm_setjmp_test ; NONE: call setjmp ; NONE: call longjmp diff --git a/llvm/test/CodeGen/WebAssembly/lower-em-ehsjlj.ll b/llvm/test/CodeGen/WebAssembly/lower-em-ehsjlj.ll index 7cf05cc922cd3..32942cd92e684 100644 --- a/llvm/test/CodeGen/WebAssembly/lower-em-ehsjlj.ll +++ b/llvm/test/CodeGen/WebAssembly/lower-em-ehsjlj.ll @@ -49,7 +49,7 @@ try.cont: ; preds = %lpad, %entry ; longjmp checking part ; CHECK: if.then1: -; CHECK: call i32 @testSetjmp +; CHECK: call i32 @__wasm_setjmp_test } ; @foo can either throw an exception or longjmp. Because this function doesn't @@ -117,7 +117,6 @@ if.end: ; preds = %entry ; CHECK: rethrow.exn: ; CHECK-NEXT: %exn = call ptr @__cxa_find_matching_catch_2() -; CHECK-NEXT: call void @free(ptr %setjmpTable{{.*}}) ; CHECK-NEXT: call void @__resumeException(ptr %exn) ; CHECK-NEXT: unreachable @@ -147,7 +146,6 @@ throw: ; preds = %if.end, %entry unreachable ; CHECK: throw: -; CHECK-NEXT: call void @free(ptr %setjmpTable{{.*}}) ; CHECK-NEXT: call void @__cxa_throw(ptr null, ptr null, ptr null) ; CHECK-NEXT: unreachable } @@ -208,7 +206,6 @@ return: ; preds = %entry, %if.end ; CHECK: rethrow.exn: ; CHECK-NEXT: %exn = call ptr @__cxa_find_matching_catch_2() -; CHECK-NEXT: tail call void @free(ptr %setjmpTable{{.*}}) ; CHECK-NEXT: call void @__resumeException(ptr %exn) ; CHECK-NEXT: unreachable } diff --git a/llvm/test/CodeGen/WebAssembly/lower-em-sjlj-alias.ll b/llvm/test/CodeGen/WebAssembly/lower-em-sjlj-alias.ll index 1a85a63e44ad4..79ae16191d6b3 100644 --- a/llvm/test/CodeGen/WebAssembly/lower-em-sjlj-alias.ll +++ b/llvm/test/CodeGen/WebAssembly/lower-em-sjlj-alias.ll @@ -12,7 +12,7 @@ target triple = "wasm32-unknown-emscripten" ; CHECK-LABEL: @malloc_test define void @malloc_test() { entry: - ; CHECK: call ptr @malloc + ; CHECK: alloca i32 %retval = alloca i32, align 4 %jmp = alloca [1 x %struct.__jmp_buf_tag], align 16 store i32 0, ptr %retval, align 4 diff --git a/llvm/test/CodeGen/WebAssembly/lower-em-sjlj-debuginfo.ll b/llvm/test/CodeGen/WebAssembly/lower-em-sjlj-debuginfo.ll index 4f694151c7613..fec9836a1607c 100644 --- a/llvm/test/CodeGen/WebAssembly/lower-em-sjlj-debuginfo.ll +++ b/llvm/test/CodeGen/WebAssembly/lower-em-sjlj-debuginfo.ll @@ -16,25 +16,22 @@ entry: call void @foo(), !dbg !7 ret void, !dbg !8 ; CHECK: entry: - ; CHECK-NEXT: call ptr @malloc(i32 40), !dbg ![[DL0:.*]] + ; CHECK-NEXT: %functionInvocationId = alloca i32, align 4, !dbg ![[DL0:.*]] ; CHECK: entry.split: ; CHECK: alloca {{.*}}, !dbg ![[DL0]] - ; CHECK: call ptr @saveSetjmp{{.*}}, !dbg ![[DL1:.*]] - ; CHECK-NEXT: call i32 @getTempRet0{{.*}}, !dbg ![[DL1]] + ; CHECK: call void @__wasm_setjmp{{.*}}, !dbg ![[DL1:.*]] ; CHECK-NEXT: br {{.*}}, !dbg ![[DL2:.*]] ; CHECK: entry.split.split: ; CHECK: call {{.*}} void @__invoke_void{{.*}}, !dbg ![[DL2]] ; CHECK: entry.split.split.split: - ; CHECK-NEXT: call void @free{{.*}}, !dbg ![[DL3:.*]] ; CHECK: if.then1: - ; CHECK: call i32 @testSetjmp{{.*}}, !dbg ![[DL2]] + ; CHECK: call i32 @__wasm_setjmp_test{{.*}}, !dbg ![[DL2]] ; CHECK: if.end: - ; CHECK: call i32 @getTempRet0{{.*}}, !dbg ![[DL2]] ; CHECK: call.em.longjmp: ; CHECK: call void @emscripten_longjmp{{.*}}, !dbg ![[DL2]] @@ -43,26 +40,6 @@ entry: ; CHECK: call void @setTempRet0{{.*}}, !dbg ![[DL2]] } -; No instruction has debug info but the current function (setjmp_debug_info2) -; and the called function (malloc / free) have DISubprograms, so the newly -; generated calls should have debug info attached. We don't have an instruction -; to take debug info from, so we create dummy debug info. -define void @setjmp_debug_info1() !dbg !9 { -; CHECK-LABEL: @setjmp_debug_info1 -entry: - %buf = alloca [1 x %struct.__jmp_buf_tag], align 16 - %arraydecay = getelementptr inbounds [1 x %struct.__jmp_buf_tag], ptr %buf, i32 0, i32 0 - %call = call i32 @setjmp(ptr %arraydecay) #0 - call void @foo() - ret void - ; CHECK: call ptr @malloc(i32 40), !dbg ![[DL_DUMMY:.*]] - ; CHECK: call void @free{{.*}}, !dbg ![[DL_DUMMY]] -} - -; Note that these functions have DISubprograms. -declare !dbg !10 ptr @malloc(i32) -declare !dbg !11 void @free(ptr) - declare void @foo() ; Function Attrs: returns_twice declare i32 @setjmp(ptr) #0 @@ -79,9 +56,3 @@ declare i32 @setjmp(ptr) #0 !6 = !DILocation(line:4, scope: !3) !7 = !DILocation(line:5, scope: !3) !8 = !DILocation(line:6, scope: !3) -!9 = distinct !DISubprogram(name: "setjmp_debug_info1", unit:!2, file: !1, line: 50) -!10 = !DISubprogram(name: "malloc", file: !1, line: 10, isDefinition: false) -!11 = !DISubprogram(name: "free", file: !1, line: 20, isDefinition: false) - -; Dummy debug info generated -; CHECK: ![[DL_DUMMY]] = !DILocation(line: 50, column: 1, scope: !9) diff --git a/llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll b/llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll index 7115b01ed1618..27ec95a2c462a 100644 --- a/llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll +++ b/llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll @@ -22,15 +22,12 @@ entry: call void @longjmp(ptr %buf, i32 1) #1 unreachable ; CHECK: entry: -; CHECK-NEXT: %[[MALLOCCALL:.*]] = tail call ptr @malloc([[PTR]] 40) -; CHECK-NEXT: store i32 0, ptr %[[MALLOCCALL]] -; CHECK-NEXT: %[[SETJMP_TABLE_SIZE:.*]] = add i32 4, 0 +; CHECK-NEXT: %functionInvocationId = alloca i32, align 4 ; CHECK-NEXT: br label %entry.split ; CHECK: entry.split ; CHECK-NEXT: %[[BUF:.*]] = alloca [1 x %struct.__jmp_buf_tag] -; CHECK-NEXT: %[[SETJMP_TABLE1:.*]] = call ptr @saveSetjmp(ptr %[[BUF]], i32 1, ptr %[[MALLOCCALL]], i32 %[[SETJMP_TABLE_SIZE]]) -; CHECK-NEXT: %[[SETJMP_TABLE_SIZE1:.*]] = call i32 @getTempRet0() +; CHECK-NEXT: call void @__wasm_setjmp(ptr %[[BUF]], i32 1, ptr %functionInvocationId) ; CHECK-NEXT: br label %entry.split.split ; CHECK: entry.split.split: @@ -51,8 +48,7 @@ entry: ; CHECK: if.then1: ; CHECK-NEXT: %[[__THREW__VAL_P:.*]] = inttoptr [[PTR]] %[[__THREW__VAL]] to ptr -; CHECK-NEXT: %[[__THREW__VAL_P_LOADED:.*]] = load [[PTR]], ptr %[[__THREW__VAL_P]] -; CHECK-NEXT: %[[LABEL:.*]] = call i32 @testSetjmp([[PTR]] %[[__THREW__VAL_P_LOADED]], ptr %[[SETJMP_TABLE1]], i32 %[[SETJMP_TABLE_SIZE1]]) +; CHECK-NEXT: %[[LABEL:.*]] = call i32 @__wasm_setjmp_test(ptr %[[__THREW__VAL_P]], ptr %functionInvocationId) ; CHECK-NEXT: %[[CMP:.*]] = icmp eq i32 %[[LABEL]], 0 ; CHECK-NEXT: br i1 %[[CMP]], label %call.em.longjmp, label %if.end2 @@ -69,7 +65,6 @@ entry: ; CHECK: call.em.longjmp: ; CHECK-NEXT: %threw.phi = phi [[PTR]] [ %[[__THREW__VAL]], %if.then1 ] ; CHECK-NEXT: %threwvalue.phi = phi i32 [ %[[THREWVALUE_VAL]], %if.then1 ] -; CHECK-NEXT: tail call void @free(ptr %[[SETJMP_TABLE1]]) ; CHECK-NEXT: call void @emscripten_longjmp([[PTR]] %threw.phi, i32 %threwvalue.phi) ; CHECK-NEXT: unreachable @@ -87,13 +82,12 @@ entry: call void @foo() ret void ; CHECK: entry: -; CHECK: %[[SETJMP_TABLE:.*]] = call ptr @saveSetjmp( +; CHECK: call void @__wasm_setjmp( ; CHECK: entry.split.split: ; CHECK: @__invoke_void(ptr @foo) ; CHECK: entry.split.split.split: -; CHECK-NEXT: tail call void @free(ptr %[[SETJMP_TABLE]]) ; CHECK-NEXT: ret void } @@ -110,9 +104,8 @@ entry: call void @foo() ret void ; CHECK: call.em.longjmp: -; CHECK-NEXT: %threw.phi = phi [[PTR]] [ %__THREW__.val, %if.then1 ], [ %__THREW__.val4, %if.then15 ] -; CHECK-NEXT: %threwvalue.phi = phi i32 [ %__threwValue.val, %if.then1 ], [ %__threwValue.val8, %if.then15 ] -; CHECK-NEXT: tail call void @free(ptr %[[SETJMP_TABLE1]]) +; CHECK-NEXT: %threw.phi = phi [[PTR]] [ %__THREW__.val, %if.then1 ], [ %__THREW__.val2, %if.then13 ] +; CHECK-NEXT: %threwvalue.phi = phi i32 [ %__threwValue.val, %if.then1 ], [ %__threwValue.val6, %if.then13 ] ; CHECK-NEXT: call void @emscripten_longjmp([[PTR]] %threw.phi, i32 %threwvalue.phi) ; CHECK-NEXT: unreachable } @@ -145,7 +138,6 @@ entry: %cmp = icmp sgt i32 %n, 5 br i1 %cmp, label %if.then, label %if.end ; CHECK: entry: -; CHECK: %[[SETJMP_TABLE_SIZE0:.*]] = add i32 4, 0 if.then: ; preds = %entry %0 = load i32, ptr @global_var, align 4 @@ -154,13 +146,10 @@ if.then: ; preds = %entry br label %if.end ; CHECK: if.then: ; CHECK: %[[VAR0:.*]] = load i32, ptr @global_var, align 4 -; CHECK: %[[SETJMP_TABLE1:.*]] = call ptr @saveSetjmp( -; CHECK-NEXT: %[[SETJMP_TABLE_SIZE1:.*]] = call i32 @getTempRet0() +; CHECK: call void @__wasm_setjmp( ; CHECK: if.then.split: -; CHECK: %[[VAR1:.*]] = phi i32 [ %[[VAR2:.*]], %if.end3 ], [ %[[VAR0]], %if.then ] -; CHECK: %[[SETJMP_TABLE_SIZE2:.*]] = phi i32 [ %[[SETJMP_TABLE_SIZE1]], %if.then ], [ %[[SETJMP_TABLE_SIZE3:.*]], %if.end3 ] -; CHECK: %[[SETJMP_TABLE2:.*]] = phi ptr [ %[[SETJMP_TABLE1]], %if.then ], [ %[[SETJMP_TABLE3:.*]], %if.end3 ] +; CHECK: %[[VAR1:.*]] = phi i32 [ %[[VAR2:.*]], %if.end1 ], [ %[[VAR0]], %if.then ] ; CHECK: store i32 %[[VAR1]], ptr @global_var, align 4 if.end: ; preds = %if.then, %entry @@ -168,8 +157,6 @@ if.end: ; preds = %if.then, %entry unreachable ; CHECK: if.end: ; CHECK: %[[VAR2]] = phi i32 [ %[[VAR1]], %if.then.split ], [ undef, %entry.split ] -; CHECK: %[[SETJMP_TABLE_SIZE3]] = phi i32 [ %[[SETJMP_TABLE_SIZE2]], %if.then.split ], [ %[[SETJMP_TABLE_SIZE0]], %entry.split ] -; CHECK: %[[SETJMP_TABLE3]] = phi ptr [ %[[SETJMP_TABLE2]], %if.then.split ], [ %setjmpTable, %entry.split ] } ; Test a case when a function only calls other functions that are neither setjmp nor longjmp @@ -296,8 +283,8 @@ declare void @free(ptr) ; JS glue functions and invoke wrappers declaration ; CHECK-DAG: declare i32 @getTempRet0() ; CHECK-DAG: declare void @setTempRet0(i32) -; CHECK-DAG: declare ptr @saveSetjmp(ptr, i32, ptr, i32) -; CHECK-DAG: declare i32 @testSetjmp([[PTR]], ptr, i32) +; CHECK-DAG: declare void @__wasm_setjmp(ptr, i32, ptr) +; CHECK-DAG: declare i32 @__wasm_setjmp_test(ptr, ptr) ; CHECK-DAG: declare void @emscripten_longjmp([[PTR]], i32) ; CHECK-DAG: declare void @__invoke_void(ptr) @@ -308,8 +295,8 @@ attributes #3 = { allocsize(0) } ; 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"="__invoke_void" } -; 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"="__wasm_setjmp" } +; CHECK-DAG: attributes #{{[0-9]+}} = { "wasm-import-module"="env" "wasm-import-name"="__wasm_setjmp_test" } ; CHECK-DAG: attributes #{{[0-9]+}} = { noreturn "wasm-import-module"="env" "wasm-import-name"="emscripten_longjmp" } ; CHECK-DAG: attributes #{{[0-9]+}} = { "wasm-import-module"="env" "wasm-import-name"="__invoke_ptr_i32_ptr" } ; CHECK-DAG: attributes #[[ALLOCSIZE_ATTR]] = { allocsize(1) } From a0072099146c55df16c3528fd97bdf24d57ea3f5 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Sat, 16 Mar 2024 12:01:24 +0900 Subject: [PATCH 27/28] comment --- .../WebAssemblyLowerEmscriptenEHSjLj.cpp | 83 ++++++++++--------- 1 file changed, 42 insertions(+), 41 deletions(-) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp index 591585fd565bc..c349c51a8583e 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp @@ -142,6 +142,8 @@ /// into /// __wasm_setjmp(env, label, functionInvocationId) /// +/// __wasm_setjmp records the necessary info (the label and +/// functionInvocationId) to the "env". /// 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. /// @@ -152,8 +154,7 @@ /// __THREW__ = 0; /// %__threwValue.val = __threwValue; /// if (%__THREW__.val != 0 & %__threwValue.val != 0) { -/// %label = testSetjmp(mem[%__THREW__.val], setjmpTable, -/// setjmpTableSize); +/// %label = __wasm_setjmp_test(%__THREW__.val, functionInvocationId); /// if (%label == 0) /// emscripten_longjmp(%__THREW__.val, %__threwValue.val); /// setTempRet0(%__threwValue.val); @@ -167,16 +168,16 @@ /// ... /// default: goto splitted next BB /// } -/// 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 -/// 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. Label -1 means no longjmp -/// occurred. Otherwise we jump to the right post-setjmp BB based on the -/// label. +/// +/// __wasm_setjmp_test examines the jmp buf to see if it was for 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. +/// __wasm_setjmp_test 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. Label -1 means no longjmp occurred. +/// Otherwise we jump to the right post-setjmp BB based on the label. /// /// * Wasm setjmp / longjmp handling /// This mode still uses some Emscripten library functions but not JavaScript's @@ -193,40 +194,39 @@ /// If there are calls to setjmp() /// /// 2) and 3): The same as 2) and 3) in Emscripten SjLj. -/// (setjmpTable/setjmpTableSize initialization + setjmp callsite -/// transformation) +/// (functionInvocationId initialization + setjmp callsite transformation) /// /// 4) Create a catchpad with a wasm.catch() intrinsic, which returns the value -/// thrown by __wasm_longjmp function. In Emscripten library, we have this -/// struct: +/// thrown by __wasm_longjmp function. In the runtime library, we have an +/// equivalent of the following struct: /// /// struct __WasmLongjmpArgs { /// void *env; /// int val; /// }; -/// struct __WasmLongjmpArgs __wasm_longjmp_args; /// -/// The thrown value here is a pointer to __wasm_longjmp_args struct object. We -/// use this struct to transfer two values by throwing a single value. Wasm -/// throw and catch instructions are capable of throwing and catching multiple -/// values, but it also requires multivalue support that is currently not very -/// reliable. +/// The thrown value here is a pointer to the struct. We use this struct to +/// transfer two values by throwing a single value. Wasm throw and catch +/// instructions are capable of throwing and catching multiple values, but +/// it also requires multivalue support that is currently not very reliable. /// TODO Switch to throwing and catching two values without using the struct /// /// All longjmpable function calls will be converted to an invoke that will /// unwind to this catchpad in case a longjmp occurs. Within the catchpad, we -/// test the thrown values using testSetjmp function as we do for Emscripten -/// SjLj. The main difference is, in Emscripten SjLj, we need to transform every -/// longjmpable callsite into a sequence of code including testSetjmp() call; in -/// Wasm SjLj we do the testing in only one place, in this catchpad. +/// test the thrown values using __wasm_setjmp_test function as we do for +/// Emscripten SjLj. The main difference is, in Emscripten SjLj, we need to +/// transform every longjmpable callsite into a sequence of code including +/// __wasm_setjmp_test() call; in Wasm SjLj we do the testing in only one +/// place, in this catchpad. /// -/// After testing calling testSetjmp(), if the longjmp does not correspond to -/// one of the setjmps within the current function, it rethrows the longjmp -/// by calling __wasm_longjmp(). If it corresponds to one of setjmps in the -/// function, we jump to the beginning of the function, which contains a switch -/// to each post-setjmp BB. Again, in Emscripten SjLj, this switch is added for -/// every longjmpable callsite; in Wasm SjLj we do this only once at the top of -/// the function. (after functionInvocationId initialization) +/// After testing calling __wasm_setjmp_test(), if the longjmp does not +/// correspond to one of the setjmps within the current function, it rethrows +/// the longjmp by calling __wasm_longjmp(). If it corresponds to one of +/// setjmps in the function, we jump to the beginning of the function, which +/// contains a switch to each post-setjmp BB. Again, in Emscripten SjLj, this +/// switch is added for every longjmpable callsite; in Wasm SjLj we do this +/// only once at the top of the function. (after functionInvocationId +/// initialization) /// /// The below is the pseudocode for what we have described /// @@ -680,8 +680,9 @@ static bool isEmAsmCall(const Value *Callee) { CalleeName == "emscripten_asm_const_async_on_main_thread"; } -// Generate testSetjmp function call seqence with preamble and postamble. -// The code this generates is equivalent to the following JavaScript code: +// Generate __wasm_setjmp_test function call seqence with preamble and +// postamble. The code this generates is equivalent to the following +// JavaScript code: // %__threwValue.val = __threwValue; // if (%__THREW__.val != 0 & %__threwValue.val != 0) { // %label = __wasm_setjmp_test(%__THREW__.val, functionInvocationId); @@ -1269,8 +1270,8 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) { Instruction *FunctionInvocationId; IRB.SetInsertPoint(Entry->getTerminator()->getIterator()); // This alloca'ed pointer is used by the runtime to identify function - // inovactions. It's just for pointer comparisons. It will never - // be dereferenced. + // invocations. It's just for pointer comparisons. It will never be + // dereferenced. FunctionInvocationId = IRB.CreateAlloca(IRB.getInt32Ty(), nullptr, "functionInvocationId"); FunctionInvocationId->setDebugLoc(FirstDL); @@ -1493,7 +1494,7 @@ void WebAssemblyLowerEmscriptenEHSjLj::handleLongjmpableCallsForEmscriptenSjLj( IRB.SetInsertPoint(NormalBB); IRB.CreateBr(Tail); - BB = NormalBB; // New insertion point to insert testSetjmp() + BB = NormalBB; // New insertion point to insert __wasm_setjmp_test() } } @@ -1502,9 +1503,9 @@ void WebAssemblyLowerEmscriptenEHSjLj::handleLongjmpableCallsForEmscriptenSjLj( // right setjmp-tail if so ToErase.push_back(BB->getTerminator()); - // Generate a function call to testSetjmp function and preamble/postamble - // code to figure out (1) whether longjmp occurred (2) if longjmp - // occurred, which setjmp it corresponds to + // Generate a function call to __wasm_setjmp_test function and + // preamble/postamble code to figure out (1) whether longjmp + // occurred (2) if longjmp occurred, which setjmp it corresponds to Value *Label = nullptr; Value *LongjmpResult = nullptr; BasicBlock *EndBB = nullptr; From a060778d5e9b2e2ce6b15ac013751723b1ef3fa3 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Thu, 21 Mar 2024 12:03:20 +0900 Subject: [PATCH 28/28] simplify a bit --- .../Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp index c349c51a8583e..027ee1086bf4e 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp @@ -1267,12 +1267,11 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) { DebugLoc FirstDL = getOrCreateDebugLoc(&*Entry->begin(), F.getSubprogram()); SplitBlock(Entry, &*Entry->getFirstInsertionPt()); - Instruction *FunctionInvocationId; IRB.SetInsertPoint(Entry->getTerminator()->getIterator()); // This alloca'ed pointer is used by the runtime to identify function // invocations. It's just for pointer comparisons. It will never be // dereferenced. - FunctionInvocationId = + Instruction *FunctionInvocationId = IRB.CreateAlloca(IRB.getInt32Ty(), nullptr, "functionInvocationId"); FunctionInvocationId->setDebugLoc(FirstDL);