RISC-V アセンブリを読む その2 関数呼び出し

今回は引数や返り値がわかりやすい関数呼び出しが行われているアセンブリを精査していく。

関数呼び出し

#include <stdio.h>

int add(int a, int b) {
    return a + b;
}

int main() {
    int c = 1;
    int d = 2;
    int e  = add(c, d);
    printf("%d", e);
    return 0;
}
000000008000226c <add>:
    8000226c:   1101                   addi sp,sp,-32
    8000226e:   ec22                 sd   s0,24(sp)
    80002270:  1000                   addi s0,sp,32
// 引数の値を一時レジスタに格納
    80002272:  87aa                    mv   a5,a0
    80002274:  872e                    mv   a4,a1
// とりあえずメモリに格納
    80002276:  fef42623             sw   a5,-20(s0)
    8000227a:   87ba                    mv   a5,a4
    8000227c:   fef42423             sw   a5,-24(s0)
// 結局以下のように使うからメモリに格納する必要はないように思う
    80002280:  fec42783             lw   a5,-20(s0)
    80002284:  873e                    mv   a4,a5
    80002286:  fe842783             lw   a5,-24(s0)
// (a + b)の計算
    8000228a:   9fb9                    addw a5,a5,a4
// signed extension 符号拡張
// 64bitレジスタを考えているから32bit整数を64bitに変更
    8000228c:   2781                   sext.w a5,a5
// 返り値をa0に格納
    8000228e:   853e                    mv   a0,a5
    80002290:  6462                   ld   s0,24(sp)
    80002292:  6105                   addi sp,sp,32
    80002294:  8082                   ret

0000000080002296 <main>:
    80002296:  1101                   addi sp,sp,-32
    80002298:  ec06                 sd   ra,24(sp)
// フレームポインタの退避
    8000229a:   e822                 sd   s0,16(sp)
// 新しいフレームポインタの更新
    8000229c:   1000                   addi s0,sp,32
    8000229e:   4785                   li   a5,1
    800022a0:   fef42623             sw   a5,-20(s0)
    800022a4:   4789                   li   a5,2
    800022a6:   fef42423             sw   a5,-24(s0)
    800022aa:   fe842703             lw   a4,-24(s0)
    800022ae:   fec42783             lw   a5,-20(s0)
// 以下二つは関数のための引数を用意している
// a0が第一引数、a1が第二引数
    800022b2:   85ba                    mv   a1,a4
    800022b4:   853e                    mv   a0,a5
    800022b6:   fb7ff0ef             jal  ra,8000226c <add>
// 返り値a0をa5に退避
    800022ba:   87aa                    mv   a5,a0
// 以下の二つの命令は意味がない。無駄なスピル。
    800022bc:   fef42223             sw   a5,-28(s0)
    800022c0:   fe442783             lw   a5,-28(s0)
    800022c4:   85be                    mv   a1,a5
    800022c6:   00000517           auipc    a0,0x0
    800022ca:   1ba50513            addi a0,a0,442 # 80002480 <main+0x1ea>
    800022ce:   a99ff0ef             jal  ra,80001d66 <printf>
// 以下二つの命令はreturn 0を作るため
    800022d2:   4781                   li   a5,0
    800022d4:   853e                    mv   a0,a5
// 退避させていたraの復帰
    800022d6:   60e2                    ld   ra,24(sp)
// 退避させていたフレームポインタの復帰
    800022d8:   6442                   ld   s0,16(sp)
// スタックポインタの値がもとに戻る
    800022da:   6105                   addi sp,sp,32
    800022dc:   8082                   ret

最適化された関数呼び出し

0000000080002378 <add>:
    80002378:  9d2d                    addw a0,a0,a1
    8000237a:   8082                   ret

Disassembly of section .text.startup:

000000008000237c <main-0x18>:
    8000237c:   1141                   addi sp,sp,-16
    8000237e:   00000517           auipc    a0,0x0
    80002382:  05250513           addi a0,a0,82 # 800023d0 <main+0x3c>
    80002386:  e406                 sd   ra,8(sp)
    80002388:  8a3ff0ef            jal  ra,80001c2a <printstr>
    8000238c:   60a2                    ld   ra,8(sp)
    8000238e:   557d                    li   a0,-1
    80002390:  0141                   addi sp,sp,16
    80002392:  8082                   ret

