Open4

WasmLinux: LLVMのsetjmp/longjmp passは素のWasmでも使えるのかよ!

okuokuokuoku

https://github.com/WebAssembly/wasi-libc/pull/467

Despite the pass name WebAssemblyLowerEmscriptenEHSjLj, that pass is also responsible for some of Wasm SjLj transformation.

マジかよ!

https://github.com/llvm/llvm-project/blob/c105848fd29d3b46eeb794bb6b10dad04f903b09/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp#L187

マジだったよ。。。

というわけで、自前のsetjmp/longjmpを捨てて、このtranslateは一旦前提にしても良いんではないか。ただし、setjmp のたびに malloc(40) が入るのがちょっと微妙とも言える。

https://github.com/llvm/llvm-project/blob/c105848fd29d3b46eeb794bb6b10dad04f903b09/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp#L1294-L1295

okuokuokuoku

Exception handlingを前提にして良いのか問題

https://webassembly.org/features/

ブラウザとv8は基本的に対応している。WASI系(Wasmer、Wasmtime)やインタプリタ(WASM3やWAMR)はダメみたいですね。。

これはめっちゃ悩むところだな。。まぁでもWASIに入るんなら1、2年で追いついてくるでしょう多分ということで一旦WASI系のインタプリタは切ってしまおうかな。どうせmmap類に対応できないのは全部一緒だし。

okuokuokuoku

アセンブリを眺める

いわゆるbuiltinとして処理されるので、関数名さえ合っていれば setjmplongjmp の呼出しとして変換される。

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
okuokuokuoku

vfork をどうすんのか問題

今のランタイムは setjmpvfork を同じテクニックで実装している。

https://github.com/okuoku/wasmlinux-musl/commit/02e7b63250477d8fbb10dbfb1d614caa76fa2c9b

https://github.com/okuoku/wasmlinux-musl/commit/f6bd94f8d03f8e41dc817b44a6ac981e0280b4f0

LLVMのpassは setjmp は救うものの vfork は救わないので、これをどうにかする必要がある。

うーん。。 jmp_buf をグローバル変数に取っておいてそちらに対して setjmp するのを vfork ということにして、 execve されたときはランタイム側からWasm的なthrowをすれば良い。。。ような気がする。つまり、単に、

#define vfork() setjmp(global_jmpbuf)

のようなマクロにできないか。

ただし、 vfork には setjmp みたいな代入制約は無いから、厳密には正常動作は保証されない。いやまぁLLVMの実装では問題ないけど。

https://zenn.dev/okuoku/scraps/f1044bc008b139