インラインアセンブラ記法

毎回 忘れているので、メモ。ちなみにrust大先生もマクロで大体似たような記法にしていて、いつものようにドキュメントが充実しているのでこちらを見るというのもアリ、なのである。

基本的な記法

clang, gccとも同じ。clangが意識して似せているらしい。文法は、

__asm__(
    "アセンブリ"
    : /*アウトプットオペランド*/
    : /*インプットオペランド*/
    : /*破壊されるレジスタ*/
    : /*ラベル*/
);

なおインプットオペランド以降は省略可能。

コンパイラの最適化を避けるために

__asm__ volatile (...)

とすることもできる。なお、コンパイラは基本的にアセンブリを知らずにoutput/inputオペランドの振る舞いに基づいて最適化を行うらしく、このようにvolatileをつけないと壊れるパターンがあるようだ。

さて、細かく見ていこう。

アセンブリ

ええっと、本当は特に何も言う必要はないのだけど、x86/amd64の場合、AT&T記法を使うと%をエスケープせねばならず厄介なことになる。そのためIntel記法を使うのが無難。なお、これも100万回くらい忘れているが、Intel記法は基本的に

命令 書き込み先, 書き込み元

というような感じである。

mov rax, QWORD PTR [rbp - 0x10]

みたいな。命令にもなんかファンシーなfとかlとかが末尾についたりしない。

コンパイルは、gccなら

gcc -masm=intel hoge.c

とする。

(AT&T記法を使う場面を見たことがないんだけど、誰が使っているのだろう…?)

アウトプットオペランド

ここからがめんどくさいやつ。文法は

[[アセンブリ中のシンボル名]] オペランド制約 (C中での変数名)

となる。これをカンマ区切りで書いていく。つまり、C中の変数に「アセンブリ中のシンボル名」あるいは0始まりの%0, %1, ...みたいなシンボル名の、アセンブリ中で使えるエイリアスを結びつけるのがこの部分の目的。

オペランド制約は、どうやってCの変数を結びつけるかを指定する。まず、お約束としてアウトプットオペランドでは、制約は=(書き込みオンリー)あるいは+(読み書き)から始まる必要がある。続いてどうやってCの変数をアセンブリに結びつけるかを指定する。つまり、レジスタ経由かメモリ経由かということ。レジスタならr,メモリならmとする。x86/amd64なら、aとかすればraxに、みたいなこともできるみたい。詳しくはここ

インプットオペランド

アウトプットと同じ。定数に関しては即値iが使える。

ラベル

goto用に作ったラベルに、jmpすることができる。オペランドと異なり、アセンブリ中で参照する場合%l1のようにlをつける必要がある。これを使えばripとか取れそう。gcc拡張でできるという説もある。

Comments

comments powered by Disqus