WasmLinux: LLVMのsetjmp/longjmp passは素のWasmでも使えるのかよ!
Despite the pass name
WebAssemblyLowerEmscriptenEHSjLj
, that pass is also responsible for some of Wasm SjLj transformation.
マジかよ!
マジだったよ。。。
というわけで、自前のsetjmp/longjmpを捨てて、このtranslateは一旦前提にしても良いんではないか。ただし、setjmp のたびに malloc(40)
が入るのがちょっと微妙とも言える。
Exception handlingを前提にして良いのか問題
ブラウザとv8は基本的に対応している。WASI系(Wasmer、Wasmtime)やインタプリタ(WASM3やWAMR)はダメみたいですね。。
これはめっちゃ悩むところだな。。まぁでもWASIに入るんなら1、2年で追いついてくるでしょう多分ということで一旦WASI系のインタプリタは切ってしまおうかな。どうせmmap類に対応できないのは全部一緒だし。
アセンブリを眺める
いわゆるbuiltinとして処理されるので、関数名さえ合っていれば setjmp
や longjmp
の呼出しとして変換される。
typedef long jmp_buf;
void longjmp(jmp_buf env, int val);
int setjmp(jmp_buf env);
int
check(int in){
jmp_buf jb;
if(setjmp(jb) == 0){
if(in == 3){
longjmp(jb, 4);
}
return in+1;
}else{
return in+2;
}
}
clang -g --target=wasm32 -c -o out.wasm -mllvm -wasm-enable-sjlj testsjlj.c
wasm2wat --enable-exceptions out.wasm
確かにEmscripten風のimportが出てきた:
(import "env" "__linear_memory" (memory (;0;) 0))
(import "env" "__stack_pointer" (global (;0;) (mut i32)))
(import "env" "malloc" (func (;0;) (type 0)))
(import "env" "saveSetjmp" (func $saveSetjmp (type 1)))
(import "env" "getTempRet0" (func $getTempRet0 (type 2)))
(import "env" "__wasm_longjmp" (func $__wasm_longjmp (type 3)))
(import "env" "testSetjmp" (func $testSetjmp (type 5)))
(import "env" "free" (func (;5;) (type 4)))
(import "env" "__indirect_function_table" (table (;0;) 0 funcref))
try
命令は今のプロポーサルには無いけど、とりあえずwasm2cでは正常に認識されるので使えるようだ。
try ;; label = @6
local.get 59
local.get 60
call $__wasm_longjmp
br 5 (;@1;)
catch $__c_longjmp
vfork
をどうすんのか問題
今のランタイムは setjmp
と vfork
を同じテクニックで実装している。
LLVMのpassは setjmp
は救うものの vfork
は救わないので、これをどうにかする必要がある。
うーん。。 jmp_buf
をグローバル変数に取っておいてそちらに対して setjmp
するのを vfork
ということにして、 execve
されたときはランタイム側からWasm的なthrowをすれば良い。。。ような気がする。つまり、単に、
#define vfork() setjmp(global_jmpbuf)
のようなマクロにできないか。
ただし、 vfork
には setjmp
みたいな代入制約は無いから、厳密には正常動作は保証されない。いやまぁLLVMの実装では問題ないけど。