bamboo’s blog

Bambooの気まぐれブログ

GCCインラインアセンブラの使い方

 インラインアセンブラとは、ソースコード内にアセンブリコードを埋め込むことができるというコンパイラの機能である。本記事では、GCCにおけるインラインアセンブラの扱い方について解説する。

書き方

 インラインアセンブラは、asmまたは__asm__から始め、()の中にアセンブリコードを記述する。記述方法は以下の通り。

asm(
    "" // アセンブリコード
    : // 出力オペランド(オプション)
    : // 入力オペランド(オプション)
    : // 上書きレジスタ(オプション)
)

アセンブリコード

アセンブリコードを記述する。複数のコードを記述する場合はセミコロン;で区切る。

// 例
asm(
    "mov r0, #1;"
    "mov r1, #2"
)

入出力オペランド

 出力オペランドを指定すると、C言語の変数に値を渡すことができる。入力オペランドを指定すると、C言語の変数から値を受け取ることができる。記述方法はどちらも同じである。複数記述する場合はコンマ,で区切る。

asm(
    "" // アセンブリコード
    : [マクロ名] "オペランド制約" (変数名)
)

 オペランド制約では値をどのように扱うかを決定する。出力オペランドには追加で制約修飾子を指定する。主に使用されるのは以下の通り。

オペランド制約 意味
r 汎用レジスタ(r0~r15)
I 即値
m メモリアドレス
制約修飾子 意味
= 書き込みのみ
+ 読み書き可

上書きレジスタ

 インラインアセンブラを使うと任意の場所でレジスタを上書きできるが、そのまま上書きすると予期せぬ動作を起こす可能性がある。レジスタを上書きする場合は、このセクションであらかじめ上書きするレジスタを指定しておくと良い。

コード例

 以上を踏まえて、インラインアセンブラを使用した演算例を紹介。

#include <stdio.h>

int main(void) {

    int a = 1, b = 2, sum;

    asm(
        "add %[Rd], %[Rs1], %[Rs2]" // Rd = Rs1 + Rs2
        : [Rd] "=r" (sum) // Rdをsumに出力
        : [Rs1] "r" (a), [Rs2] "r" (b) // aをRs1に、bをRs2に入力
    );

    printf("%d + %d = %d\n", a, b, sum); // a + b = sum

    return 0;
}

 基本的な処理はCで記述しているが、演算処理はアセンブリコードで記述している。これをARM用にビルドし実行すると1 + 2 = 3と表示されるはずだ。上記の例ではレジスタを使用していないが、レジスタを使用する場合は上書きレジスタを指定するのを忘れずに。