XbyakをRustから使ってみた

# はじめに

[Xbyak](https://github.com/herumi/xbyak)はC++で書かれたJITアセンブラです。Xbyak自体は「そういうアセンブラがあるらしい」程度にしか知らなかったのですが、"[JITアセンブラXbyakを使ってみる（その１）](https://qiita.com/kaityo256/items/a9e6d32f20096d791817)"を読んで「JITである（つまりプログラム実行時に動的にアセンブリを生成・実行する）」というのを知り、面白そうなので使ってみようと思いました。
とはいえここ数年Rustばかり書いていて、C++を書くのは億劫になってしまったためRustから使う方法を考えてみました。
本当はいい感じのインターフェースができればライブラリにして公開するつもりだったのですが、いまいちいい感じのが思いつかなかったため、作業履歴だけ残しておきます。どなたかが良いインターフェースを思いついてくれる（あわよくばコードも書いてくれる）ことに期待します。

# 方針

XbyakのコードはC++なので、別途.cppファイルに書いてC ABIを通してリンクすればいいのですが、やはりインラインアセンブラ的に書きたいです。したがって手続きマクロを使ってRustのコード中にXbyakのC++コードを埋め込みます。
すなわち

```rust
use xbyak_rs::xbyak;

xbyak! {
    struct Code : Xbyak::CodeGenerator {
        Code(int x)
        {
            mov(eax, x);
            ret();
        }
    };

    extern "C" int run(int n) {
        Code c(n);
        int (*f)() = c.getCode<int (*)()>();
        return f();
    }
}
```

のようなマクロ`xbyak!`を用意して、このコードをコンパイル・リンクすればよいということになります。
（このマクロの中身は[https://github.com/herumi/xbyak#how-to-use-it](https://github.com/herumi/xbyak#how-to-use-it)のサンプルコードと同じなので、コードの説明はそちらをご参照ください。）

# 手続きマクロ

`xbyak!`マクロの実装は以下のような感じになります。

```rust
use proc_macro::TokenStream;
use std::env;
use std::fs;
use std::path::Path;
use std::str::FromStr;

static XBYAK_H: &'static str = include_str!("xbyak/xbyak.h");
static XBYAK_BIN2HEX_H: &'static str = include_str!("./xbyak/xbyak_bin2hex.h");
static XBYAK_MNEMONIC_H: &'static str = include_str!("./xbyak/xbyak_mnemonic.h");
static XBYAK_UTIL_H: &'static str = include_str!("./xbyak/xbyak_util.h");
static XBYAK_COPYRIGHT: &'static str = include_str!("./xbyak/COPYRIGHT");

#[proc_macro]
pub fn xbyak(input: TokenStream) -> TokenStream {
    let out_dir = env::var_os("OUT_DIR").unwrap();

    let xbyak_path = Path::new(&out_dir).join("xbyak.h");
    fs::write(&xbyak_path, XBYAK_H).unwrap();

    let xbyak_path = Path::new(&out_dir).join("xbyak_bin2hex.h");
    fs::write(&xbyak_path, XBYAK_BIN2HEX_H).unwrap();

    let xbyak_path = Path::new(&out_dir).join("xbyak_mnemonic.h");
    fs::write(&xbyak_path, XBYAK_MNEMONIC_H).unwrap();

    let xbyak_path = Path::new(&out_dir).join("xbyak_util.h");
    fs::write(&xbyak_path, XBYAK_UTIL_H).unwrap();

    let xbyak_path = Path::new(&out_dir).join("COPYRIGHT");
    fs::write(&xbyak_path, XBYAK_COPYRIGHT).unwrap();

    let mut code = String::from("#include <xbyak.h>\n");
    code.push_str(&input.to_string());
    let dest_path = Path::new(&out_dir).join("xbyak_code.cpp");
    fs::write(&dest_path, code).unwrap();

    cc::Build::new()
        .cpp(true)
        .file(&dest_path)
        .include(&out_dir)
        .compile("xbyak_code");

    FromStr::from_str(&"").unwrap()
}
```

Xbyakの本体であるヘッダファイル群は手続きマクロに`include_str`で取り込み、手続きマクロの実行時に`OUT_DIR`に展開します。さらに、`xbyak!`マクロの中身を、先頭に`#include <xbyak.h>`を付与して書き出します。
書き出した`xbyak_code.cpp`を`cc`クレートを使ってコンパイルしています。

# 使い方

使う際にはまず`build.rs`が必要です。

```rust:build.rs
fn main() {
    println!(
        "cargo:rustc-env=OUT_DIR={}",
        std::env::var("OUT_DIR").unwrap()
    );
    println!(
        "cargo:rustc-env=TARGET={}",
        std::env::var("TARGET").unwrap()
    );
    println!(
        "cargo:rustc-env=OPT_LEVEL={}",
        std::env::var("OPT_LEVEL").unwrap()
    );
    println!("cargo:rustc-env=HOST={}", std::env::var("HOST").unwrap());

    println!("cargo:rustc-link-lib=static=xbyak_code");
    println!(
        "cargo:rustc-link-search=native={}",
        std::env::var("OUT_DIR").unwrap()
    );
    println!("cargo:rustc-link-lib=stdc++");
}
```

このあたりはライブラリ化するなら`xbyak_rs::build()`のような感じでまとめる必要があるでしょう。
最初の4つの`println!`は手続きマクロ内で`cc`によるコンパイルをするために使います。これらの環境変数は`build.rs`でのみ提供されており、手続きマクロからアクセスすることはできません。`cc`は`build.rs`で使われる想定なのでこれらの環境変数が必須であり、ここで`rustc`に渡しています。
後半の3つの`println!`は手続きマクロで生成したCのライブラリをリンクするためのオプションです。`build.rs`にて`cc`を使ってコンパイルするとこのあたりは自動でやってくれるのですが、手続きマクロ内で使用した場合はそれが効かないので手動でやる必要があります。

これを用意すれば以下のようなコードが通るようになります。

```rust
use xbyak_rs::xbyak;

xbyak! {
    struct Code : Xbyak::CodeGenerator {
        Code(int x)
        {
            mov(eax, x);
            ret();
        }
    };

    extern "C" int run(int n) {
        Code c(n);
        int (*f)() = c.getCode<int (*)()>();
        return f();
    }
}

extern "C" {
    fn run(n: i32) -> i32;
}

fn main() {
    unsafe {
        println!("{}", run(45));
    }
}
```

# Rust的なインターフェースを考える

とりあえず動作確認はできたのでRust的にどんな感じのインターフェースがいいのかを考えます。
少なくとも2か所の`extern "C"`は手続きマクロによる自動生成であって欲しいです。
コードジェネレータのオブジェクト`Code`はRustでも`struct`で良さそうです。
すなわちこんなイメージでしょうか。

```rust
xbyak! {
    struct Code : Xbyak::CodeGenerator {
        Code(int x)
        {
            mov(eax, x);
            ret();
        }
    };
}

// xbyak!マクロによって以下が生成される
// struct Code {
//     fn new(i32) -> Self {
//         ...
//     }
//     fn run(&self) -> i32 {
//         ...
//     }
// }

fn main() {
    let code = Code::new(45);
    unsafe {
        println!("{}", code.run());
    }
}
```

問題は`Code`の`run`の引数と戻り値です。これはC++のコード上は`c.getCode<int (*)()>();`の型引数`int (*)()`に対応するもので、この場合は引数なしで戻り値が`int`になります。
なので`run`の関数プロトタイプをRustで宣言し、それを解析してC++の型引数を生成する感じになると思うのですが、どう宣言させるのがいいか…というところです。