Xbyak2.01に@@, @F, @Bを追加してみた(適当Ver)
誰かすでにやってそうですが調子こいて書いてみます。
MASMなど、一部のマクロアセンブラには、正式名称は知りませんが便利なラベルがあります。@@, @F, @Bの三つです。
@@: hoge hoge... jg @F(下の@@にジャンプ) hoge hoge... jnz @B(上の@@にジャンプ) @@:
こんな風に使います。@Fは次に出現する@@を参照し、@Bは一個前に出現する@@を参照します。この機能はifっぽいものを書くときにものすごく便利で、ラベルの名前を決める苦しみからプログラマを救ってくれます。本来の用途から考えるとXbyakにこれを実装するメリットはあんまり無いと思うんですが、お手軽にアセンブリ言語で遊べる環境をより快適にしようという理由で実装してみました。
方針:
まず@@が現れるたびに1ずつ増加するカウンタを持ちます。カウンタの初期値は0とします。そして、@@が現れたら@@の後ろにカウンタの数字をくっつけます。
@@: --> @@1: @@: --> @@2: @@: --> @@3: ...
こんな感じになります。Xbyak(2.01)では命令が上から順に処理されていくので、二つの@@の間ではカウンタは下のような感じになります。
@@: ; counter == n jmp @B ; counter == n jmp @F ; counter == n @@: ; counter == n + 1
これにより、
@B --> @@ + counter @F --> @@ + (counter + 1)
と言うラベルに変換してあげることで、あまりソースコードに手を加えることなく実装できるのではないかと考えました。
実装:(ものすごく効率を考えない実装になっとります。ご了承ください。)
Xbyakは非常に綺麗にかかれており、今回の実装は class Label を少しいじるだけで実現可能です。まず、int anonymousLabels_; というメンバを追加し、コンストラクタで初期化します。次に
static std::string convert(int n) { std::string l("@@"); for (; n != 0; n /= 26) { l += static_cast<char>(n % 26 + 'A'); } return l; }
こんなのを追加してあげます。@@の後ろにカウンタの数字nに対応する文字列をくっつけてあげる関数です。また、defineの先頭に
std::string l(label); if (l == "@@") { l = convert(++anonymousLabels_); label = l.c_str(); }
これを追加します。最後に、getAddressとaddUndefinedLabelの先頭に
std::string l(label); if (l == "@F") { l = convert(anonymousLabels_ + 1); label = l.c_str(); } else if (l == "@B") { l = convert(anonymousLabels_); label = l.c_str(); }
を追加して終了です。短く書こうと思えば色々手段はあるので、その辺は好きにやってください。
テスト:
// int (*)(const int*, int); class Sum : public Xbyak::CodeGenerator { public: Sum() { push(esi); mov(esi, ptr [esp + 4 + 4]); mov(ecx, ptr [esp + 4 + 8]); L("@@"); xor(eax, eax); jmp("@F"); inc(eax); L("@@"); L("@@"); add(eax, ptr [esi]); add(esi, 4); dec(ecx); jnz("@B"); L("@@"); pop(esi); ret(); } };
配列と要素数を渡すと和を計算する関数です。動作テストのため沢山ラベルを設置してみました。