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

            Bug ID: 86912
           Summary: Function pointer imposes an optimization barrier
           Product: gcc
           Version: 9.0
            Status: UNCONFIRMED
          Keywords: missed-optimization
          Severity: normal
          Priority: P3
         Component: middle-end
          Assignee: unassigned at gcc dot gnu.org
          Reporter: antoshkka at gmail dot com
  Target Milestone: ---

Consider the following minimized example of std::variant visitation:

struct A {};
struct B : A {};
struct C : A {};
struct D : A {};
struct E : A {};
struct X : A {};

template <class T>
struct helper {
    static A& call(void* value) {
        return *static_cast<T*>(value);
    }
};

A& get_base(int index, void* value) {
    using f_ptr = A&(*)(void*);
    constexpr f_ptr visitors[] = {
        helper<A>::call, helper<B>::call, helper<C>::call,
        helper<D>::call, helper<E>::call, helper<X>::call,
    };
    return visitors[index](value);
}


For the above code GCC generates very suboptimal assembly that fills an array
with pointers and does the jmp:
<...>
get_base(int, void*):
  mov QWORD PTR [rsp-56], OFFSET FLAT:(anonymous
namespace)::helper<A>::call(void*)
  movsx rax, edi
  mov rdi, rsi
  mov QWORD PTR [rsp-48], OFFSET FLAT:(anonymous
namespace)::helper<B>::call(void*)
  mov QWORD PTR [rsp-40], OFFSET FLAT:(anonymous
namespace)::helper<C>::call(void*)
  mov QWORD PTR [rsp-32], OFFSET FLAT:(anonymous
namespace)::helper<D>::call(void*)
  mov QWORD PTR [rsp-24], OFFSET FLAT:(anonymous
namespace)::helper<E>::call(void*)
  mov QWORD PTR [rsp-16], OFFSET FLAT:(anonymous
namespace)::helper<X>::call(void*)
  mov rax, QWORD PTR [rsp-56+rax*8]
  jmp rax


Optimal assembly should be the following:
get_base(int, void*):
  mov rax, rsi
  ret


If we rewrite `get_base` to avoid calls by function pointer, the assembly
becomes optimal:
A& get_base(int index, void* value) {
    if (index == 0) return helper<A>::call(value);
    if (index == 1) return helper<B>::call(value);
    if (index == 2) return helper<C>::call(value);
    if (index == 3) return helper<D>::call(value);
    if (index == 4) return helper<E>::call(value);
    if (index == 5) return helper<X>::call(value);
}

Reply via email to