https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89924

            Bug ID: 89924
           Summary: [missed-optimization] Function not de-virtualized
                    within the same TU
           Product: gcc
           Version: 9.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: tree-optimization
          Assignee: unassigned at gcc dot gnu.org
          Reporter: eyalroz at technion dot ac.il
  Target Milestone: ---

Related StackOverflow question: https://stackoverflow.com/q/55464578/1593077
GodBolt example: https://godbolt.org/z/l0vdFG

In the following code:

  struct A {
      virtual A& operator+=(const A& other) noexcept = 0;
  };

  void foo_inner(int *p) noexcept { *p += *p; }
  void foo_virtual_inner(A *p) noexcept { *p += *p; }

  void foo(int *p) noexcept
  {
      return foo_inner(p);
  } 

  struct Aint : public A {
      int i;
      A& operator+=(const A& other) noexcept override final
      { 
          i += dynamic_cast<const Aint&>(other).i; 
  //      i += reinterpret_cast<const Aint&>(other).i; 
          return *this;
      }
  };

   void foo_virtual(Aint *p) noexcept
   {
       return foo_virtual_inner(p);
   }

Both functions, `foo()` and `foo_virtual()`, should compile to the same thing.
But g++ 8.3 (on x86_64) with -O3 produces:
```
foo(int*):
        sal     DWORD PTR [rdi]
        ret
foo_virtual(Aint*):
        mov     rax, QWORD PTR [rdi]
        mov     rax, QWORD PTR [rax]
        cmp     rax, OFFSET FLAT:Aint::operator+=(A const&)
        jne     .L19
        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
.L19:
        mov     rsi, rdi
        jmp     rax
foo_virtual(Aint*) [clone .cold.1]:
.L20:
        call    __cxa_bad_cast
```
i.e. it doesn't manage to de-virtualize `Aint::operator+=` - although it really
should. It has all the necessary information, as far as I can tell.

As a side note, even regardless of de-virtualization, there's a whole lot of
code there, while with with clang 8, we only get:
```
foo_virtual(Aint*):                  # @foo_virtual(Aint*)
        mov     rax, qword ptr [rdi]
        mov     rax, qword ptr [rax]
        mov     rsi, rdi
        jmp     rax     
```
which at least doesn't need the type info.

Reply via email to