RV32Iの基本命令のJ形式を見ていきます。J形式はJAL, JALRの2つの命令があります。
どちらも関数呼び出しで使われる命令ですので、これも実際の関数の記述も見ながらどう使われているか見てみます。
以下のC言語の記述を考えましょう。
int h1(void ) {
return 10;
}
int h2(int a ) {
return a+10;
}
int(*ptr)(int ) =h2;/*関数ポインタ*/
int goo( ) {
int t;
t = h1();
return ptr(t);
}
ふつうの関数呼び出しと、関数ポインタによる関数呼び出しを使いました。
これをまた、IAR社のEmbedded Workbench for RISC-V(EWRISC-V)でコンパイルをしてみました。
その結果を以下に示します。
命令call20と命令callrは疑似命令です。
きちんと分析してみましょう。
CALL20命令は、命令フォーマットに従って分析するとJAL ra, 0x18となりました。
JAL命令は、実行するとPC=PC+即値で計算されるアドレスに変更する(ようするに、関数のアドレスにジャンプします)と同時に、戻りアドレスをレジスタraに保存します。
EWRISC-Vで動いているところを見てみます。左が命令call20(JAL)を実行前で、右側が実行後です。
命令CALL20(JAL)は0x2000 0188に配置されているので、実行するとPCは+0x18されて、
PCが0x2000 01a0になっているのが確認できます。
それと同時にレジスタraが0x2000 018Cになっています。
これは命令CALL20の次の命令ですから、関数呼び出しの戻り先アドレスとなります。
CALLR命令は、JALR ra, a1,0x0となりました。
JALR命令は、実行するとPC=a1+即値で計算されるアドレスに変更し、戻りアドレスをレジスタraに保存します。
これもEWRISC-Vで動かしながら確認してみましょう。
命令callrがアドレス0x20000190にあります。
命令callr(JALR)の直前にレジスタa1の値が0x20000178になっています。
これにより本命令を実行するとPC=a1+即値となりますので、今回は即値が0x0ですので、PC=0x20000178です。
右側の画面で確認すると、確かにPC=0x20000178となっており、かつ、レジスタraが0x20000194になっています。
これで、関数実行後にきちんと戻るところに設定されていることが分かります。
折角なので、命令retも見ておきます。
RISC-Vのお約束でretも疑似命令です。解析すると以下のようになります。
この時、戻りアドレスをzeroレジスタに指定する形になっていますが、zeroレジスタは書いても無視します。
そのため、戻りレジスタ(ra)+即値のアドレスに戻る(ジャンプ)するだけになります。
関数の戻りだったら、それで十分な動きですね。
すこし、細かい点まで見ていきましたが、RISC-Vは疑似命令を使ってアセンブラが記述されることも多く、
最初は戸惑うかもしれませんが、こうした例を見ておくと実際のケースでも安心です。