GAS_インラインアセンブラ

GAS(GNU アセンブラ)のインラインアセンブラ

インラインアセンブラとは、アセンブリ言語で記述されたコードをC言語のような高級言語のソースコードに埋め込む機能のことです。

gccを利用した場合、C言語とGASは相性よくインライン記述することが可能です。


インラインアセンブラの構文

インラインアセンブラは、以下の基本形でC言語ファイルにアセンブラコードを関数のように埋め込みます。


__asm__("asm code");

なお、「__asm__」ではなく「asm」と記述することも出来ますが、asmが予約語や関数名と衝突するしている場合には使用できない(C99ではasmキーワードが予約されています)ため、通常は「__asm__」を用います。


インライン構文は複数行に渡って書くことができます。

複数の命令を書く場合は、一行毎にダブルクオーテーションで括り、命令の後に「\n\t」または「;」を付加します。


void function{
    __asm__("movl $1, %eax\n\t"
            "addl $2, %eax\n\t");
    __asm__("movl $1, %eax;"
            "addl $2, %eax;");
}

volatileキーワード

volatileキーワードを用いることで、コンパイル時に最適化などでアセンブリ命令が大きく変更されることを防ぐことができます。

インラインアセンブラを記述する際は、以下の形式を用いることで予期しない動作を防ぐことができます。


__asm__ __volatile__("asm code");

GAS(GNU アセンブラ)の拡張インラインアセンブラ

拡張インラインアセンブラの構文

C言語の変数をオペランドとして指定をする場合には、拡張構文で記述します。


__asm__(アセンブリテンプレート
        : 出力オペランド
        : 入力オペランド
        : ワークレジスタ);
  • アセンブリテンプレート
    • アセンブリ命令を記述します。
    • eaxやebxなどのレジスタ名を使用する場合は、%%eaxや%%ebxのように「%%」を付けて使用します。
    • 入力/出力オペランドで指定したC言語変数を使用する場合は、左から指定した順に「%0, %1, %2,...」と表記します。
  • 出力オペランド
    • 修飾子付きオペランド制約を記述して、レジスタの値を指定されたC言語の変数へ書き込みます。
  • 入力オペランド
    • オペランド制約を記述して、C言語の変数をレジスタに読み込みます。
  • ワークレジスタ
    • アセンブリ命令を実行したときに、レジスタの値を破壊する可能性があるレジスタ名を列挙します。これにより、コンパイラはレジスタ破壊が起きないようにコンパイルします。

int main(void)
{
    int x = 5;
    int y = 0;

    __asm__("movl %1, %%eax;" /* 入力オペランド(x)をeaxに代入する */
            "movl %%eax, %0;" /* eaxの値を出力オペランド(y)に代入する */
            :"=r"(y)          /* 変数yを出力オペランドとする */
            :"r"(x)           /* 変数xを入力オペランドとする */
            :"%eax"           /* eaxレジスタは破壊されることを定義する */
    );

    printf("%d\n", y);
    return(0);
}

オペランド制約について

拡張アセンブリ構文では、C言語の変数はオペランドとして機能します。

オペランド制約の記述は以下の形式となります。


"オペランド制約" (C言語の変数)

制約に修飾子が付加されると、出力オペランドとなります。


"修飾子とオペランド制約" (C言語の変数)

オペランド制約と制約修飾子の指定文字についてはgccマニュアルに詳しい説明があります。

使用頻度が高いものを以下に記載します。


[オペランド制約の文字]

r eax, eax, ecx, edx, esi, ediの中から使用レジスタを自動で割り当てる。
q eax, ebx, ecx, edxの中から使用レジスタを自動で割り当てる。
a eaxレジスタを割り当てる。
b ebxレジスタを割り当てる。
c ecxレジスタを割り当てる。
d edxレジスタを割り当てる。
S esiレジスタを割り当てる。
D sdiレジスタを割り当てる。
A eaxとedxを割り当てて64bit(long long型)で使う。
g 汎用レジスタから自動で割り当てる。
f 浮動小数点レジスタの中から、自動で割り当てる。
m 直接メモリへアクセスしたい場合に指定する。
o メモリオペランドのうち、オフセット可能なアドレス。

[制約修飾子]

(無し) オペラントが読み込み専用であることを示す。
= オペランドが書き込み専用であることを示す。
+ オペランドが読み書き両用であることを示す。
& 早期破壊オペラント(命令が入力オペランドを使い終わる前に変更されるオペランド)であることを示す。



関連ページ