https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108181
Bug ID: 108181 Summary: [missed optimization] Call to virtual function under runtime index should be optimized into jump with an offset Product: gcc Version: 13.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c++ Assignee: unassigned at gcc dot gnu.org Reporter: m.cencora at gmail dot com Target Milestone: --- Given code below compiled with g++ 11 or newer, compiler should be able to optimize 'get' into similar code as manually-optimized 'get_opt'. g++ -std=c++20 -O2 #include <bit> #include <cstdint> struct foo { virtual constexpr int& get0() noexcept = 0; virtual constexpr int& get1() noexcept = 0; virtual constexpr int& get2() noexcept = 0; virtual constexpr int& get3() noexcept = 0; virtual constexpr int& get4() noexcept = 0; virtual constexpr int& get5() noexcept = 0; virtual constexpr int& get6() noexcept = 0; virtual constexpr int& get7() noexcept = 0; virtual constexpr int& get8() noexcept = 0; virtual constexpr int& get9() noexcept = 0; }; template <typename T, unsigned idx> constexpr auto memPtr = nullptr; template <typename T> constexpr auto memPtr<T, 0> = &T::get0; template <typename T> constexpr auto memPtr<T, 1> = &T::get1; template <typename T> constexpr auto memPtr<T, 2> = &T::get2; template <typename T> constexpr auto memPtr<T, 3> = &T::get3; template <typename T> constexpr auto memPtr<T, 4> = &T::get4; template <typename T> constexpr auto memPtr<T, 5> = &T::get5; template <typename T> constexpr auto memPtr<T, 6> = &T::get6; template <typename T> constexpr auto memPtr<T, 7> = &T::get7; template <typename T> constexpr auto memPtr<T, 8> = &T::get8; template <typename T> constexpr auto memPtr<T, 9> = &T::get9; int& get(unsigned idx, foo* f) noexcept { switch (idx) { case 0: return (f->*memPtr<foo, 0>)(); case 1: return (f->*memPtr<foo, 1>)(); case 2: return (f->*memPtr<foo, 2>)(); case 3: return (f->*memPtr<foo, 3>)(); case 4: return (f->*memPtr<foo, 4>)(); case 5: return (f->*memPtr<foo, 5>)(); case 6: return (f->*memPtr<foo, 6>)(); case 7: return (f->*memPtr<foo, 7>)(); case 8: return (f->*memPtr<foo, 8>)(); case 9: return (f->*memPtr<foo, 9>)(); default: __builtin_unreachable(); } } int& get_opt(unsigned idx, foo* f) noexcept { // assuming System V x64 ABI struct RawMemPtr { std::uintptr_t v[2]; }; using PtrType = int& (foo::*)() noexcept; const RawMemPtr rawPtr{{sizeof(void*) * idx + 1, 0}}; const auto ptr = std::bit_cast<PtrType>(rawPtr); return (f->*ptr)(); }