Open4

WebGL-Native: 初期化時にスタックオーバーフローする問題

okuokuokuoku

prev: https://zenn.dev/okuoku/scraps/5777ef80737f27
next: https://zenn.dev/okuoku/scraps/1128673fdb2a15

ちょっと複雑になると起動しなくなる

一難去ってまた一難だな。。

複雑な代入は一旦演算の途中結果をスタックにspillすることになるが、これが最適化を切っていることにより全てが別々のインスタンスになり、1エントリあたり数ワード(wasm2c的な定義のtableぶん)のスタック消費になってしまうように見える。

生成された関数の冒頭部分の逆アセンブルは:

static void init_table(void) {
00007FFB1D66E970  push        r15  
00007FFB1D66E972  push        r14  
00007FFB1D66E974  push        r13  
00007FFB1D66E976  push        r12  
00007FFB1D66E978  push        rsi  
00007FFB1D66E979  push        rdi  
00007FFB1D66E97A  push        rbp  
00007FFB1D66E97B  push        rbx  
00007FFB1D66E97C  mov         eax,143528h  ★ スタック要求サイズ
00007FFB1D66E981  call        __chkstk (07FFB1D601055h)  
00007FFB1D66E986  sub         rsp,rax  
00007FFB1D66E989  lea         rax,[w2c___ZL26RegisterInterfaceSplitImplyyP15IUnityInterface (07FFB20BE04F0h)]  
00007FFB1D66E990  lea         rcx,[w2c_b289 (07FFB20BE0490h)]  
00007FFB1D66E997  lea         rdx,[w2c__ManagedStreamHelpers_ManagedStreamSeek_mD7B16AF1079F3F11EE782A32F851ABD761BD2E9E (07FFB20BE0240h)]  
00007FFB1D66E99E  lea         r8,[w2c_b288 (07FFB20BE01D0h)]  
00007FFB1D66E9A5  lea         r9,[w2c__InputDevices_InvokeConnectionEvent_m19E87BB6671D4B4CE3EB322EEE3621B0146A7077 (07FFB20BDF9F0h)]  
00007FFB1D66E9AC  lea         r10,[w2c_b287 (07FFB20BDF990h)]  
00007FFB1D66E9B3  lea         r11,[w2c_b286 (07FFB20BDE7C0h)]  
00007FFB1D66E9BA  lea         rsi,[w2c__UmAlQuraCalendar_CheckTicksRange_mF5B461A483849D8CB9A2B87B47FA5218E68FEFF7 (07FFB20BDF130h)]  
00007FFB1D66E9C1  lea         rdi,[w2c__HijriCalendar_CheckTicksRange_m2D1B18699664C2C21AC0F985BD6731A8B3DEC395 (07FFB20BDE820h)]  
00007FFB1D66E9C8  lea         rbx,[w2c__PlayerConnection_MessageCallbackInternal_m223C558BDA58E11AE597D73CEC359718A336FD74 (07FFB20BDE20

Win32におけるデフォルトスタックサイズは1MiBとかなのでちょっと足りない結果になる。

okuokuokuoku

とりあえず最適化を有効に

https://github.com/okuoku/cwgl-proto/commit/31962129e5479dd1a20295aee6a84fc86d02a699

diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt
index d44a802..7ef9967 100644
--- a/apps/CMakeLists.txt
+++ b/apps/CMakeLists.txt
@@ -1,6 +1,6 @@
@@ -32,6 +32,7 @@ function(translate_wasm proj wasmpath)
     # .c => .bc
     add_custom_command(OUTPUT ${bcpath}
         COMMAND clang -g3 -c -emit-llvm -O0
+        -Xclang -disable-O0-optnone
         -DWASM_RT_MEMCHECK_SIGNAL_HANDLER=1
         -I${WASMSTUB}
         -o ${bcpath} ${wasmcpath}
@@ -40,7 +41,7 @@ function(translate_wasm proj wasmpath)
 
     # .bc => .o
     add_custom_command(OUTPUT ${outpath}
-        COMMAND clang -g3 -O0 -c
+        COMMAND clang -g3 -O1 -c
         -o ${outpath} ${bcpath}
         DEPENDS ${bcpath}
         COMMENT "WASM: Generating machine code LLVM => bin (${proj})")

-Xclang -disable-O0-optnone がミソで、LLVM-IRレベルでの最適化を抑制しつつ、後段のclangによるamd64コード生成で最適化をしたければこのオプションが必要になる。LLVM-IRの生成時に -O0 を付けた場合、後段(特にLTO時)での最適化を避けるために optnone 属性が付いてしまう

... これで一応起動はするようになったが、ビルドには30分以上掛かるように。。まぁ滅多に再ビルドしないから良いけど。。

okuokuokuoku

なんか遅い

せっかく最適化を有効にしたんでDOSboxを起動してみたけど何か遅い。

Import関数(= JavaScript側に実装された関数) egetTempRet0 らしく、64bit値用のヘルパー関数らしい。これが実行時間の 10% 程度を占めている。

    "e": getTempRet0,

Node.jsの WebAssembly を使う場合は、WASMとJavaScriptの界面のコストはほぼ無視できる(本当?)と考えられるが、今回のようにWASM実行環境とJavaScript環境を分離しようとすると、どうしてもそれなりのコストになってしまう。

これはちょっとどうしようも無いかな。。Dosbox-xがfastcompを捨ててくれれば改善できるかもしれないが。。

okuokuokuoku

ついでにスタック破壊問題を修正

デバッグビルドだとこんな警告出るんだ。。破壊されている alloca のアロケーションが足りなかった。

https://github.com/okuoku/cwgl-proto/commit/13a5e6d0186744bc2d559d2ac562c4b1d9c25052

diff --git a/node-nccc/node-nccc.c b/node-nccc/node-nccc.c
index 2c9d939..65c697f 100644
--- a/node-nccc/node-nccc.c
+++ b/node-nccc/node-nccc.c
@@ -128,7 +128,7 @@ nccc_call_trampoline(napi_env env, napi_callback_info info){
         abort();
     }
     if(dispatch){
-        inbuf = alloca(sizeof(uint64_t)*ctx->incount+1);
+        inbuf = alloca(sizeof(uint64_t)*(ctx->incount+1));
     }else{
         inbuf = alloca(sizeof(uint64_t)*ctx->incount);
     }

寝不足では。

dispatch モードの場合、引数は実際に呼ばれるC関数ポインタが追加される関係上1つ増える。このため、1つ増えた変数を格納するぶんだけ余計に alloca する必要がある(前のコードだと演算子の優先順位上1バイトしか増えない)。