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:
parent
bdddd3e720
commit
a01f8c85bc
1 changed files with 94 additions and 7 deletions
|
|
@ -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)))
|
||||
|
|
|
|||
Loading…
Reference in a new issue