WASMで負の数を得る命令ってどうやるんだろうか?とC言語からコンパイルしてみたら、条件分岐のないイカしたビット操作のコードを出力してきた。
C言語で絶対値を得るコード:
|
を書いて、EmscriptenでWASMにコンパイルして出力結果を見てみると:
$ emcc -s WASM=1 -s NO_EXIT_RUNTIME=1 -O1 neg.c && wasm2wat a.out.wasm |
条件分岐がないコードを出力してくる。 一見何しているのかわからなかったので読み解いてみた。
- 引数が負の場合(例:
-5=1011b
):shr_s -5, 31 (= 1111b = -1)
: 符号ビットで埋めた値-1
が得られるtee
でスタックトップをローカルに格納、しかし取り除かないadd -5, -1 (= 1010b = -6)
:引数-1
xor -6, -1 (= 0101b = 5)
: 全ビット反転- 要するに2の補数を取っている
- 正か0の場合(例:
5
):shr_s 5, 31 (= 0)
:0
add 5, 0 (= 5)
: 変化なしxor 5, 0 (= 5)
: 変化なし- 引数の値がそのまま得られる
出力されるコードが多少長くなっても条件分岐をさせないほうがパフォーマンスがいいということなんですかね。
x86の場合
試しにMacOS/x86のgcc(Clang)でコンパイルしてみたらまたちょっと違って、negl
で符号反転した値を作って cmovl
という条件付きムーブ命令で選択するコードが出力された:
$ gcc -S -O1 -fno-asynchronous-unwind-tables neg.c && cat neg.s |
gcc/UbuntuではWASM版と同じような方法だった(xor
を先に行って、add
の代わりに sub
)。