3.1.8.5. インラインアセンブラ¶
C/C++では、ソースプログラムに記述された特定のアセンブラコードを、asm文によりコンパイラの出力コードに埋め込むことができます。
3.1.8.5.1. 注意事項¶
インラインアセンブラ(asm文)の利用に当たり、注意事項を示します。
asm 文(インラインアセンブラ)には、一般形式とGNU拡張形式の2種類があります。 一般形式とはJISの C言語規格「The asm keyword」および、C++言語規格「asm宣言」で定義された形式です。
コンパイラが生成したアセンブルファイルは GNU assembler でアセンブルされます。文法形式、利用できる疑似命令については GNU assembler のマニュアルを参照してください。
コンパイラは asm 文の内容の正しさに対して検査をしません。次の各項目に対して誤りがないかどうかは、利用者自身での確認が必要となります。
命令のニモニックのスペル
命令で使用できるレジスタの制限やメモリアドレスの指定
通常ペアになって使用される疑似命令の対応
ラベル名などのシンボルの重複
ABIへの準拠(レジスタの退避・復元、スタック利用のコンベンション等)
制御の流れを変える命令(call命令、ret 命令、分岐命令等)の後ろの delay slot は明示的に他の命令によって埋められる必要があります。
3.1.8.5.2. コンパイル¶
[_LNlogin]$ fccpx sample_asm.c (C言語) [_LNlogin]$ FCCpx sample_asm.cc (C++)
C/C++コンパイラでは、-S
オプションを指定することで、アセンブラコードを出力することができます。
[_LNlogin]$ fccpx -S sample_asm.c (C言語) [_LNlogin]$ FCCpx -S sample_asm.cc (C++)注意
C/C++コンパイラでは、asm文のアセンブラ命令と、コンパイラが出力するアセンブラ命令について、整合性の確認は行われません。asm文は利用者の責任で利用頂く必要があります。
3.1.8.5.3. asm文(一般形式)¶
C/C++では、asm 文を使用することで任意のアセンブラ命令をコンパイラが出力するアセンブラコードに出力することができます。一般形式では、asm 文で指定されたアセンブラ命令は、一切加工されずそのままの形で、アセンブラコマンドに渡されます。
asm("アセンブラ命令");
3.1.8.5.4. asm文(GNU拡張形式)¶
asm文中のアセンブラ命令ではオペランドをC言語の式で記述することができます。C言語の式で記述することにより、該当するデータの実際のハードウェア上での場所(レジスタまたはメモリ位置)を指定する必要はありません。asm文の記述方式を示します。
asm("アセンブリテンプレート" : 出力オペランド : 入力オペランド : 破壊レジスタ );
asmは __asm__ と記述することが可能です。
__asm__("アセンブリテンプレート" : 出力オペランド : 入力オペランド : 破壊レジスタ );
各レジスタ表記は文字列データとして記述し、以下のレジスタの表と同じレジスタ表記を記述します。
出力オペランドおよび入力オペランドは、1個のオペランド記述、または2個以上のオペランド記述をコンマ(" , ")でつないで記述します。
オペランドには出力オペランド、入力オペランドを通して 0から始まる番号(最大10個)が振られます。番号はアセンブラ命令の記述の中で、" %n "(nは1桁のオペランドの番号)の形式でオペランドの位置を指定するのに使用されます。
出力オペランドを持たないアセンブラ命令の場合は、アセンブラ命令の記述と入力オペランド記述の間にコロン(" : ")を2個続けて記述します。
アセンブリテンプレートにおいて、複数行のasm文を一度に記述するには、改行文字(\n)とタブ文字(\t)を挿入します。
3.1.8.5.5. 入力/出力オペランド¶
出力オペランド/入力オペランドは、シンボルオペランド名、オペランド制約文字列、オペランドから構成されます。
[ シンボルオペランド名 ] "オペランド制約文字列" (オペランド)
アセンブラ命令の記述の中で、オペランドの位置を指定するのにはシンボルオペランドを使用する方法があります。シンボルオペランド名はアセンブラ命令中でシンボルオペランドを使用する場合にのみ必要です。アセンブラ命令中の表記は、`%'`[' シンボルオペランド名 `]'となります。
( )内のオペランド部分に C/C++の式を記述します。
シンボルオペランド名は asm文の外側のC/C++プログラム中の識別子名と関連がなく、既に使われている変数名と同じものを使用しても、同じ実体を指しているとは見なされません。
入力オペランド式の型がアセンブラ命令のオペランドに要求される型として適当であることは利用者の責任になります。
3.1.8.5.6. 制約文字¶
C/C++コンパイラが受け入れる制約文字を以下の表に示します。
制約文字
説明
X
任意のオペランドを表します。
g
任意の汎用レジスタ、メモリ、定数のいずれかを表します。
0,1,2,3,4,5,6,7,8,9
数値と同じ番号を持つ、既に指定されたオペランドと同じオペランドを表します。
[
シンボルオペランド名の開始を表します。
r
汎用レジスタを表します。
f
浮動小数点レジスタの下位32bitを表わします。
m
メモリを表します。
p
有効なメモリアドレスを表します。ロードアドレス命令やプッシュアドレス命令で使われます。o
オフセットテーブルであるようなメモリを表します。
V
オフセットテーブルでないメモリを表します。
i
整数定数を表します。定数値はアセンブル時に確定していなければなりません。
s
コンパイル時には不明な整数定数を表します。
E
浮動小数点定数を表します。ターゲットマシンとコンパイラを実行するマシンの浮動小数点フォーマットが一致している時に使用できます。F
浮動小数点定数を表します。
J
ゼロを表します。
付加情報
説明
=
出力のみのオペランドであることを意味します。
+
更新される(入力と出力が両方行われる)オペランドであることを意味します。
&
入力オペランドとして使用されますが、命令群が終了するまでに更新されるオペランドであることを意味します。
3.1.8.5.7. アセンブラコード上での名前を指定する方法¶
C/C++のソース上の変数および関数を、アセンブラ出力する際に別の名前する場合は、宣言子の直後にasm記述をします。#pragma redefine_extname
指令は、当記述方法と同じ意味となります。
int foo asm("myfoo") = 2;
3.1.8.5.8. 特定のレジスタを指定する方法¶
C/C++のソース上の変数を特定のハードウェアレジスタに割り当てる為には、変数の定義時に、記憶域クラス指示「register」を指定します。
register int foo asm("g2");注意
asm記述中に指定する名前は、以下の表に示すレジスタ名と同じにすることはできません。
アセンブラの仕様でアセンブラ命令、擬似命令や特殊レジスタ名として指定されている名前を指定することはできません。
3.1.8.5.9. 指定例(GNU拡張形式)¶
GNU拡張形式の指定例を示します。
- 例1(入力/出力オペランド)入力オペランドと出力オペランドを持つアセンブラ命令の例を示します。
int asm_in1=5, asm_in2=10; int asm_out1, asm_out2; asm ( "asm_op %0,%2\n\tasm_op %1,%3" // assembler template : "=r" (asm_out1) , "=r" (asm_out2) // output operands : "r" (asm_in1) , "r" (asm_in2)); // input operandsオペランドには出力オペランド、入力オペランドを通して 0から始まる番号(最大10個)が振られます。番号はアセンブラ命令の記述の中で、" %n "(nは1桁のオペランドの番号)の形式でオペランドの位置を指定するのに使用されます。
入力オペランド "r" (asm_in1) → %0
入力オペランド "r" (asm_in2) → %1
出力オペランド "=r" (asm_out1) → %2
出力オペランド "=r" (asm_out2) → %3
- 例2(出力オペランドなし)出力オペランドを持たないアセンブラ命令の場合は、アセンブラ命令の記述と入力オペランド記述の間にコロン(" : ")が2個続けて並ぶことになります。
int asm_in1=5, asm_in2=10; asm ( "asm_op %0\n\tasm_op %1" // assembler template : // output operands : "r" (asm_in1) , "r" (asm_in2)); // input operands
- 例3(volatile)asmの直後にvolatileを記述することにより、アセンブラ命令が重大な副作用を持つことをC/C++コンパイラに指示することができます。
int asm_out; asm volatile ("asm_op %0" : "=r" (asm_out) );