さて前の記事でEmscriptenのインストールとBonanzaのビルドまで終えられました。では早速、Emscriptenを利用してJavaScriptに移植していきましょう。
前の記事はこちら→ embona - ブラウザで動くBonanzaを作ってみた(その1)
とりあえず遊んでみたい、という方はその1の記事の最初にリンクを用意しておきました。そちらをご参照ください。
とりあえずJavaScriptを出力してみる
さて、まずしなければいけないのは、gccの利用をやめてemccでビルドすることです。Makefileを修正しましょう。
emcc:
$(MAKE) CC=emcc CFLAGS='-std=gnu99 -O2 -Wall $(OPT)' LDFLAG1='-lm -lpthread' bonanza
これを追加してmake emccしてみます。
$ make clean
$ make emcc
(略)
./bitop.h:20:12: fatal error: 'smmintrin.h' file not found
# include <smmintrin.h>
^
1 error generated.
早速ですね…。とりあえずbitop.hの中身を見ると、次のような感じです。
#if defined(HAVE_SSE2) || defined(HAVE_SSE4)
#if defined(HAVE_SSE4)
# include <smmintrin.h>
なるほど、SSE命令(インテルCPU独自の高速命令)を使うところなのですね。SSE命令はEmscriptenでは使えないですが、大変ありがたいことにBonanzaはdefineでSSEを使わないように設定出来るようです。Makefileの中のコメントでコンパイルオプションについて解説があるので、それを参照してMakefileの中のオプションを次のように編集しました。
OPT =-DNDEBUG -DMINIMUM -DDFPN -DTLP -DDFPN_CLIENT -DINANIWA_SHIFT -DMNJ_LAN -DCSA_LAN
SSE系のものをすべてはずし、改めてmake clean && make emccを行うと、次のようなエラーが出ました。
./shogi.h:57:39: error: typedef redefinition with different types
おそらくBonanza側での想定外のオプションだったせいで定義が被ってしまったようです。両方とも同じ定義のようでしたので、今回はとりあえずshogi.hの該当の定義をコメントアウトしてビルドをしてみました。
$ make emcc
(略)
emcc -lm -lpthread -o bonanza data.o main.o io.o proce.o utility.o ini.o attack.o book.o makemove.o unmake.o time.o csa.o valid.o bitop.o iterate.o searchr.o search.o quiesrch.o evaluate.o swap.o hash.o root.o next.o movgenex.o genevasn.o gencap.o gennocap.o gendrop.o mate1ply.o rand.o learn1.o learn2.o evaldiff.o problem.o ponder.o thread.o sckt.o debug.o mate3.o genchk.o phash.o dfpn.o dfpnhash.o
WARNING root: emcc: cannot find library "m"
WARNING root: emcc: cannot find library "pthread"
$
(警告に目をつぶって)とりあえずビルドは成功しました!ところが、出力ファイルの ./bonanza はバイナリファイルであり、目的のJavaScriptの出力ファイルがありません。最初のハマりどころです。Emscriptenは、厄介なことに-oオプションで指定した出力ファイルの拡張子によって出力する内容を変化させる仕様となっております。
- .js: nodeで動かす目的のJavaScript
- .html: ブラウザで動かす目的のJavaScriptとHTMLファイル
- それ以外: 普通の実行バイナリ
今回は、まずnodeで実行することを目指してみましょう。Makefileを適宜変更します。
emcc:
$(MAKE) CC=emcc CFLAGS='-std=gnu99 -O2 -Wall $(OPT)' LDFLAG1='-lm -lpthread' bonanza.js
bonanza.js : $(OBJS)
$(CC) $(LDFLAG1) -o bonanza.js $(OBJS) $(LDFLAG2)
さて、これでmake clean && make emccをすればついにJavaScriptの出力が得られます!初めて Emscripten を触った方は、ビルドの遅さにビックリされるかもしれません。
$ make emcc
(略)
emcc -lm -lpthread -o bonanza.js data.o main.o io.o proce.o utility.o ini.o attack.o book.o makemove.o unmake.o time.o csa.o valid.o bitop.o iterate.o searchr.o search.o quiesrch.o evaluate.o swap.o hash.o root.o next.o movgenex.o genevasn.o gencap.o gennocap.o gendrop.o mate1ply.o rand.o learn1.o learn2.o evaldiff.o problem.o ponder.o thread.o sckt.o debug.o mate3.o genchk.o phash.o dfpn.o dfpnhash.o
WARNING root: emcc: cannot find library "m"
WARNING root: emcc: cannot find library "pthread"
warning: unresolved symbol: pthread_create
warning: unresolved symbol: pthread_attr_setdetachstate
$
なんかいやな警告が最後に出ていますね…。前の記事で書いた通り、Emscriptenでスレッド関連の実装は現在サポートされておりません。とりあえず警告ですんでいるので、何はともあれ実行してみましょうか。
$ node bonanza.js
(略)
RangeError: Offset out of range.
at allocate (/Users/tkihira/projects/bonanza_v6.0/src/client/bonanza.js:659:14)
一筋縄ではいきそうにないですが、とりあえず実行できるJavaScriptを出力することには成功しました!
Bonanzaが動くようにEmscriptenの設定を変える
さて、ではエラーの場所を見てみましょう。bonanza.jsの1358行目ですね。自動生成されたコードですが、比較的読みやすいコードです。
/* memory initializer */ allocate([37,115,0,0, ... ,37,115], "i8", ALLOC_NONE, Runtime.GLOBAL_BASE+275492160);
メモリ初期化の所で失敗していますね。Emscriptenは仕組み的に、起動時にヒープメモリをすべて確保し、それを適宜プログラム内でallocateするようになっています。というわけで、おそらくヒープメモリ不足です。将棋エンジンは探索のために大量のメモリを仕様しますから仕方ないですね。Emscriptenでは、ヒープメモリはTOTAL_MEMORYを定義すれば拡張することが出来ます。
$(CC) $(LDFLAG1) -o bonanza.js $(OBJS) $(LDFLAG2) -s TOTAL_MEMORY=333333333
まあトータルでこんなもんあれば多分動くだろう、と適当に設定しました。これでリビルドして実行すると…
$ node bonanza.js
increasing TOTAL_MEMORY to 335544320 to be compliant with the asm.js spec
ERROR: Can't open a file, fv.bin
$
おおお!前の記事で出たのと同じエラーが出ました!その前にEmscriptenさんに怒られたので、TOTAL_MEMORYを335544320に変えておきますw
$(CC) $(LDFLAG1) -o bonanza.js $(OBJS) $(LDFLAG2) -s TOTAL_MEMORY=335544320
さて、ここまでいい感じで来ていますね!しかしここで、はたと問題にぶち当たります。Emscriptenにおいて、ファイルの入出力ってどのように扱えばいいのでしょうか…。というわけで、次の記事ではEmscriptenでのファイルの扱い方について説明します。
次の記事はこちら→ embona - ブラウザで動くBonanzaを作ってみた(その3)