[clang] [llvm] [WebAssembly] Implement an alternative translation for -wasm-enable-sjlj (PR #84137)
YAMAMOTO Takashi via cfe-commits
cfe-commits at lists.llvm.org
Thu Mar 21 07:40:56 PDT 2024
https://github.com/yamt updated https://github.com/llvm/llvm-project/pull/84137
>From caf4c18b0450eaecabbf985de5ea7dbbed83bbe5 Mon Sep 17 00:00:00 2001
From: YAMAMOTO Takashi <yamamoto at midokura.com>
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 b7c6efab83e806..ea5807983f465a 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 e8f58a19d25e3b..7f15742367be09 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp
@@ -54,6 +54,9 @@ cl::opt<bool>
// setjmp/longjmp handling using wasm EH instrutions
cl::opt<bool> WebAssembly::WasmEnableSjLj(
"wasm-enable-sjlj", cl::desc("WebAssembly setjmp/longjmp handling"));
+cl::opt<bool> 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 15aeaaeb8c4a4e..d23de9d407d894 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
@@ -44,6 +44,7 @@ extern cl::opt<bool> WasmEnableEmEH; // asm.js-style EH
extern cl::opt<bool> WasmEnableEmSjLj; // asm.js-style SjLJ
extern cl::opt<bool> WasmEnableEH; // EH using Wasm EH instructions
extern cl::opt<bool> WasmEnableSjLj; // SjLj using Wasm EH instructions
+extern cl::opt<bool> 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 0427fe473f8e16..b616c437b68d81 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<Instruction>(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<Instruction>(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<PHINode *, 4> 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<Instruction *, 16> ExitingInsts;
- for (BasicBlock &BB : F) {
- Instruction *TI = BB.getTerminator();
- if (isa<ReturnInst>(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<CallInst>(&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<Instruction *, 16> ExitingInsts;
+ for (BasicBlock &BB : F) {
+ Instruction *TI = BB.getTerminator();
+ if (isa<ReturnInst>(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<CallInst>(&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<OperandBundleDef, 1> Bundles;
- if (auto *CB = dyn_cast<CallBase>(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<OperandBundleDef, 1> Bundles;
+ if (auto *CB = dyn_cast<CallBase>(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 70685b2e3bb2de..6db019034028bc 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 <yamamoto at midokura.com>
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 b616c437b68d81..7c599717c55a00 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 <yamamoto at midokura.com>
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 7c599717c55a00..5ac0e4f24cecea 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 <yamamoto at midokura.com>
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 ea5807983f465a..8d49eaf901b330 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 <yamamoto at midokura.com>
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 7f15742367be09..4371318f4443ce 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp
@@ -56,7 +56,7 @@ cl::opt<bool> WebAssembly::WasmEnableSjLj(
"wasm-enable-sjlj", cl::desc("WebAssembly setjmp/longjmp handling"));
cl::opt<bool> 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 <yamamoto at midokura.com>
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 5ac0e4f24cecea..8adb898f53ac8e 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<Instruction>(U.getUser()))
- if (I->getParent() != Entry)
- SetjmpTableSSA.RewriteUse(U);
- for (auto &U : make_early_inc_range(SetjmpTableSize->uses()))
- if (auto *I = dyn_cast<Instruction>(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<Instruction>(U.getUser()))
+ if (I->getParent() != Entry)
+ SetjmpTableSSA.RewriteUse(U);
+ for (auto &U : make_early_inc_range(SetjmpTableSize->uses()))
+ if (auto *I = dyn_cast<Instruction>(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 <yamamoto at midokura.com>
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 8adb898f53ac8e..66497da849ff3e 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 <yamamoto at midokura.com>
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 8d49eaf901b330..b7c6efab83e806 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 4371318f4443ce..e8f58a19d25e3b 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp
@@ -54,9 +54,6 @@ cl::opt<bool>
// setjmp/longjmp handling using wasm EH instrutions
cl::opt<bool> WebAssembly::WasmEnableSjLj(
"wasm-enable-sjlj", cl::desc("WebAssembly setjmp/longjmp handling"));
-cl::opt<bool> 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 d23de9d407d894..15aeaaeb8c4a4e 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
@@ -44,7 +44,6 @@ extern cl::opt<bool> WasmEnableEmEH; // asm.js-style EH
extern cl::opt<bool> WasmEnableEmSjLj; // asm.js-style SjLJ
extern cl::opt<bool> WasmEnableEH; // EH using Wasm EH instructions
extern cl::opt<bool> WasmEnableSjLj; // SjLj using Wasm EH instructions
-extern cl::opt<bool> 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 6db019034028bc..70685b2e3bb2de 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 <yamamoto at midokura.com>
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 66497da849ff3e..9781e3a4be6f9c 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<Instruction *, 16> 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 <yamamoto at midokura.com>
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 9781e3a4be6f9c..dcf944da37ae39 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<PHINode *> &SetjmpRetPHIs);
void
handleLongjmpableCallsForWasmSjLj(Function &F, InstVector &SetjmpTableInsts,
- InstVector &SetjmpTableSizeInsts,
SmallVectorImpl<PHINode *> &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<PHINode *> &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 <yamamoto at midokura.com>
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 dcf944da37ae39..20cc72845885c9 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 <yamamoto at midokura.com>
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 20cc72845885c9..00c6d82ac4c3cf 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 <yamamoto at midokura.com>
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 00c6d82ac4c3cf..04eb9081199f6a 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<PHINode *> &SetjmpRetPHIs);
void
- handleLongjmpableCallsForWasmSjLj(Function &F, InstVector &SetjmpTableInsts,
+ handleLongjmpableCallsForWasmSjLj(Function &F,
+ Instruction *FunctionInvocationId,
SmallVectorImpl<PHINode *> &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<PHINode *> &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 <yamamoto at midokura.com>
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 04eb9081199f6a..448de86b62fe8f 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 <yamamoto at midokura.com>
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 448de86b62fe8f..045be27c4a8360 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 <yamamoto at midokura.com>
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 b8d2230fac9f5b..82c04e24b72f13 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 <yamamoto at midokura.com>
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 25471eb50081b3..bd8db83a0e57ed 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 <yamamoto at midokura.com>
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 045be27c4a8360..92536dd8003c41 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 <yamamoto at midokura.com>
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 92536dd8003c41..920f3e7a570031 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 <yamamoto at midokura.com>
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 920f3e7a570031..3d9eccb4219894 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 <yamamoto at midokura.com>
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 3d9eccb4219894..b7c62eab7b145b 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<PHINode *> &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<Instruction *, 64> ToErase;
- // Vector of %setjmpTable values
- SmallVector<Instruction *, 4> SetjmpTableInsts;
- // Vector of %setjmpTableSize values
- SmallVector<Instruction *, 4> 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<Instruction>(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<PHINode *, 4> 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<Instruction *, 16> ExitingInsts;
- for (BasicBlock &BB : F) {
- Instruction *TI = BB.getTerminator();
- if (isa<ReturnInst>(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<CallInst>(&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<OperandBundleDef, 1> Bundles;
- if (auto *CB = dyn_cast<CallBase>(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<Instruction>(U.getUser()))
- if (I->getParent() != Entry)
- SetjmpTableSSA.RewriteUse(U);
- for (auto &U : make_early_inc_range(SetjmpTableSize->uses()))
- if (auto *I = dyn_cast<Instruction>(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<PHINode *> &SetjmpRetPHIs) {
Module &M = *F.getParent();
LLVMContext &C = F.getContext();
IRBuilder<> IRB(C);
SmallVector<Instruction *, 64> 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 <yamamoto at midokura.com>
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 b7c62eab7b145b..8f72e6d7898e21 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 <yamamoto at midokura.com>
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 8f72e6d7898e21..c483644eb1e1db 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 <yamamoto at midokura.com>
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 c483644eb1e1db..18a94900e910e7 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 <yamamoto at midokura.com>
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 18a94900e910e7..591585fd565bcd 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 <yamamoto at midokura.com>
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 aa4d87756c87d0..4a63c812d6ae9a 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 7cf05cc922cd37..32942cd92e684f 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 1a85a63e44ad4b..79ae16191d6b3c 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 4f694151c76132..fec9836a1607c5 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 7115b01ed1618e..27ec95a2c462ab 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 <yamamoto at midokura.com>
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 591585fd565bcd..c349c51a8583e0 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 <yamamoto at midokura.com>
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 c349c51a8583e0..027ee1086bf4e0 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);
More information about the cfe-commits
mailing list