https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82107
--- Comment #3 from Martin Liška <marxin at gcc dot gnu.org> --- Huh, very nice example I must admit. Following bad happens: In FooImpl.cpp content of real_payload is inlined to payload function. Then ICF does merge operation: FooImpl::real_payload (struct FooImpl * const this, struct wstring & result1, struct wstring & result2) { <bb 2> [100.00%] [count: INV]: FooBase<FooImpl>::payload (this_2(D), result1_3(D), result2_4(D)); [tail call] return; } On the other hand in main.cpp a tail call is generated from original source code: FooBase<FooImpl>::payload (struct FooBase * const this, struct wstring & result1, struct wstring & result2) { <bb 2> [100.00%] [count: INV]: FooImpl::real_payload (this_2(D), result1_3(D), result2_4(D)); [tail call] return; } And as both are COMDATs wrong combination of function is selected and one can see the infinite loop: 0x400b80 <_ZN7FooImpl12real_payloadERNSt7__cxx1112basic_stringIwSt11char_traitsIwESaIwEEES6_> jmpq 0x400a30 <_ZN7FooBaseI7FooImplE7payloadERNSt7__cxx1112basic_stringIwSt11char_traitsIwESaIwEEES8_> 0x400a30 <_ZN7FooBaseI7FooImplE7payloadERNSt7__cxx1112basic_stringIwSt11char_traitsIwESaIwEEES8_> jmpq 0x400b80 <_ZN7FooImpl12real_payloadERNSt7__cxx1112basic_stringIwSt11char_traitsIwESaIwEEES6_>_> Well, we would need to somehow preserve the original order, probably based on inlining decisions that were done. Let me discuss that with Honza.