0000000080002394 <main>:
    80002394:  1141                   addi sp,sp,-16
    80002396:  458d                    li   a1,3
    80002398:  00000517           auipc    a0,0x0
    8000239c:   06050513           addi a0,a0,96 # 800023f8 <main+0x64>
    800023a0:   e406                 sd   ra,8(sp)
    800023a2:   c91ff0ef             jal  ra,80002032 <printf>
    800023a6:   60a2                    ld   ra,8(sp)
    800023a8:   4501                   li   a0,0
    800023aa:   0141                   addi sp,sp,16
    800023ac:   8082                   ret

非常に最適化されたいるのはいいのだが、<main-0x18>というラベルが出現している。これはなんなのか?

関数呼び出し(引数が9個以上)

引数が8個以下の時は、a0~a7のレジスタに引数として格納して関数を呼び出す。しかし引数が9個以上となると一時レジスタが足りなくなるので、代わりにスタックポインタを使うようになる。以下が引数が10個の場合。

#include <stdio.h>

int add(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j) {
    return a + b + c + d + e + f + g + h + i + j;
}

int main() {
    int a = 1;int b = 2;int c = 3;int d = 4;
    int e = 5;int f = 6;int g = 7;int h = 8;
    int i = 9;int j =10;
    int fuga  = add(a, b, c, d, e, f, g, h, i, j);
    printf("%d", fuga);
    return 0;
}
000000008000226c <add>:
    8000226c:   7179                   addi sp,sp,-48
    8000226e:   f422                 sd   s0,40(sp)
    80002270:  1800                   addi s0,sp,48
    80002272:  8eaa                    mv   t4,a0
    80002274:  8e2e                    mv   t3,a1
    80002276:  8332                   mv   t1,a2
    80002278:  8536                   mv   a0,a3
    8000227a:   85ba                    mv   a1,a4
    8000227c:   863e                    mv   a2,a5
    8000227e:   86c2                    mv   a3,a6
    80002280:  8746                   mv   a4,a7
    80002282:  87f6                    mv   a5,t4
    80002284:  fef42623             sw   a5,-20(s0)
    80002288:  87f2                    mv   a5,t3
    8000228a:   fef42423             sw   a5,-24(s0)
    8000228e:   879a                    mv   a5,t1
    80002290:  fef42223             sw   a5,-28(s0)
    80002294:  87aa                    mv   a5,a0
    80002296:  fef42023             sw   a5,-32(s0)
    8000229a:   87ae                    mv   a5,a1
    8000229c:   fcf42e23             sw   a5,-36(s0)
    800022a0:   87b2                    mv   a5,a2
    800022a2:   fcf42c23             sw   a5,-40(s0)
    800022a6:   87b6                    mv   a5,a3
    800022a8:   fcf42a23             sw   a5,-44(s0)
    800022ac:   87ba                    mv   a5,a4
    800022ae:   fcf42823             sw   a5,-48(s0)
    800022b2:   fec42783             lw   a5,-20(s0)
    800022b6:   873e                    mv   a4,a5
    800022b8:   fe842783             lw   a5,-24(s0)
    800022bc:   9fb9                    addw a5,a5,a4
    800022be:   2781                   sext.w a5,a5
    800022c0:   fe442703             lw   a4,-28(s0)
    800022c4:   9fb9                    addw a5,a5,a4
    800022c6:   2781                   sext.w a5,a5
    800022c8:   fe042703             lw   a4,-32(s0)
    800022cc:   9fb9                    addw a5,a5,a4
    800022ce:   2781                   sext.w a5,a5
    800022d0:   fdc42703             lw   a4,-36(s0)
    800022d4:   9fb9                    addw a5,a5,a4
    800022d6:   2781                   sext.w a5,a5
    800022d8:   fd842703             lw   a4,-40(s0)
    800022dc:   9fb9                    addw a5,a5,a4
    800022de:   2781                   sext.w a5,a5
    800022e0:   fd442703             lw   a4,-44(s0)
    800022e4:   9fb9                    addw a5,a5,a4
    800022e6:   2781                   sext.w a5,a5
    800022e8:   4018                   lw   a4,0(s0)
    800022ea:   9fb9                    addw a5,a5,a4
    800022ec:   2781                   sext.w a5,a5
    800022ee:   4418                   lw   a4,8(s0)
    800022f0:   9fb9                    addw a5,a5,a4
    800022f2:   2781                   sext.w a5,a5
    800022f4:   853e                    mv   a0,a5
    800022f6:   7422                   ld   s0,40(sp)
    800022f8:   6145                   addi sp,sp,48
    800022fa:   8082                   ret

