アセンブラからみた効率のよい戻り値の型

今回は戻り値の型でアセンブラがどう変わるか確認します。
まずは戻り値がchar型の場合。

takk@deb9:~/tmp$ cat -n t.c
     1  char func1(void)
     2  {
     3          return 100;
     4  }
     5
     6  int main()
     7  {
     8          char ret;
     9          ret = func1();
    10  }
takk@deb9:~/tmp$

func1の戻り値はEAXに格納されるようです。char型の戻り値であっても、32bitのレジスタが使用されるんですね。

0000000000000660 <func1>:
 660:   55                      push   %rbp
 661:   48 89 e5                mov    %rsp,%rbp
 664:   b8 64 00 00 00          mov    $0x64,%eax
 669:   5d                      pop    %rbp
 66a:   c3                      retq

000000000000066b <main>:
 66b:   55                      push   %rbp
 66c:   48 89 e5                mov    %rsp,%rbp
 66f:   48 83 ec 10             sub    $0x10,%rsp
 673:   e8 e8 ff ff ff          callq  660 <func1>
 678:   88 45 ff                mov    %al,-0x1(%rbp)
 67b:   b8 00 00 00 00          mov    $0x0,%eax
 680:   c9                      leaveq
 681:   c3                      retq
 682:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
 689:   00 00 00
 68c:   0f 1f 40 00             nopl   0x0(%rax)

main側で、ALレジスタを使用することで、32bitのEAXレジスタから効率的に8bitを取得してるんですね。
便利なレジスタですね。
ということは、16bitの戻り値になっても同じかと思います。

takk@deb9:~/tmp$ cat -n t.c
     1  short func1(void)
     2  {
     3          return 100;
     4  }
     5
     6  int main()
     7  {
     8          short ret;
     9          ret = func1();
    10  }
takk@deb9:~/tmp$
0000000000000660 <func1>:
 660:   55                      push   %rbp
 661:   48 89 e5                mov    %rsp,%rbp
 664:   b8 64 00 00 00          mov    $0x64,%eax
 669:   5d                      pop    %rbp
 66a:   c3                      retq

000000000000066b <main>:
 66b:   55                      push   %rbp
 66c:   48 89 e5                mov    %rsp,%rbp
 66f:   48 83 ec 10             sub    $0x10,%rsp
 673:   e8 e8 ff ff ff          callq  660 <func1>
 678:   66 89 45 fe             mov    %ax,-0x2(%rbp)
 67c:   b8 00 00 00 00          mov    $0x0,%eax
 681:   c9                      leaveq
 682:   c3                      retq
 683:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
 68a:   00 00 00
 68d:   0f 1f 00                nopl   (%rax)

やはり32bitのEAXからAXで16bitの戻り値のみが使用されています。

最後に戻り値longの場合。

takk@deb9:~/tmp$ cat -n t.c
     1  long func1(void)
     2  {
     3          return 100;
     4  }
     5
     6  int main()
     7  {
     8          long ret;
     9          ret = func1();
    10  }
takk@deb9:~/tmp$

結局func1関数は、戻り値が8/16/32bitどの場合でも同じコードでした。

0000000000000660 <func1>:
 660:   55                      push   %rbp
 661:   48 89 e5                mov    %rsp,%rbp
 664:   b8 64 00 00 00          mov    $0x64,%eax
 669:   5d                      pop    %rbp
 66a:   c3                      retq

000000000000066b <main>:
 66b:   55                      push   %rbp
 66c:   48 89 e5                mov    %rsp,%rbp
 66f:   48 83 ec 10             sub    $0x10,%rsp
 673:   e8 e8 ff ff ff          callq  660 <func1>
 678:   48 89 45 f8             mov    %rax,-0x8(%rbp)
 67c:   b8 00 00 00 00          mov    $0x0,%eax
 681:   c9                      leaveq
 682:   c3                      retq
 683:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
 68a:   00 00 00
 68d:   0f 1f 00                nopl   (%rax)

main関数の呼び出しの方は、コードは変わりますが、コードサイズが変わってません。
戻り値の方は32bitや64bitにこだわる必要もなさそうです。

コメント

タイトルとURLをコピーしました