2007-11-15

SPARC のアセンブラ


Kernel Planet のDaveM's Linux Networking BLOGを見て。

2つのレジスタの状態によって、第3のレジスタに 0 か 1 を代入するということをしたい時。
つまり、


if(reg1==reg2)
reg3=1;
else
reg3=0;

ってことをしたい時。

下のようなコードだと分岐予測がきかないため効率が良くないらしく(いまいち理由がわからない…)、


cmp %reg1, %reg2
mov 0, %reg3
bCOND,a 1f
mov 1, %reg3
1:



次のようなコードを生成するらしい。


/* %reg3 = (%reg1 != %reg2) */
xor %reg1, %reg2, %reg3
subcc %g0, %reg3, %g0
addx %g0, 0, %reg3

/* %reg3 = (%reg1 < %reg2) */
cmp %reg1, %reg2
addx %g0, 0, %reg3

/* %reg3 = (%reg1 > %reg2) */
cmp %reg2, %reg1
addx %g0, 0, %reg3



これを読むために色々調べたことをメモ。
SPARC のアセンブラの演算は

hoge <レジスタ>,<レジスタもしくは数字>,<レジスタ>

と呼出し、第1引数と第2引数の演算結果を第3引数のレジスタに代入する。
g0 レジスタは、常に 0 がはいっていて代入は意味がない。
〜cc は with carry で演算結果によっては carry bit をセットする。
〜x は carry bit を考慮して演算する。
下2つはともかく、 %reg3 = (%reg1 != %reg2) の時が面白い。
xor して reg3 が 0 (reg1 == reg2) なら
subcc は 0- 0 で carry bit は 0 。
addx は 0+ 0 + <carry bit> で reg3 は 0。

xor して reg3 が 正 (reg1 != reg2) なら
subcc は 0- 正 で carry bit は 1 。
addx は 0+ 0 + <carry bit> で reg3 は 1。

引き算の carry bit を足し算で使うというアイデア。

386本で読んだはずなのに、 carry bit なんてすっかり忘れてた…。