[Clang] Add __builtin_invoke and use it in libc++ (#116709)
`std::invoke` is currently quite heavy compared to a function call,
since it involves quite heavy SFINAE. This can be done significantly
more efficient by the compiler, since most calls to `std::invoke` are
simple function calls and 6 out of the seven overloads for `std::invoke`
exist only to support member pointers. Even these boil down to a few
relatively simple checks.
Some real-world testing with this patch revealed some significant
results. For example, instantiating `std::format("Banane")` (and its
callees) went down from ~125ms on my system to ~104ms.
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
#include <__type_traits/is_same.h>
|
||||
#include <__type_traits/is_void.h>
|
||||
#include <__type_traits/nat.h>
|
||||
#include <__type_traits/void_t.h>
|
||||
#include <__utility/declval.h>
|
||||
#include <__utility/forward.h>
|
||||
|
||||
@@ -61,6 +62,112 @@
|
||||
|
||||
_LIBCPP_BEGIN_NAMESPACE_STD
|
||||
|
||||
#if __has_builtin(__builtin_invoke)
|
||||
|
||||
template <class... _Args>
|
||||
using __invoke_result_t = decltype(__builtin_invoke(std::declval<_Args>()...));
|
||||
|
||||
template <class, class... _Args>
|
||||
struct __invoke_result_impl {};
|
||||
|
||||
template <class... _Args>
|
||||
struct __invoke_result_impl<__void_t<__invoke_result_t<_Args...> >, _Args...> {
|
||||
using type _LIBCPP_NODEBUG = __invoke_result_t<_Args...>;
|
||||
};
|
||||
|
||||
template <class... _Args>
|
||||
using __invoke_result = __invoke_result_impl<void, _Args...>;
|
||||
|
||||
template <class... _Args>
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __invoke_result_t<_Args...> __invoke(_Args&&... __args)
|
||||
_NOEXCEPT_(noexcept(__builtin_invoke(std::forward<_Args>(__args)...))) {
|
||||
return __builtin_invoke(std::forward<_Args>(__args)...);
|
||||
}
|
||||
|
||||
template <class _Void, class... _Args>
|
||||
inline const bool __is_invocable_impl = false;
|
||||
|
||||
template <class... _Args>
|
||||
inline const bool __is_invocable_impl<__void_t<__invoke_result_t<_Args...> >, _Args...> = true;
|
||||
|
||||
template <class... _Args>
|
||||
inline const bool __is_invocable_v = __is_invocable_impl<void, _Args...>;
|
||||
|
||||
template <class... _Args>
|
||||
struct __is_invocable : integral_constant<bool, __is_invocable_v<_Args...> > {};
|
||||
|
||||
template <class _Ret, bool, class... _Args>
|
||||
inline const bool __is_invocable_r_impl = false;
|
||||
|
||||
template <class _Ret, class... _Args>
|
||||
inline const bool __is_invocable_r_impl<_Ret, true, _Args...> =
|
||||
__is_core_convertible<__invoke_result_t<_Args...>, _Ret>::value || is_void<_Ret>::value;
|
||||
|
||||
template <class _Ret, class... _Args>
|
||||
inline const bool __is_invocable_r_v = __is_invocable_r_impl<_Ret, __is_invocable_v<_Args...>, _Args...>;
|
||||
|
||||
template <bool __is_invocable, class... _Args>
|
||||
inline const bool __is_nothrow_invocable_impl = false;
|
||||
|
||||
template <class... _Args>
|
||||
inline const bool __is_nothrow_invocable_impl<true, _Args...> = noexcept(__builtin_invoke(std::declval<_Args>()...));
|
||||
|
||||
template <class... _Args>
|
||||
inline const bool __is_nothrow_invocable_v = __is_nothrow_invocable_impl<__is_invocable_v<_Args...>, _Args...>;
|
||||
|
||||
template <bool __is_invocable, class _Ret, class... _Args>
|
||||
inline const bool __is_nothrow_invocable_r_impl = false;
|
||||
|
||||
template <class _Ret, class... _Args>
|
||||
inline const bool __is_nothrow_invocable_r_impl<true, _Ret, _Args...> =
|
||||
__is_nothrow_core_convertible_v<__invoke_result_t<_Args...>, _Ret> || is_void<_Ret>::value;
|
||||
|
||||
template <class _Ret, class... _Args>
|
||||
inline const bool __is_nothrow_invocable_r_v =
|
||||
__is_nothrow_invocable_r_impl<__is_nothrow_invocable_v<_Args...>, _Ret, _Args...>;
|
||||
|
||||
# if _LIBCPP_STD_VER >= 17
|
||||
|
||||
// is_invocable
|
||||
|
||||
template <class _Fn, class... _Args>
|
||||
struct _LIBCPP_NO_SPECIALIZATIONS is_invocable : bool_constant<__is_invocable_v<_Fn, _Args...> > {};
|
||||
|
||||
template <class _Ret, class _Fn, class... _Args>
|
||||
struct _LIBCPP_NO_SPECIALIZATIONS is_invocable_r : bool_constant<__is_invocable_r_v<_Ret, _Fn, _Args...>> {};
|
||||
|
||||
template <class _Fn, class... _Args>
|
||||
_LIBCPP_NO_SPECIALIZATIONS inline constexpr bool is_invocable_v = __is_invocable_v<_Fn, _Args...>;
|
||||
|
||||
template <class _Ret, class _Fn, class... _Args>
|
||||
_LIBCPP_NO_SPECIALIZATIONS inline constexpr bool is_invocable_r_v = is_invocable_r<_Ret, _Fn, _Args...>::value;
|
||||
|
||||
// is_nothrow_invocable
|
||||
|
||||
template <class _Fn, class... _Args>
|
||||
struct _LIBCPP_NO_SPECIALIZATIONS is_nothrow_invocable : bool_constant<__is_nothrow_invocable_v<_Fn, _Args...> > {};
|
||||
|
||||
template <class _Ret, class _Fn, class... _Args>
|
||||
struct _LIBCPP_NO_SPECIALIZATIONS is_nothrow_invocable_r
|
||||
: integral_constant<bool, __is_nothrow_invocable_r_v<_Ret, _Fn, _Args...>> {};
|
||||
|
||||
template <class _Fn, class... _Args>
|
||||
_LIBCPP_NO_SPECIALIZATIONS inline constexpr bool is_nothrow_invocable_v = __is_nothrow_invocable_v<_Fn, _Args...>;
|
||||
|
||||
template <class _Ret, class _Fn, class... _Args>
|
||||
_LIBCPP_NO_SPECIALIZATIONS inline constexpr bool is_nothrow_invocable_r_v =
|
||||
__is_nothrow_invocable_r_v<_Ret, _Fn, _Args...>;
|
||||
|
||||
template <class _Fn, class... _Args>
|
||||
struct _LIBCPP_NO_SPECIALIZATIONS invoke_result : __invoke_result<_Fn, _Args...> {};
|
||||
|
||||
template <class _Fn, class... _Args>
|
||||
using invoke_result_t = __invoke_result_t<_Fn, _Args...>;
|
||||
|
||||
# endif // _LIBCPP_STD_VER >= 17
|
||||
|
||||
#else // __has_builtin(__builtin_invoke)
|
||||
|
||||
template <class _DecayedFp>
|
||||
struct __member_pointer_class_type {};
|
||||
|
||||
@@ -211,21 +318,21 @@ struct __nothrow_invokable_r_imp<true, false, _Ret, _Fp, _Args...> {
|
||||
template <class _Tp>
|
||||
static void __test_noexcept(_Tp) _NOEXCEPT;
|
||||
|
||||
#ifdef _LIBCPP_CXX03_LANG
|
||||
# ifdef _LIBCPP_CXX03_LANG
|
||||
static const bool value = false;
|
||||
#else
|
||||
# else
|
||||
static const bool value =
|
||||
noexcept(_ThisT::__test_noexcept<_Ret>(std::__invoke(std::declval<_Fp>(), std::declval<_Args>()...)));
|
||||
#endif
|
||||
# endif
|
||||
};
|
||||
|
||||
template <class _Ret, class _Fp, class... _Args>
|
||||
struct __nothrow_invokable_r_imp<true, true, _Ret, _Fp, _Args...> {
|
||||
#ifdef _LIBCPP_CXX03_LANG
|
||||
# ifdef _LIBCPP_CXX03_LANG
|
||||
static const bool value = false;
|
||||
#else
|
||||
# else
|
||||
static const bool value = noexcept(std::__invoke(std::declval<_Fp>(), std::declval<_Args>()...));
|
||||
#endif
|
||||
# endif
|
||||
};
|
||||
|
||||
template <class _Ret, class _Fp, class... _Args>
|
||||
@@ -236,22 +343,6 @@ template <class _Fp, class... _Args>
|
||||
using __nothrow_invokable _LIBCPP_NODEBUG =
|
||||
__nothrow_invokable_r_imp<__is_invocable<_Fp, _Args...>::value, true, void, _Fp, _Args...>;
|
||||
|
||||
template <class _Ret, bool = is_void<_Ret>::value>
|
||||
struct __invoke_void_return_wrapper {
|
||||
template <class... _Args>
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static _Ret __call(_Args&&... __args) {
|
||||
return std::__invoke(std::forward<_Args>(__args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template <class _Ret>
|
||||
struct __invoke_void_return_wrapper<_Ret, true> {
|
||||
template <class... _Args>
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static void __call(_Args&&... __args) {
|
||||
std::__invoke(std::forward<_Args>(__args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template <class _Func, class... _Args>
|
||||
inline const bool __is_invocable_v = __is_invocable<_Func, _Args...>::value;
|
||||
|
||||
@@ -268,12 +359,7 @@ struct __invoke_result
|
||||
template <class _Func, class... _Args>
|
||||
using __invoke_result_t _LIBCPP_NODEBUG = typename __invoke_result<_Func, _Args...>::type;
|
||||
|
||||
template <class _Ret, class... _Args>
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Ret __invoke_r(_Args&&... __args) {
|
||||
return __invoke_void_return_wrapper<_Ret>::__call(std::forward<_Args>(__args)...);
|
||||
}
|
||||
|
||||
#if _LIBCPP_STD_VER >= 17
|
||||
# if _LIBCPP_STD_VER >= 17
|
||||
|
||||
// is_invocable
|
||||
|
||||
@@ -311,7 +397,30 @@ struct _LIBCPP_NO_SPECIALIZATIONS invoke_result : __invoke_result<_Fn, _Args...>
|
||||
template <class _Fn, class... _Args>
|
||||
using invoke_result_t = typename invoke_result<_Fn, _Args...>::type;
|
||||
|
||||
#endif // _LIBCPP_STD_VER >= 17
|
||||
# endif // _LIBCPP_STD_VER >= 17
|
||||
|
||||
#endif // __has_builtin(__builtin_invoke_r)
|
||||
|
||||
template <class _Ret, bool = is_void<_Ret>::value>
|
||||
struct __invoke_void_return_wrapper {
|
||||
template <class... _Args>
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static _Ret __call(_Args&&... __args) {
|
||||
return std::__invoke(std::forward<_Args>(__args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template <class _Ret>
|
||||
struct __invoke_void_return_wrapper<_Ret, true> {
|
||||
template <class... _Args>
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static void __call(_Args&&... __args) {
|
||||
std::__invoke(std::forward<_Args>(__args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template <class _Ret, class... _Args>
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Ret __invoke_r(_Args&&... __args) {
|
||||
return __invoke_void_return_wrapper<_Ret>::__call(std::forward<_Args>(__args)...);
|
||||
}
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
|
||||
@@ -37,6 +37,15 @@ concept __core_convertible_to = __is_core_convertible<_Tp, _Up>::value;
|
||||
|
||||
#endif // _LIBCPP_STD_VER >= 20
|
||||
|
||||
template <class _Tp, class _Up, bool = __is_core_convertible<_Tp, _Up>::value>
|
||||
inline const bool __is_nothrow_core_convertible_v = false;
|
||||
|
||||
#ifndef _LIBCPP_CXX03_LANG
|
||||
template <class _Tp, class _Up>
|
||||
inline const bool __is_nothrow_core_convertible_v<_Tp, _Up, true> =
|
||||
noexcept(static_cast<void (*)(_Up) noexcept>(0)(static_cast<_Tp (*)() noexcept>(0)()));
|
||||
#endif
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
#endif // _LIBCPP___TYPE_TRAITS_IS_CORE_CONVERTIBLE_H
|
||||
|
||||
Reference in New Issue
Block a user