RISC-Vの関数呼び出しと戻り
ジャンプ系の疑似命令
以下の関数呼び出しやジャンプ系の命令は jal と jalr に置き換えられる(riscv-spec の Chapter 25 RISC-V Assembly Programmer's Handbook を参照)
j offset ( Jump )
→ jal x0, offset
jal offset ( Jump and link )
→ jal x1, offset
jr rs ( Jump register )
→ jalr x0, 0(rs)
jalr rs ( Jump and link register )
→ jalr x1, 0(rs)
ret ( Return from subroutine )
→ jalr x0, 0(x1)
call offset ( Call far-away fubroutine )
各命令の使いどころ
どういう時にどの命令を使えばいい?
関数内のラベルにジャンプ
j offset
近場の関数にジャンプ
jal offset
遠くの関数にジャンプ
call offset
(使い分けがよく分かってないんだけど、同じモジュール内なら jal offset で別のモジュールなら call offset みたいな感じかな?)
関数からのリターン
ret または jr ra (どっちでも良さそう)
自作CPUでは jal と jalr だけ用意すれば良さげ
ジャンプ系の命令も最終的にはjalかjalrになるので、自作CPUではjalとjalrだけを用意すれば良さそう。
jal (Jump And Link)
J形式
code:疑似コード
rd = PC+4; PC += imm
code:使い方
// x1に戻り先のアドレスを保存して、labelへ飛ぶ
jal x1, label
jalr (Jump And Link Reg)
I形式
code:疑似コード
rd = PC+4; PC = rs + imm
code:使い方
// jalで関数を呼び出して、jalrで戻る
jalr x0, x1, 0
J形式とI形式のフォーマット
https://gyazo.com/b421eb85b24d600656563d853ee983cb
疑似命令が出力する機械語を確認
以下のコードをコンパイルした結果を objdump でのぞいてみる
code:foo.S
.text
.globl foo
foo:
ret
jr ra
jalr zero, ra, 0
code:objdump
$ riscv32-unknown-elf-gcc -c foo.S
$ riscv32-unknown-elf-objdump -D foo.o
foo.o: ファイル形式 elf32-littleriscv
セクション .text の逆アセンブル:
00000000 <foo>:
0: 00008067 ret
4: 00008067 ret
8: 00008067 ret
全部 00008067 になってる。逆アセンブル結果は ret になってるけど実態は jalr なんだろうな。
参考
第7回 RV32Iのアセンブラブログラミング
The RISC-V Instruction Set Manual Volume I: Unprivileged ISA (riscv-spec-20191213.pdf)