00000000800022fc <main>:
// 以下四つの命令は基本的に毎回行われる
    800022fc:   715d                    addi sp,sp,-80
    800022fe:   e486                 sd   ra,72(sp)
    80002300:  e0a2                 sd   s0,64(sp)
    80002302:  0880                   addi s0,sp,80

    80002304:  4785                   li   a5,1
    80002306:  fef42623             sw   a5,-20(s0)
    8000230a:   4789                   li   a5,2
    8000230c:   fef42423             sw   a5,-24(s0)
    80002310:  478d                    li   a5,3
    80002312:  fef42223             sw   a5,-28(s0)
    80002316:  4791                   li   a5,4
    80002318:  fef42023             sw   a5,-32(s0)
    8000231c:   4795                   li   a5,5
    8000231e:   fcf42e23             sw   a5,-36(s0)
    80002322:  4799                   li   a5,6
    80002324:  fcf42c23             sw   a5,-40(s0)
    80002328:  479d                    li   a5,7
    8000232a:   fcf42a23             sw   a5,-44(s0)
    8000232e:   47a1                    li   a5,8
    80002330:  fcf42823             sw   a5,-48(s0)
    80002334:  47a5                    li   a5,9
    80002336:  fcf42623             sw   a5,-52(s0)
    8000233a:   47a9                    li   a5,10
    8000233c:   fcf42423             sw   a5,-56(s0)
    80002340:  fd042883             lw   a7,-48(s0)
    80002344:  fd442803             lw   a6,-44(s0)
    80002348:  fd842303             lw   t1,-40(s0)
    8000234c:   fdc42703             lw   a4,-36(s0)
    80002350:  fe042683             lw   a3,-32(s0)
    80002354:  fe442603             lw   a2,-28(s0)
    80002358:  fe842583             lw   a1,-24(s0)
    8000235c:   fec42503             lw   a0,-20(s0)
// ここで一時レジスタa5を使って引数をメモリ上のスタックに詰めていることがわかる
    80002360:  fc842783             lw   a5,-56(s0)
    80002364:  e43e                 sd   a5,8(sp)
    80002366:  fcc42783             lw   a5,-52(s0)
    8000236a:   e03e                 sd   a5,0(sp)
// 上の方でt1に入れていたのでa5に入れ直す
    8000236c:   879a                    mv   a5,t1
    8000236e:   effff0ef             jal  ra,8000226c <add>
    80002372:  87aa                    mv   a5,a0
    80002374:  fcf42223             sw   a5,-60(s0)
    80002378:  fc442783             lw   a5,-60(s0)
    8000237c:   85be                    mv   a1,a5
    8000237e:   00000517           auipc    a0,0x0
    80002382:  1ba50513            addi a0,a0,442 # 80002538 <main+0x23c>
    80002386:  9e1ff0ef            jal  ra,80001d66 <printf>
    8000238a:   4781                   li   a5,0
    8000238c:   853e                    mv   a0,a5
    8000238e:   60a6                    ld   ra,72(sp)
    80002390:  6406                   ld   s0,64(sp)
    80002392:  6161                   addi sp,sp,80
    80002394:  8082                   ret

これは最適化しなかったアセンブリなので、非常に無駄な命令が多くなってしまった。

次回は分岐命令。