今日だけ x86 とアセンブラな記録

こんばんは、siokoshou です。忘れられない CPU 命令は eieio です。
最近 x86 アセンブラがマイブームです。 x86 に詳しくなかったんですがインテルのマニュアルと古い本を何冊か読んでちょっとわかってきました。で、ネットをふらふらしてたらおもしろそうなネタが転がってたので、高速化にチャレンジしてみました。もっと速いコードが示されてるので実用的な意味はありませんが(^^;

続きを読む

あこがれの cmov を使ってみた

前のエントリーの続き。
パイプラインの長い現代 CPU の敵はジャンプだ!ってことでジャンプを cmov 命令(条件 mov)に置き換えてみたんだけど、逆に遅くなってしまいました。cmov にあこがれてて、いつか使ってみたいと思ってたのにガッカリした!! やみくもに使ってもうまくいくわけじゃないんですねぇ。

eiz レジスタ?

さらに続き。
もう飽きてきたころに、なんで C のほうが速いんだろうってことで objdump で逆アセンブルしてみたときに見つけた変なレジスタ

4017a5:	8d 74 26 00          	lea    0x0(%esi,%eiz,1),%esi
4017a9:	8d bc 27 00 00 00 00 	lea    0x0(%edi,%eiz,1),%edi
4017b0: ...

Intel形式なら lea edi, [edi + eiz + 0] でしょうか。

eiz レジスタ?ぐぐってみると Add fake index registers, EIZ/RIZ, to x86 assembler/disassembler. だそうで、フェイクらしい。命令フォーマットを読み解いてみると、8d が LEA のオペコード、ModR/M が 74 なので デスティネーションが esi、SIB が 26 なので esi + none、ディスプレースメントが 0。というわけで、インデックスレジスタの指定なしを表してるようです。

さらに、この2つの命令は実行しても何も変わりません。CPU時間を食うのと、スペースを食うだけです。 gcc で -O3 をつけてコンパイルした結果なのになんでこんなのが挟み込まれるのかと思ったら、http://gcc.gnu.org/ml/gcc/2007-03/msg00817.html によるとアラインメント調整だそうです。なるほど納得。命令フェッチが16バイト境界に揃ってると有利とかなんとかありましたね。この2つの命令の次のアドレスがきっちり揃っています。

アセンブルしたコードを見てみると、あちこちにこの命令のバリエーションが挟み込まれています。上の例でも 4byte と 7byte の2つがありますが、ほかに 3byte (8d 76 00 lea 0x0(%esi),%esi)、6byte (8d b6 00 00 00 00 lea 0x0(%esi),%esi) もありました。一つだけ使ったり、上のように二つ並べたりしてます。
いずれも次の命令がきっちり 16byte 境界に揃っていました。ジャンプなどの飛び先を 16byte 境界にそろえているようです。

でも、必ずしも飛び先アドレスが 16byte 境界にそろってるわけでもないので、飛んだ先に十分な命令があれば調整しないとかしてるんでしょうかねぇ。まあ、とにかく、コンパイラには私はかないそうもないとわかりましたw

以上、アセンブラな記録はおしまい。

nop は遅い

あ、もう一つだけ。さっきの gcc のスレッドにまだ続きがあって、強烈におもしろかった。なんで nop で埋めないの?って質問があって、その答えが nop じゃ遅いだろ JK みたいな回答がw

たしかに nop を何バイトも埋めたらデコーダと実行機の数にもよるけど、数サイクル浪費しますね。対して何もしなくて速くて長い命令が上で書いた lea ってことだそうです。nop 遅いって、すごい世界だw いつか nop おせーよとか言ってみたいw やっぱりコンパイラはすごい!