前回言い忘れましたが,このシリーズの目標はLL魂2007デモコードのchaos.cpp程度のものを読めて理解できることを考えています.
具体的には
- レジスタやスタックを理解する
- 関数を作れる
- SIMD命令の基礎を知る
- JITのメリットを理解する
あたりを目指します.なお,JITアセンブラは通常のアセンブラに比べてワンクッション概念が必要になります.もしかしたらアセンブリ言語の本当の入門としては不適切かもしれませんが,まあご了承ください.
それでは,最初に他の言語と同様,"Hello Xbyak!"を表示してみましょう.
#include "xbyak/xbyak.h" #include <stdio.h> struct HelloGenerator : public Xbyak::CodeGenerator { // (A) HelloGenerator() // (B) { push((int)"Hello Xbyak!"); // (C1) call((int)puts); // (C2) add(esp, 4); // (C3) ret(); // (C4) } }; int main() { HelloGenerator hello; // (D) void (*code)() = (void (*)())hello.getCode(); // (E) code(); // (F) return 0; }
VC系ではコンソールアプリとしてコンパイルしてください.
gccでは-fno-operator-namesをつけるのを忘れないでください.これはandやorを演算子ではなく関数として扱いたいためです.
> g++ t.cpp -g -fno-operator-names > ./a.out > Hello Xbyak!
# C++的には
#include <stdio.h> int main(int argc, char *argv[]) { argc == 1 and puts("need option"); }
ってできたのご存じでした?
それはともかくコードの説明に入ります.Xbyakでは実行時に命令をアセンブルしてバイトコード(以下マシン語と呼ぶことにします)を生成します.それを生成するためのクラスがXbyak::CodeGeneratorであり,これを継承する(A)ことでそのクラス内でx86の殆どの命令を記述できるようになります.
今後も繰り返すことになるかと思いますが,コンパイル時ではなく実行時であることに注意してください.ここではHelloGeneratorのコンストラクタにXbyak命令(面倒なので以後asmと呼びます)を記述している(B)ので,main()内のインスタンスhelloが生成されたとき(D)にアセンブルされます.
アセンブルされたマシン語はgetCode()メソッドで取得できます(E).この返り値は生成されたマシン語へのポインタですので,適当な関数ポインタ(詳細は後述)にキャストし,その関数を呼び出すことでマシン語を実行します(F).
以上がXbyakを使った場合の大まかな流れです.
続いて実行時の挙動を追いかけてみましょう.
VC系ではDebugモードでコンパイルし,(F)のところにブレークポイントを置いてデバッグ実行します.停止したところでVC6なら[表示]→[デバッグウィンドウ]→[混合モード],VC8なら[デバッグ]→[ウィンドウ]→[逆アセンブル]で
19: code(); 00401696 8B F4 mov esi,esp 00401698 FF 95 F4 FE FF FF call dword ptr [ebp-10Ch] 0040169E 3B F4 cmp esi,esp 004016A0 E8 2B 14 01 00 call __chkesp (00412ad0)
というようなウィンドウに入ってください.[F10]を一度押し,callのところで[F11]を押すと
003730A4 68 B0 01 44 00 push offset string "Hello Xbyak!" (004401b0) 003730A9 E8 62 FA 09 00 call puts (00412b10) 003730AE 83 C4 04 add esp,4 003730B1 C3 ret
が表示されると思います.左端の数値は違うかもしれません.意味も今は分からなくて結構です.ただ上記サンプルの(C1)~(C4)に対応していることを確認してください.
gccの場合はgdbを使っておっかけましょう.まず起動してgetCode()のところでブレークポイントを置きます.
>gdb ./a.out >b HelloGenerator.getCode Breakpoint 1 at 0x804b2b6: file xbyak/xbyak.h, line 1307.
実行した後,[n]と[Enter]でステップ実行し,code()の直前まで進みます.
(gdb) r Starting program: /home/shigeo/Program/xbyak/a.out Breakpoint 1, Xbyak::CodeGenerator::getCode (this=0xbffff2e0) at xbyak/xbyak.h:1307 1307 assert(!hasUndefinedLabel()); (gdb) n 1309 return top_; (gdb)([Enter]を押す) 1310 } ([Enter]を押す) (gdb) ([Enter]を押す) main () at t.cpp:19 19 code();
ここでコードがどうなっているか見ます.その前にデバッグに見やすいaliasを作っておきます.
(gdb) define al Type commands for definition of "al". End with a line saying just "end". >x/11i $pc >end
と定義してみてください.
(gdb) al 0x8048904 <main+52>: mov eax,DWORD PTR [ebp-12] 0x8048907 <main+55>: call eax 0x8048909 <main+57>: mov ebx,0x0 0x804890e <main+62>: lea eax,[ebp-0x108] 0x8048914 <main+68>: mov DWORD PTR [esp],eax 0x8048917 <main+71>: call 0x8049c2c <~HelloGenerator> 0x804891c <main+76>: mov DWORD PTR [ebp-0x10c],ebx 0x8048922 <main+82>: jmp 0x8048952 <main+130> 0x8048924 <main+84>: mov DWORD PTR [ebp-0x110],eax 0x804892a <main+90>: mov ebx,DWORD PTR [ebp-0x110] 0x8048930 <main+96>: lea eax,[ebp-0x108]
siを使ってasmレベルでのステップ実行をします.
(gdb) si 0x08048907 19 code(); (gdb) ([Enter]を押す) 0x0804c190 in ?? ()
call eaxが実行された瞬間からXbyakが生成したマシン語に突入しています.alで確認しましょう.
(gdb) al 0x804c194: push 0x804b44b 0x804c199: call 0x8048734 <puts@plt> 0x804c19e: add esp,0x4 0x804c1a1: ret
やはり上記サンプルの(C1)~(C4)に対応していることを確認してください.
うっかり行き過ぎたら,最初からやり直してマシン語を一行ずつ実行する感じをつかんでください.gdbを使うときはアセンブラで遊ぶ時に便利なgdb設定を参考にすると便利だと思います.上記alもこのリンク先の.gdbinitで定義されているのを拝借しました.
今回はXbyakの基本的な書き方と実行時に確認する方法の説明をしました.長くなりましたので一端区切ります.次はコードの説明に入ります.
コメント (2)
Intel Mac(Jan 2007) + Mac OS X 10.4.10 + Xcode Tools 2.4.1 の環境で試してみましたが、いくつかハマりましたので報告します。
1. サンプルのcalcのビルドで下記のエラーが発生。
calc.cpp:31:33: error: boost/spirit/core.hpp: No such file or directory
どうやら、別途ライブラリが必要な様子ですが、とりあえず無視しました。
2. alコマンドがない。
小一時間程調べて "x/11i $pc" のマクロらしい事は何となく分かりました。
差し障りがなければ、~/.gdbinit を公開して頂けると助かります。
3. 下記の行ですが、
(gdb)#([Enter]を押す)
1310 } ([Enter]を押す)
(gdb) ([Enter]を押す)
これは多分、
(gdb) ([Enter]を押す)
1310 }
(gdb) ([Enter]を押す)
ですよね?
以上、よろしくお願い致します。
投稿者: っき | 2007年10月01日 05:02
日時: 2007年10月01日 05:02
>別途ライブラリが必要な様子ですが
calc.cppのコンパイルにはboostライブラリ(http://www.boost.org/)が必要です.書き忘れて申し訳ありません.
>2. alコマンドがない。
こちらもすいません.本文後半にある「アセンブラで遊ぶ時に便利なgdb設定」に書かれている.gdbinitをお使いください.
3. そうです.
ご指摘どうもありがとうございました.
投稿者: へるみ | 2007年10月01日 10:14
日時: 2007年10月01日 10:14