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]....