RISC-V アセンブリを読む その1 Hello World

前回の「RISC-V アセンブリを読む その0 - takumi9のブログ」でアセンブリを読むための環境を作ったので、これから実際に様々なRISC-Vアセンブリを読んでいこうと思う。前回の記事の通り、gccを用いてコンパイルした。

Hello, World

まずは一番基本的なコード。

#include <stdio.h>

int main() {
    printf("Hello, World");
    return 0;
}
000000008000226c <main>:
// まずはspを-16して必要な分のフレームサイズを確保
    8000226c:   1141                   addi sp,sp,-16
// 返り値を退避させる
    8000226e:   e406                 sd   ra,8(sp)
// 呼び出し側のフレームポインタを退避
// 呼び出し側に戻った時にフレームポインタを復帰できるように
    80002270:  e022                 sd   s0,0(sp)
// 呼び出され側のフレームポインタをs0に代入
// このフレームポインタがちょうど呼び出し側と呼び出され側の境に位置している
    80002272:  0800                   addi s0,sp,16
// pc相対で即値を作るものの0。mov a0 zeroとかで良くない?
// a0 = 80002274
    80002274:  00000517           auipc    a0,0x0
// a0 = 80002274+444(=1bc) = 80002430。次に呼ぶprintf関数の第一引数。
    80002278:  1bc50513            addi a0,a0,444 # 80002430 <main+0x1c4>
// 次のpcをraレジスタに退避させて戻って来れるようにする
// さらにpcを即値の80001d66に設定してprintfに飛ぶ。
    8000227c:   aebff0ef             jal  ra,80001d66 <printf>
// この後の二つの命令はreturn 0のための準備
    80002280:  4781                   li   a5,0
    80002282:  853e                    mv   a0,a5
// 前に退避させておいたmain関数の返り値を再びロードする。
    80002284:  60a2                    ld   ra,8(sp)
// 前に退避させておいたフレームポインタも再びロードする。
    80002286:  6402                   ld   s0,0(sp)
// spをもとに戻す。
    80002288:  0141                   addi sp,sp,16
    8000228a:   8082                   ret
  • スタックポインタは現在実行中の関数の低位アドレス側を指し、フレームポインタは高位アドレス側(呼び出し側に近い方)を指す。

今回アセンブリを眺めていて、無駄なアセンブリが多いと感じた。これは最適化オプションをつけずに以下のコマンドでコンパイルしていたからだと気づいた。ただ、アセンブリを色々調べるにあたっては冗長でむしろわかりやすくてよかった。次回からも最適化オプションをつけないものと最適化オプション-O3をつけたものの両方を見てみようと思う。

$ riscv64-unknown-elf-gcc -static -mcmodel=medany -fno-common -fno-builtin-printf -nostdlib -nostartfiles -lm -lgcc -T link.ld syscall.c crt.S test0.c -o test0

最適化したHello, World

0000000080002390 <main>:
    80002390:  1141                   addi sp,sp,-16
    80002392:  00000517           auipc    a0,0x0
    80002396:  05e50513            addi a0,a0,94 # 800023f0 <main+0x60>
    8000239a:   e406                 sd   ra,8(sp)
    8000239c:   c97ff0ef             jal  ra,80002032 <printf>
    800023a0:   60a2                    ld   ra,8(sp)
    800023a2:   4501                   li   a0,0
    800023a4:   0141                   addi sp,sp,16
    800023a6:   8082                   ret

非常に短く無駄なアセンブリはないスッキリしたものになった。

次回以降も別のRISC-Vアセンブリを精査していく。

参考サイト