title | tags | author | slide |
---|---|---|---|
xbyakを使ってみる |
C++ |
dameyodamedame |
false |
どうやらGoogle Open Source Peer Bonus programとやらで受賞されたらしいxbyakを使ってみました。Google社員さん以外の方を他薦することによりノミネートされたものの中から選ばれたみたい。今日SNSで流れて来て初めて知っただけですけど…
https://github.com/herumi/xbyak/tree/master
どうやら日本人の方が作られたもののようですね。結構昔からあって知る人ぞ知る的なJITアセンブラのようです。
amd64(x64)です。
#include <iostream>
#include "xbyak/xbyak.h"
struct AddFunc : public Xbyak::CodeGenerator
{
AddFunc(int y)
{
#ifdef XBYAK64_GCC
mov(rax, rdi);
#else // XBYAK64_WIN
mov(rax, rcx);
#endif
add(rax, y);
ret();
}
};
int main()
{
AddFunc a(3);
auto add3 = a.getCode<int (*)(int)>();
std::cout << "3 + 2 = " << add3(2) << std::endl;
return 0;
}
落ちてたサンプルコードを今風に64bit化して、VS/mingw/Linuxで動くようにしたものです。当然32bit(x86)だと動きません。
xbyakは関数オブジェクトっぽく実装されているようで、そのまま実行するのではなく、生成したコードの関数ポインタを取得して実行する感じの使い方のようですね(そのまま実行もあるかもしれないけど見てない)。コンストラクタで書かれたアセンブラ風のメソッドを呼び出すことによりコードを生成していくスタイルで、ユーザーが実装するコードはアーキテクチャや処理系にガチ依存する感じになっているみたいです。
実用上関数単位のコード生成しかないようで、calling conventionが分かっていないと使えないので注意が必要です。今回はレジスタ操作だけなのでスタックとかいじってませんが、一般的な関数のEntry/Exitコードはもう少し処理があります。
Linuxだけで動く原理を説明するコードを書いてみました。
#include <iostream>
#include <cstdint>
#include <unistd.h>
#include <sys/mman.h>
struct MyAddFunc {
// amd64 gcc only(extracted from xbyak)
uint8_t code[16] __attribute__ ((aligned (16))) = {0x48,0x89,0xf8,0x48,0x83,0xc0,0x03,0xc3};
void* getCode() {
size_t pageSize = sysconf(_SC_PAGESIZE);
size_t iaddr = reinterpret_cast<size_t>(code);
size_t roundAddr = iaddr & ~(pageSize - static_cast<size_t>(1));
mprotect(reinterpret_cast<void*>(roundAddr), sizeof(code) + (iaddr - roundAddr), PROT_READ|PROT_WRITE|PROT_EXEC) == 0;
return code;
}
};
int main()
{
MyAddFunc a;
auto add3_2 = reinterpret_cast<int(*)(int)>(a.getCode());
std::cout << "3 + 2 = " << add3_2(2) << std::endl;
return 0;
}
コード生成の他にNX/XDビット制御があるようです。インラインアセンブラとはこの辺が違っていて、本当に速いコードを生成して実行したい場合(例えばSQLのような言語を元に動的に生成されるロジックを高速実行したいなど)には重宝されそうですね。
ヘッダオンリーでここまでよくやるなぁというのが個人的な感想です。大事なことを言い忘れていました。 作者様、受賞おめでとうございます。