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