C言語の関数の引数ですが、どんな型にすると効率が良いのでしょう。
ALレジスタを見ると、8bitアクセスなので、一見効率がよさそうに見えます。
+-------+-------+-------+-------+-------+-------+-------+-------+ | RAX(64) | +-------+-------+-------+-------+-------+-------+-------+-------+ | | EAX(32) | +-------+-------+-------+-------+-------+-------+-------+-------+ | | AX(16) | +-------+-------+-------+-------+-------+-------+-------+-------+ | | AL(8) | AL(8) | +-------+-------+-------+-------+-------+-------+-------+-------+
引数なし、8bit、16bit、32bit、64bitの型の引数の関数を呼び出しした時に、アセンブラがどのように変化するか確認してみます。
引数なしの場合。
takk@deb9:~/tmp$ cat -n t.c 1 void func1() 2 { 3 4 } 5 6 int main() 7 { 8 func1(); 9 } takk@deb9:~/tmp$
takk@deb9:~/tmp$ gcc t.c takk@deb9:~/tmp$ objdump -d a.out
引数なしだと、アセンブラはとてもスッキリしています。
関数に何も書かないと、nopが入るのですね。
0000000000000660 <func1>: 660: 55 push %rbp 661: 48 89 e5 mov %rsp,%rbp 664: 90 nop 665: 5d pop %rbp 666: c3 retq 0000000000000667 <main>: 667: 55 push %rbp 668: 48 89 e5 mov %rsp,%rbp 66b: b8 00 00 00 00 mov $0x0,%eax 670: e8 eb ff ff ff callq 660 <func1> 675: b8 00 00 00 00 mov $0x0,%eax 67a: 5d pop %rbp 67b: c3 retq 67c: 0f 1f 40 00 nopl 0x0(%rax)
次は8bitの引数の場合。char型を使ってみます。
takk@deb9:~/tmp$ cat -n t.c 1 void func1(char aaa) 2 { 3 4 } 5 6 int main() 7 { 8 func1(100); 9 } takk@deb9:~/tmp$
アセンブラです。8bitなので、ALレジスタが使用されています(666行目)
0000000000000660 <func1>: 660: 55 push %rbp 661: 48 89 e5 mov %rsp,%rbp 664: 89 f8 mov %edi,%eax 666: 88 45 fc mov %al,-0x4(%rbp) 669: 90 nop 66a: 5d pop %rbp 66b: c3 retq 000000000000066c <main>: 66c: 55 push %rbp 66d: 48 89 e5 mov %rsp,%rbp 670: bf 64 00 00 00 mov $0x64,%edi 675: e8 e6 ff ff ff callq 660 <func1> 67a: b8 00 00 00 00 mov $0x0,%eax 67f: 5d pop %rbp 680: c3 retq 681: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1) 688: 00 00 00 68b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
呼び出し側はなんだか複雑ですね。
次は16bit。shortの引数にします。
takk@deb9:~/tmp$ cat -n t.c 1 void func1(short aaa) 2 { 3 4 } 5 6 int main() 7 { 8 func1(100); 9 } takk@deb9:~/tmp$
ALレジスタがAXに変わっただけのようです。
0000000000000660 <func1>: 660: 55 push %rbp 661: 48 89 e5 mov %rsp,%rbp 664: 89 f8 mov %edi,%eax 666: 66 89 45 fc mov %ax,-0x4(%rbp) 66a: 90 nop 66b: 5d pop %rbp 66c: c3 retq 000000000000066d <main>: 66d: 55 push %rbp 66e: 48 89 e5 mov %rsp,%rbp 671: bf 64 00 00 00 mov $0x64,%edi 676: e8 e5 ff ff ff callq 660 <func1> 67b: b8 00 00 00 00 mov $0x0,%eax 680: 5d pop %rbp 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)
次は、32bit。longを使います。
takk@deb9:~/tmp$ cat -n t.c 1 void func1(long aaa) 2 { 3 4 } 5 6 int main() 7 { 8 func1(100); 9 } takk@deb9:~/tmp$
なんと! func1のコードが短くなっています。
0000000000000660 <func1>: 660: 55 push %rbp 661: 48 89 e5 mov %rsp,%rbp 664: 48 89 7d f8 mov %rdi,-0x8(%rbp) 668: 90 nop 669: 5d pop %rbp 66a: c3 retq 000000000000066b <main>: 66b: 55 push %rbp 66c: 48 89 e5 mov %rsp,%rbp 66f: bf 64 00 00 00 mov $0x64,%edi 674: e8 e7 ff ff ff callq 660 <func1> 679: b8 00 00 00 00 mov $0x0,%eax 67e: 5d pop %rbp 67f: c3 retq
mainも短くなっています。
次は、64bit。long longでもよいのですが、分かりにくいのでint64_tにしました。
takk@deb9:~/tmp$ cat -n t.c 1 #include <stdint.h> 2 void func1(int64_t aaa) 3 { 4 5 } 6 7 int main() 8 { 9 func1(100); 10 } takk@deb9:~/tmp$
32bitの時と同じコードサイズです。レジスタはrdiに変わってます。
0000000000000660 <func1>: 660: 55 push %rbp 661: 48 89 e5 mov %rsp,%rbp 664: 48 89 7d f8 mov %rdi,-0x8(%rbp) 668: 90 nop 669: 5d pop %rbp 66a: c3 retq 000000000000066b <main>: 66b: 55 push %rbp 66c: 48 89 e5 mov %rsp,%rbp 66f: bf 64 00 00 00 mov $0x64,%edi 674: e8 e7 ff ff ff callq 660 <func1> 679: b8 00 00 00 00 mov $0x0,%eax 67e: 5d pop %rbp 67f: c3 retq
32bitも64bitもアセンブラのコードサイズは同じですね。
しかし、8bit/16bitと比べると数バイト短くなります。
引数の型は、32bit以上にすると、効率がよさそうです。
コメント