https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89924
--- Comment #4 from Jan Hubicka <hubicka at ucw dot cz> --- And to answer the question about why GCC produces more code, it is actually speculative devirtualization of the call. GCC determines the most likely target and inlines it. foo_virtual(Aint*): # this tests whether the dynamic type uses # Aint::operator+= mov rax, QWORD PTR [rdi] mov rax, QWORD PTR [rax] cmp rax, OFFSET FLAT:Aint::operator+=(A const&) jne .L19 # if so, this is inlined copy of operator += push rbx xor ecx, ecx mov edx, OFFSET FLAT:typeinfo for Aint mov esi, OFFSET FLAT:typeinfo for A mov rbx, rdi call __dynamic_cast test rax, rax je .L20 mov eax, DWORD PTR [rax+8] add DWORD PTR [rbx+8], eax pop rbx ret # this is the fallback code in case devirtualizaiton # failed. .L19: mov rsi, rdi jmp rax -fno-devirtualize-speculatively will lead to same code as Clang does. Honza