C++のiostreamを使った関数の書き方で最後の関数呼び出しがtailcallになることにいまさら気づいた。いままでnontailcallの書き方をしていたが、それはインライン展開されたときに<<の戻り値に依存しなくなって最適化が効きやすいかと思ったからだったが、奥が深い。
% cat -n tailcall.cc
1 #include <iostream>
2 using namespace std;
3 ostream& nontailcall(ostream&);
4 ostream& tailcall(ostream&);
5 int
6 main()
7 {
8 nontailcall(cout);
9 tailcall(cout);
10 }
11 ostream& nontailcall(ostream& ost)
12 {
13 ost << 1;
14 return ost;
15 }
16 ostream& tailcall(ostream& ost)
17 {
18 return ost << 1;
19 }
% g++ -O9 tailcall.cc -o tailcall
% objdump -d tailcall | c++filt
tailcall: file format elf64-x86-64-freebsd
....[snip]....
Disassembly of section .text:
....[snip]....
0000000000400790 <tailcall(std::basic_ostream<char, std::char_traits<char> >&)>:
400790: be 01 00 00 00 mov $0x1,%esi
400795: e9 6a fe ff ff jmpq 400604 <std::basic_ostream<char, std::char_traits<char> >::operator<<(int)@plt>
40079a: 66 66 90 xchg %ax,%ax
40079d: 66 66 90 xchg %ax,%ax
00000000004007a0 <nontailcall(std::basic_ostream<char, std::char_traits<char> >&)>:
4007a0: 53 push %rbx
4007a1: be 01 00 00 00 mov $0x1,%esi
4007a6: 48 89 fb mov %rdi,%rbx
4007a9: e8 56 fe ff ff callq 400604 <std::basic_ostream<char, std::char_traits<char> >::operator<<(int)@plt>
4007ae: 48 89 d8 mov %rbx,%rax
4007b1: 5b pop %rbx
4007b2: c3 retq
4007b3: 90 nop
4007b4: 66 66 66 90 xchg %ax,%ax
4007b8: 66 66 66 90 xchg %ax,%ax
4007bc: 66 66 66 90 xchg %ax,%ax
....[snip]....














