Speed up compilation of typical invocations of std::visit(std::variant<...>) for up to 11 types (#164196)

Similar to https://reviews.llvm.org/D90168 which was reverted (https://github.com/llvm/llvm-project/issues/62648#issuecomment-1832315651), but uses `switch` statements rather than function pointer tables.
This commit is contained in:
higher-performance 2026-07-29 22:38:39 -04:00 committed by GitHub
parent bdddd3e720
commit a01f8c85bc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1332,6 +1332,10 @@ private:
friend struct __variant_detail::__visitation::__variant;
};
template <class... _Types>
variant<_Types...> __upcast_to_variant(const volatile variant<_Types...>*);
void __upcast_to_variant(...);
template <size_t _Ip, class... _Types>
_LIBCPP_HIDE_FROM_ABI constexpr bool __holds_alternative(const variant<_Types...>& __v) noexcept {
return __v.index() == _Ip;
@ -1589,23 +1593,106 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __throw_if_valueless(_Vs&&... __vs) {
}
}
# define _LIBCPP_VARIANT_DISPATCH_COUNT 11 // Speed up compilation for the common cases
template < class _Visitor, class... _Vs, typename>
_LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) visit(_Visitor&& __visitor, _Vs&&... __vs) {
using __variant_detail::__visitation::__variant;
std::__throw_if_valueless(std::forward<_Vs>(__vs)...);
// NOLINTNEXTLINE(bugprone-use-after-move) __throw_if_valueless doesn't actually forward the variants
return __variant::__visit_value(std::forward<_Visitor>(__visitor), std::forward<_Vs>(__vs)...);
// Determine the std::variant base class on which we call index() below.
// This is important for avoiding calling index() on a derived class.
using __variant_type = decltype(std::__upcast_to_variant(std::declval<remove_reference_t<_Vs>*>()...));
constexpr size_t __variant_size =
conditional_t<is_void_v<__variant_type>,
std::integral_constant<size_t, variant_npos>,
variant_size<__variant_type>>::value;
if constexpr (__variant_size <= _LIBCPP_VARIANT_DISPATCH_COUNT) {
using __variant_detail::__access::__variant;
# define _LIBCPP_VARIANT_DISPATCH_INDEX(_I) \
case _I: \
if constexpr (_I < __variant_size) { \
return std::__invoke( \
std::forward<_Visitor>(__visitor), __variant::__get_alt<_I>(std::forward<_Vs>(__vs)...).__value); \
} \
[[__fallthrough__]]
switch ((..., __vs.__variant_type::index())) {
// Use __LINE__ to avoid hardcoding numbers that might become stale
enum : size_t { _I0 = __LINE__ + 1 }; // New line required
_LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
_LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
_LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
_LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
_LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
_LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
_LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
_LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
_LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
_LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
_LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
static_assert(__LINE__ - _I0 == _LIBCPP_VARIANT_DISPATCH_COUNT, "index count mismatch");
default:
std::__throw_bad_variant_access("std::visit: variant is valueless");
}
# undef _LIBCPP_VARIANT_DISPATCH_INDEX
} else {
using __variant_detail::__visitation::__variant;
std::__throw_if_valueless(std::forward<_Vs>(__vs)...);
// NOLINTNEXTLINE(bugprone-use-after-move) __throw_if_valueless doesn't actually forward the variants
return __variant::__visit_value(std::forward<_Visitor>(__visitor), std::forward<_Vs>(__vs)...);
}
}
# if _LIBCPP_STD_VER >= 20
template < class _Rp, class _Visitor, class... _Vs, typename>
_LIBCPP_HIDE_FROM_ABI constexpr _Rp visit(_Visitor&& __visitor, _Vs&&... __vs) {
using __variant_detail::__visitation::__variant;
std::__throw_if_valueless(std::forward<_Vs>(__vs)...);
return __variant::__visit_value<_Rp>(std::forward<_Visitor>(__visitor), std::forward<_Vs>(__vs)...);
// Determine the std::variant base class on which we call index() below.
// This is important for avoiding calling index() on a derived class.
using __variant_type = decltype(std::__upcast_to_variant(std::declval<remove_reference_t<_Vs>*>()...));
constexpr size_t __variant_size =
conditional_t<is_void_v<__variant_type>,
std::integral_constant<size_t, variant_npos>,
variant_size<__variant_type>>::value;
if constexpr (__variant_size <= _LIBCPP_VARIANT_DISPATCH_COUNT) {
using __variant_detail::__access::__variant;
# define _LIBCPP_VARIANT_DISPATCH_INDEX(_I) \
case _I: \
if constexpr (_I < __variant_size) { \
if constexpr (std::is_void_v<_Rp>) { \
return (void)std::__invoke( \
std::forward<_Visitor>(__visitor), __variant::__get_alt<_I>(std::forward<_Vs>(__vs)...).__value); \
} else { \
return std::__invoke( \
std::forward<_Visitor>(__visitor), __variant::__get_alt<_I>(std::forward<_Vs>(__vs)...).__value); \
} \
} \
[[__fallthrough__]]
switch ((..., __vs.__variant_type::index())) {
// Use __LINE__ to avoid hardcoding numbers that might become stale
enum : size_t { _I0 = __LINE__ + 1 }; // New line required
_LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
_LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
_LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
_LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
_LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
_LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
_LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
_LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
_LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
_LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
_LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
static_assert(__LINE__ - _I0 == _LIBCPP_VARIANT_DISPATCH_COUNT, "index count mismatch");
default:
std::__throw_bad_variant_access("std::visit: variant is valueless");
}
# undef _LIBCPP_VARIANT_DISPATCH_INDEX
} else {
using __variant_detail::__visitation::__variant;
std::__throw_if_valueless(std::forward<_Vs>(__vs)...);
return __variant::__visit_value<_Rp>(std::forward<_Visitor>(__visitor), std::forward<_Vs>(__vs)...);
}
}
# endif
# undef _LIBCPP_VARIANT_DISPATCH_COUNT
template <class... _Types>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 auto
swap(variant<_Types...>& __lhs, variant<_Types...>& __rhs) noexcept(noexcept(__lhs.swap(__rhs)))