🧸
RISC-VのCalling Convention
Calling Conventionとは
C ABIで関数を呼び出すときの規約です。主にCPUレジスタの使い方が示されています。
コンパイラツールチェインやライブラリはこれに沿うように作られています。
C以外の多くの言語でもC ABIでライブラリを呼び出す仕組みを備えています。
アセンブラ出力を見る時にはまずこのコーリングコンベンションを頭に入れておくとよいと思います。
こちらのドキュメントを元に書いています。この表もそこからの引用です。
x0(zero)は読んだらゼロ、書いたら無視されます。
x1(ra)は関数のリターンアドレス。
x2(sp)はスタックポインタ。
x3(gp)はグローバルポインタ。
x4(tp)はスレッドポインタ。
a0-a7は引数を渡すのに使われ、そのうちのa0, a1はリターン値の格納に使われます。
t0-t6はテンポラリレジスタ。関数内で自由に使うことができます。別の関数を呼んだ時には破壊されます。
s0-s11は保存レジスタ。関数を呼び出された側が保存します。
フロートのレジスタも同様で、
fa0-fa7は引数を渡すのに使われ、そのうちのfa0, fa1はリターン値の格納に使われます。
ft0-ft11はテンポラリレジスタ。
fs0-fs11は保存レジスタです。
zeroレジスタはハードウェア的にゼロに固定されています。
ra, sp, gp, tp はmain
関数が呼び出された時点でランタイムにより適切な値がセットされています。
ポインタサイズの2倍のサイズの値は2つの偶数奇数のレジスタのペアに格納します。[1]
それ以上のサイズの値は参照渡しにします。
スタックポインタは16バイトにアラインされていることを維持することがコーリングコンベンションで定められています。[2]
例
関数の入り口
addi sp, sp, -48 /* スタックポインタを16の倍数分ずらす */
sd s0, 32(sp) /* レジスタをスタックに保存 */
sd ra, 40(sp)
sd s1, 24(sp)
sd s2, 16(sp)
sd s3, 8(sp)
sd s4, 0(sp)
関数の出口
mv a0, zero /* リターン値のセット */
ld ra, 40(sp) /* スタックからレジスタの復帰 */
ld s0, 32(sp)
ld s1, 24(sp)
ld s2, 16(sp)
ld s3, 8(sp)
ld s4, 0(sp)
addi sp, sp, 48 /* スタックポインタを元に戻す */
ret /* jr ra と同じ。リターンアドレスにジャンプ */
参考
関連
Discussion