C++用x86(IA-32), x64(AMD64, x86-64) JITアセンブラ Xbyak version 3.70 ----------------------------------------------------------------------------- ◎概要 これはx86, x64(AMD64, x86-64)のマシン語命令を生成するC++のクラスライブラリです. プログラム実行時に動的にアセンブルすることが可能です. ----------------------------------------------------------------------------- ◎特徴 ・ヘッダファイルオンリー xbyak.hをインクルードするだけですぐ利用することができます. C++の枠組み内で閉じているため,外部アセンブラは不要です. 32bit/64bit両対応です. 対応ニーモニック:特権命令除くx86, MMX/MMX2/SSE/SSE2/SSE3/SSSE3/SSE4/FPU(一部)/AVX ・Windows Xp(32bit, 64bit), Vista/Linux(32bit, 64bit)/Intel Mac対応 Windows Xp上ではVC2005 Express Ed., VC2008, VC2010, Windows Vista Linux (kernel 2.4.32)上ではgcc 4.5.0, CentOS 5.1上ではgcc 4.1.2 Intel Mac などで動作確認をしています. ※ Xbyakはデフォルトでand(), or(), xor(), not()関数を使います. gccではそれらを演算子として解釈してしまうため,-fno-operator-namesオプションを追加してコンパイルしてください. あるいはXBYAK_NO_OP_NAMESを定義してand_(), or_(), xor_(), not_()を使ってください. and_(), or_(), xor_(), not_()はXBYAK_NO_OP_NAMESされていないときでも使えます. ----------------------------------------------------------------------------- ◎準備 xbyak.h xbyak_bin2hex.h xbyak_mnemonic.h これらを同一のパスに入れてインクルードパスに追加してください. Linuxではmake installで/usr/local/include/xbyakにコピーされます. ----------------------------------------------------------------------------- ◎新機能 AutoGrowモード追加 これはメモリ伸長を動的に行うモードです. 今まではXbyak::CodeGenerator()に渡したメモリサイズを超えると例外が発生して いましたが,このモードでは内部でメモリを再確保して伸長します. ただし,getCode()を呼び出す前にジャンプ命令のアドレス解決をするためにready() 関数を呼ぶ必要があります. 次のように使います. struct Code : Xbyak::CodeGenerator { Code() : Xbyak::CodeGenerator(, Xbyak::AutoGrow) { ... } }; Code c; c.ready(); // この呼び出しを忘れてはいけない 注意1. ready()を呼んで確定するまではgetCurr()で得たポインタは無効化されている 可能性があります.getSize()でoffsetを保持しておきready()のあとにgetCode()を 呼び出してからgetCode() + offsetで新しいポインタを取得してください. 注意2. AutoGrowモードでは64bitモードの相対アドレッシング[rip]は非サポートです. ----------------------------------------------------------------------------- ◎文法 Xbyak::CodeGenerator クラスを継承し,そのクラスメソッド内でx86, x64アセンブラを 記述します.そのメソッドを呼び出した後,getCode()メソッドを呼び出し,その戻 り値を自分が使いたい関数ポインタに変換して利用します.アセンブルエラーは例外 により通知されます(cf. main.cpp). ・基本的にnasmの命令で括弧をつければよいです. mov eax, ebx --> mov(eax, ebx); inc ecx inc(ecx); ret --> ret(); ・アドレッシング (ptr|dword|word|byte) [base + index * (1|2|4|8) + displacement] [rip + 32bit disp] ; x64 only という形で指定します.サイズを指定する必要がない限りptrを使えばよいです. セレクタはサポートしていません. mov eax, [ebx+ecx] --> mov (eax, ptr[ebx+ecx]); test byte [esp], 4 --> test (byte [esp], 4); (注意) dword, word, byteはstaticクラス変数です.従ってたとえばunsigned intの つもりでdwordをtypedefしないでください. ・AVX 大抵の3オペランド形式の命令はデスティネーションを省略した形で呼び出すことができます. FMAについては簡略表記を導入するか検討中です(アイデア募集中). vaddps(xmm1, xmm2, xmm3); // xmm1 <- xmm2 + xmm3 vaddps(xmm2, xmm3); // xmm2 <- xmm2 + xmm3 vaddps(xmm2, xmm3, ptr [rax]); // メモリアクセスはptrで vfmadd231pd(xmm1, xmm2, xmm3); // xmm1 <- (xmm2 * xmm3) + xmm1 ・ラベル L(文字列); で定義します.ジャンプするときはその文字列を指定します.後方参照も可能ですが, 相対アドレスが8ビットに収まらない場合はT_NEARをつけないと実行時に例外が発生 します. ・hasUndefinedLabel()を呼び出して真ならジャンプ先が存在しないことを示します. コードを見直してください. L("L1"); jmp ("L1"); jmp ("L2"); ... 少しの命令の場合. ... L("L2"); jmp ("L3", T_NEAR); ... 沢山の命令がある場合 ... L("L3"); <応用編> 1. MASMライクな@@, @f, @bをサポート L("@@"); // jmp("@b"); // jmp to jmp("@f"); // jmp to L("@@"); // jmp("@b"); // jmp to 2. ラベルの局所化 ピリオドで始まるラベルをinLocalLabel(), outLocalLabel()で挟むことで局所化できます. inLocalLabel(), outLocalLabel()は入れ子にすることができます. void func1() { inLocalLabel(); L(".lp"); // ; ローカルラベル ... jmp(".lp"); // jmpt to L("aaa"); // グローバルラベル outLocalLabel(); } void func2() { inLocalLabel(); L(".lp"); // ; ローカルラベル func1(); jmp(".lp"); // jmp to outLocalLabel(); } 上記サンプルではinLocalLabel(), outLocalLabel()が無いと, ".lp"ラベルの二重定義エラーになります. ・Xbyak::CodeGenerator()コンストラクタインタフェース @param maxSize [in] コード生成最大サイズ(デフォルト2048byte) @param userPtr [in] ユーザ指定メモリ CodeGenerator(size_t maxSize = DEFAULT_MAX_CODE_SIZE, void *userPtr = 0); デフォルトコードサイズは2048(=DEFAULT_MAX_CODE_SIZE)バイトです. それより大きなコードを生成する場合はCodeGenerator()のコンストラクタに指定してください. class Quantize : public Xbyak::CodeGenerator { public: Quantize() : CodeGenerator(8192) { } ... }; # 動的にしたほうがよいのだが実行属性の管理が面倒でやってません…. またユーザ指定メモリをコード生成最大サイズと共に指定すると,CodeGeneratorは 指定されたメモリ上にバイト列を生成します. サポート関数として指定されたアドレスの実行属性を変更するCodeArray::protect()と 与えられたポインタからアライメントされたポインタを取得するCodeArray::getAlignedAddress() も用意しました.詳細はsample/test0.cppのuse memory allocated by userを参考に してください. /** change exec permission of memory @param addr [in] buffer address @param size [in] buffer size @param canExec [in] true(enable to exec), false(disable to exec) @return true(success), false(failure) */ bool CodeArray::protect(const void *addr, size_t size, bool canExec); /** get aligned memory pointer */ uint8 *CodeArray::getAlignedAddress(uint8 *addr, size_t alignedSize = ALIGN_SIZE); その他詳細は各種サンプルを参照してください. ----------------------------------------------------------------------------- ◎マクロ 32bit環境上でコンパイルするとXBYAK32が,64bit環境上でコンパイルするとXBYAK64が 定義されます.さらに64bit環境上ではWindowsならXBYAK64_WIN,gcc上ではXBYAK64_GCC も定義されます. ----------------------------------------------------------------------------- ◎使用例 test0.cpp ; 簡単な例(x86, x64) quantize.cpp ; 割り算のJITアセンブルによる量子化の高速化(x86) calc.cpp ; 与えられた多項式をアセンブルして実行(x86, x64) boost(http://www.boost.org/)が必要 bf.cpp ; JIT Brainfuck(x86, x64) ----------------------------------------------------------------------------- ◎注意 MMX/SSE命令はほぼ全て実装されていますが,3D Now!命令や,一部の特殊な 命令は現時点では実装されていません.FPUの80bit浮動小数はサポートしていません. 何かご要望があればご連絡ください. ----------------------------------------------------------------------------- ◎ライセンス 修正された新しいBSDライセンスに従います. http://opensource.org/licenses/BSD-3-Clause sample/{echo,hello}.bfは http://www.kmonos.net/alang/etc/brainfuck.php から いただきました. ----------------------------------------------------------------------------- ◎履歴 2012/11/17 and_(), or_(), xor_(), not_()をXBYAK_NO_OP_NAMESが定義されていないときでも使えるようにした 2012/11/17 CodeGeneratorのeax, ecx, ptrなどのメンバ変数をstaticにし,const参照をXbyak::utilにも定義 2012/11/09 ver 3.70 and()をand_()にするためのマクロXBYAK_NO_OP_NAMESを追加(thanks to Mattias) 2012/11/01 ver 3.62 add fwait/fnwait/finit/fninit 2012/11/01 ver 3.61 add fldcw/fstcw 2012/05/03 ver 3.60 Allocatorクラスのインタフェースを変更 2012/03/23 ver 3.51 userPtrモードがバグったのを修正 2012/03/19 ver 3.50 AutoGrowモードサポート 2011/11/09 ver 3.05 rip相対の64bitサイズ以外の扱いのバグ修正 / movsxdサポート 2011/08/15 ver 3.04 add(dword [ebp-8], 0xda);などにおけるimm8の扱いのバグ修正(thanks to lolcat) 2011/06/16 ver 3.03 Macのgcc上での__GNUC_PREREQがミスってたのを修正(thanks to t_teruya) 2011/04/28 ver 3.02 Macのgcc上ではxgetbvをdisable 2011/03/24 ver 3.01 fix typo of OSXSAVE 2011/03/23 ver 3.00 vcmpeqpsなどを追加 2011/02/16 ver 2.994 beta add vmovq for 32-bit mode(I forgot it) 2011/02/16 ver 2.993 beta remove cvtReg to avoid thread unsafe 2011/02/10 ver 2.992 beta support one argument syntax for fadd like nasm 2011/02/07 ver 2.991 beta fix pextrw reg, xmm, imm(Thanks to Gabest) 2011/02/04 ver 2.99 beta support AVX 2010/12/08 ver 2.31 fix ptr [rip + 32bit offset], support rtdscp 2010/10/19 ver 2.30 support pclmulqdq, aesdec, aesdeclast, aesenc, aesenclast, aesimc, aeskeygenassist 2010/07/07 ver 2.29 fix call(