ARM C呼び出し規約に保存するレジスタは何ですか?
-
06-07-2019 - |
質問
アームアセンブラーを最後にコーディングしてからしばらく経ちましたが、細部に少し錆びています。 C関数をアームから呼び出す場合、r0-r3とlrを保存するだけでいいのですか?
C関数が他のレジスタを使用する場合、それらをスタックに保存して復元する責任がありますか?言い換えると、コンパイラーはC関数に対してこれを行うためのコードを生成します。
たとえば、アセンブラー関数でr10を使用する場合、その値をスタックまたはメモリーにプッシュし、C呼び出し後にポップ/復元する必要はありませんか?
これはarm-eabi-gcc 4.3.0用です。
解決
コンパイルするプラットフォームの ABI に依存します。 Linuxには、2つのARM ABIがあります。古いものと新しいもの。 AFAIK、新しいもの(EABI)は実際にはARMのAAPCSです。現在、完全なEABI定義はこちらのARMにあります。情報センター。
- r0-r3 は引数およびスクラッチレジスタです。 r0-r1 は結果レジスタでもあります
- r4-r8 は呼び出し先保存レジスタです
- r9 は、呼び出し先保存レジスタである場合とそうでない場合があります(AAPCSの一部のバリアントでは、特殊レジスタです)
- r10-r11 は呼び出し先保存レジスタです
- r12-r15 は特殊レジスターです
呼び出し先保存レジスタは、呼び出し先が保存する必要があります(呼び出し元保存レジスタとは反対に、呼び出し元がレジスタを保存します)。したがって、これが使用しているABIである場合、他の関数を呼び出す前にr10を保存する必要はありません(他の関数がそれを保存する責任があります)。
編集:使用しているコンパイラに違いはありません。特にgccはいくつかの異なるABI向けに設定でき、コマンドラインで変更することもできます。それが生成するプロローグ/エピローグコードを見ると、それは便利ではありません。それは、各関数 に合わせて調整されているため、コンパイラはレジスタを保存する他の方法を使用できます(たとえば、関数)。
他のヒント
NEONレジスタに関する不足情報を追加するには:
AAPCS から&& #167; 5.1.1コアレジスタ:
- r0-r3 は引数およびスクラッチレジスタです。 r0-r1 は結果レジスタでもあります
- r4-r8 は呼び出し先保存レジスタです
- r9 は、呼び出し先保存レジスタである場合とそうでない場合があります(AAPCSの一部のバリアントでは、特殊レジスタです)
- r10-r11 は呼び出し先保存レジスタです
- r12-r15 は特殊レジスターです
AAPCSから§ 5.1.2.1 VFPレジスタの使用規則:
- s16– s31(d8– d15、q4– q7)を保持する必要があります
- s0– s15(d0– d7、q0– q3)および d16– d31(q8– q15)は保存する必要があります
64ビットARM、A64の場合(ARM 64ビットアーキテクチャのプロシージャコール標準より)
A64命令セットから見える31個の64ビット汎用(整数)レジスタがあります。これらには r0-r30 というラベルが付いています。 64ビットコンテキストでは、これらのレジスタは通常 x0-x30 という名前を使用して参照されます。 32ビットコンテキストでは、レジスタは w0-w30 を使用して指定されます。さらに、スタックポインターレジスタ SP は、限られた数の命令で使用できます。
- SP スタックポインター
- r30 LRリンクレジスタ
- r29 FPフレームポインター
- r19… r28 呼び出し先が保存したレジスタ
- r18 必要に応じてプラットフォーム登録。それ以外の場合は一時的な登録。
- r17 IP1 2番目のイントラプロシージャコール一時レジスタ(使用可能) 呼び出しベニアとPLTコードによる);他の時間に 一時的な登録。
- r16 IP0最初の手続き内呼び出しスクラッチレジスタ(呼び出しで使用可能) ベニアとPLTコード);他の時間に 一時的な登録。
- r9… r15 一時レジスタ
- r8 間接的な結果の場所レジスタ
- r0… r7 パラメータ/結果レジスタ
最初の8つのレジスタ r0-r7 は、引数値をサブルーチンに渡し、関数から結果値を返すために使用されます。また、ルーチン内で中間値を保持するためにも使用できます(ただし、一般的には、サブルーチン呼び出し間のみ)。
レジスタ r16(IP0)および r17(IP1)は、ルーチンとそれが呼び出すサブルーチンの間のスクラッチレジスタとしてリンカで使用できます。また、ルーチン内で使用して、サブルーチン呼び出し間の中間値を保持することもできます。
レジスタ r18 の役割はプラットフォーム固有です。プラットフォームABIが、プロシージャ間状態(スレッドコンテキストなど)を保持するために専用の汎用レジスタを必要とする場合、その目的でこのレジスタを使用する必要があります。プラットフォームABIにそのような要件がない場合は、追加の一時レジスタとしてr18を使用する必要があります。プラットフォームABI仕様では、このレジスタの使用法を文書化する必要があります。
SIMD
ARM 64ビットアーキテクチャには、さらに32個のレジスタ v0-v31 があり、SIMDおよび浮動小数点演算で使用できます。レジスタの正確な名前が変更され、アクセスのサイズが示されます。
注: AArch32とは異なり、AArch64では、SIMDおよび浮動小数点レジスタの128ビットおよび64ビットビューは、狭いビューで複数のレジスタとオーバーラップしません。そうq1 、d1およびs1はすべて、レジスタバンクの同じエントリを参照します。
最初の8つのレジスタ v0-v7 は、引数値をサブルーチンに渡し、関数から結果値を返すために使用されます。ルーチン内で中間値を保持するために使用することもできます(ただし、一般的にはサブルーチン呼び出し間のみ)。
Registers v8-v15 は、サブルーチン呼び出し間で呼び出し先によって保持される必要があります。残りのレジスタ( v0-v7、v16-v31 )を保存する必要はありません(または呼び出し元が保存する必要があります)。さらに、 v8-v15 に保存されている各値の下位64ビットのみを保持する必要があります。より大きな値を保持するのは呼び出し側の責任です。
CesarBとPavelの回答はAAPCSからの引用を提供しましたが、未解決の問題は残っています。呼び出し先はr9を保存しますか? r12はどうですか? r14はどうですか?さらに、回答は非常に一般的であり、要求されたarm-eabiツールチェーンに固有のものではありませんでした。以下は、どのレジスターが呼び出し先で保存され、どのレジスターが保存されていないかを調べるための実用的なアプローチです。
次のCコードには、レジスタr0〜r12およびr14の変更を要求するインラインアセンブリブロックが含まれています。コンパイラは、ABIで必要なレジスタを保存するコードを生成します。
void foo() {
asm volatile ( "nop" : : : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14");
}
コマンドラインを使用 arm-eabi-gcc-4.7 -O2 -S -o-foo.c
プラットフォームのスイッチ(たとえば、 -mcpu = arm7tdmi
)を追加します。
このコマンドは、生成されたアセンブリコードをSTDOUTに出力します。次のようになります:
foo:
stmfd sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr}
nop
ldmfd sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr}
bx lr
コンパイラが生成したコードはr4-r11を保存および復元することに注意してください。コンパイラーはr0-r3、r12を保存しません。 r14(別名lr)を復元することは、終了コードによって保存されたlrがr0に読み込まれ、「bx r0」 「bx lr」の代わりに。 -mcpu = arm7tdmi -mno-thumb-interwork
を追加するか、または -mcpu = cortex-m4 -mthumb
を使用して、次のようなわずかに異なるアセンブリコードを取得します。
foo:
stmfd sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr}
nop
ldmfd sp!, {r4, r5, r6, r7, r8, r9, sl, fp, pc}
再び、r4-r11が保存および復元されます。ただし、r14(別名lr)は復元されません。
要約するには:
- r0-r3は、呼び出し先に保存されていない
- r4-r11は呼び出し先に保存されます
- r12(エイリアスIP)は、呼び出し先に保存されていない
- r13(別名sp)は呼び出し先に保存されます
- r14(別名lr)は、呼び出し先が保存されていない
- r15(エイリアスpc)はプログラムカウンターであり、関数呼び出しの前にlrの値に設定されます
これは、少なくともarm-eabi-gccのデフォルトに対して保持されます。結果に影響する可能性のあるコマンドラインスイッチ(特に-mabiスイッチ)があります。
少なくとも関数呼び出しと割り込みのCortex M3アーキテクチャにも違いがあります。
割り込みが発生すると、R0-R3、R12、LR、PCが自動的にスタックにプッシュされ、IRQから自動的にPOPが返されます。 IRQルーチンで他のレジスタを使用する場合、手動でStackにプッシュ/ポップする必要があります。
この自動PUSHおよびPOPは、関数呼び出し(ジャンプ命令)のために作成されたとは思わない。慣例により、R0-R3は引数、結果、またはスクラッチレジスタとしてのみ使用できると示されている場合、関数を呼び出す前にそれらを保存する必要はありません。ただし、割り込みと同じように、他のすべてのCPUレジスタを関数で使用する場合は、それらを保存する必要があります。