Skip to content

Reapply "[libc++] Simplify the implementation of std::sort a bit (#104902)" #114023

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 30, 2024

Conversation

philnik777
Copy link
Contributor

@philnik777 philnik777 commented Oct 29, 2024

This reverts commit ef44e46. The patch was originally reverted because it was
deemed to introduce a performance regression for small inputs, however it also fixed
a previous performance regression for larger inputs. So overall, this patch is desirable.

@philnik777 philnik777 requested a review from a team as a code owner October 29, 2024 09:55
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Oct 29, 2024
@llvmbot
Copy link
Member

llvmbot commented Oct 29, 2024

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

Changes

This reverts commit ef44e46.


Patch is 21.59 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/114023.diff

8 Files Affected:

  • (modified) libcxx/include/__algorithm/comp.h (+3)
  • (modified) libcxx/include/__algorithm/ranges_minmax.h (+1-1)
  • (modified) libcxx/include/__algorithm/sort.h (+120-165)
  • (modified) libcxx/include/__functional/operations.h (+12)
  • (modified) libcxx/include/__functional/ranges_operations.h (+6)
  • (modified) libcxx/include/__type_traits/desugars_to.h (+6)
  • (modified) libcxx/include/__type_traits/is_trivially_copyable.h (+1-3)
  • (modified) libcxx/src/algorithm.cpp (+1-2)
diff --git a/libcxx/include/__algorithm/comp.h b/libcxx/include/__algorithm/comp.h
index 1f38f5d2d99b43..ab3c598418828a 100644
--- a/libcxx/include/__algorithm/comp.h
+++ b/libcxx/include/__algorithm/comp.h
@@ -42,6 +42,9 @@ struct __less<void, void> {
   }
 };
 
+template <class _Tp>
+inline const bool __desugars_to_v<__less_tag, __less<>, _Tp, _Tp> = true;
+
 template <class _Tp>
 inline const bool __desugars_to_v<__totally_ordered_less_tag, __less<>, _Tp, _Tp> = is_integral<_Tp>::value;
 
diff --git a/libcxx/include/__algorithm/ranges_minmax.h b/libcxx/include/__algorithm/ranges_minmax.h
index 4f2b2bf26382da..5f2e5cb2a1eeab 100644
--- a/libcxx/include/__algorithm/ranges_minmax.h
+++ b/libcxx/include/__algorithm/ranges_minmax.h
@@ -89,7 +89,7 @@ struct __minmax {
     // vectorize the code.
     if constexpr (contiguous_range<_Range> && is_integral_v<_ValueT> &&
                   __is_cheap_to_copy<_ValueT> & __is_identity<_Proj>::value &&
-                  __desugars_to_v<__totally_ordered_less_tag, _Comp, _ValueT, _ValueT>) {
+                  __desugars_to_v<__less_tag, _Comp, _ValueT, _ValueT>) {
       minmax_result<_ValueT> __result = {__r[0], __r[0]};
       for (auto __e : __r) {
         if (__e < __result.min)
diff --git a/libcxx/include/__algorithm/sort.h b/libcxx/include/__algorithm/sort.h
index 0b2137dee2f77e..39868b8b6a30ae 100644
--- a/libcxx/include/__algorithm/sort.h
+++ b/libcxx/include/__algorithm/sort.h
@@ -27,11 +27,13 @@
 #include <__functional/ranges_operations.h>
 #include <__iterator/iterator_traits.h>
 #include <__type_traits/conditional.h>
+#include <__type_traits/desugars_to.h>
 #include <__type_traits/disjunction.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/is_arithmetic.h>
 #include <__type_traits/is_constant_evaluated.h>
 #include <__type_traits/is_same.h>
+#include <__type_traits/is_trivially_copyable.h>
 #include <__type_traits/remove_cvref.h>
 #include <__utility/move.h>
 #include <__utility/pair.h>
@@ -47,110 +49,11 @@ _LIBCPP_PUSH_MACROS
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-// stable, 2-3 compares, 0-2 swaps
-
-template <class _AlgPolicy, class _Compare, class _ForwardIterator>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 unsigned
-__sort3(_ForwardIterator __x, _ForwardIterator __y, _ForwardIterator __z, _Compare __c) {
-  using _Ops = _IterOps<_AlgPolicy>;
-
-  unsigned __r = 0;
-  if (!__c(*__y, *__x)) // if x <= y
-  {
-    if (!__c(*__z, *__y))      // if y <= z
-      return __r;              // x <= y && y <= z
-                               // x <= y && y > z
-    _Ops::iter_swap(__y, __z); // x <= z && y < z
-    __r = 1;
-    if (__c(*__y, *__x)) // if x > y
-    {
-      _Ops::iter_swap(__x, __y); // x < y && y <= z
-      __r = 2;
-    }
-    return __r; // x <= y && y < z
-  }
-  if (__c(*__z, *__y)) // x > y, if y > z
-  {
-    _Ops::iter_swap(__x, __z); // x < y && y < z
-    __r = 1;
-    return __r;
-  }
-  _Ops::iter_swap(__x, __y); // x > y && y <= z
-  __r = 1;                   // x < y && x <= z
-  if (__c(*__z, *__y))       // if y > z
-  {
-    _Ops::iter_swap(__y, __z); // x <= y && y < z
-    __r = 2;
-  }
-  return __r;
-} // x <= y && y <= z
-
-// stable, 3-6 compares, 0-5 swaps
-
-template <class _AlgPolicy, class _Compare, class _ForwardIterator>
-_LIBCPP_HIDE_FROM_ABI void
-__sort4(_ForwardIterator __x1, _ForwardIterator __x2, _ForwardIterator __x3, _ForwardIterator __x4, _Compare __c) {
-  using _Ops = _IterOps<_AlgPolicy>;
-  std::__sort3<_AlgPolicy, _Compare>(__x1, __x2, __x3, __c);
-  if (__c(*__x4, *__x3)) {
-    _Ops::iter_swap(__x3, __x4);
-    if (__c(*__x3, *__x2)) {
-      _Ops::iter_swap(__x2, __x3);
-      if (__c(*__x2, *__x1)) {
-        _Ops::iter_swap(__x1, __x2);
-      }
-    }
-  }
-}
-
-// stable, 4-10 compares, 0-9 swaps
-
-template <class _AlgPolicy, class _Comp, class _ForwardIterator>
-_LIBCPP_HIDE_FROM_ABI void
-__sort5(_ForwardIterator __x1,
-        _ForwardIterator __x2,
-        _ForwardIterator __x3,
-        _ForwardIterator __x4,
-        _ForwardIterator __x5,
-        _Comp __comp) {
-  using _Ops = _IterOps<_AlgPolicy>;
-
-  std::__sort4<_AlgPolicy, _Comp>(__x1, __x2, __x3, __x4, __comp);
-  if (__comp(*__x5, *__x4)) {
-    _Ops::iter_swap(__x4, __x5);
-    if (__comp(*__x4, *__x3)) {
-      _Ops::iter_swap(__x3, __x4);
-      if (__comp(*__x3, *__x2)) {
-        _Ops::iter_swap(__x2, __x3);
-        if (__comp(*__x2, *__x1)) {
-          _Ops::iter_swap(__x1, __x2);
-        }
-      }
-    }
-  }
-}
-
-// The comparator being simple is a prerequisite for using the branchless optimization.
-template <class _Tp>
-struct __is_simple_comparator : false_type {};
-template <>
-struct __is_simple_comparator<__less<>&> : true_type {};
-template <class _Tp>
-struct __is_simple_comparator<less<_Tp>&> : true_type {};
-template <class _Tp>
-struct __is_simple_comparator<greater<_Tp>&> : true_type {};
-#if _LIBCPP_STD_VER >= 20
-template <>
-struct __is_simple_comparator<ranges::less&> : true_type {};
-template <>
-struct __is_simple_comparator<ranges::greater&> : true_type {};
-#endif
-
 template <class _Compare, class _Iter, class _Tp = typename iterator_traits<_Iter>::value_type>
-using __use_branchless_sort =
-    integral_constant<bool,
-                      __libcpp_is_contiguous_iterator<_Iter>::value && sizeof(_Tp) <= sizeof(void*) &&
-                          is_arithmetic<_Tp>::value && __is_simple_comparator<_Compare>::value>;
+inline const bool __use_branchless_sort =
+    __libcpp_is_contiguous_iterator<_Iter>::value && __is_cheap_to_copy<_Tp> && is_arithmetic<_Tp>::value &&
+    (__desugars_to_v<__less_tag, __remove_cvref_t<_Compare>, _Tp, _Tp> ||
+     __desugars_to_v<__greater_tag, __remove_cvref_t<_Compare>, _Tp, _Tp>);
 
 namespace __detail {
 
@@ -161,59 +64,88 @@ enum { __block_size = sizeof(uint64_t) * 8 };
 
 // Ensures that __c(*__x, *__y) is true by swapping *__x and *__y if necessary.
 template <class _Compare, class _RandomAccessIterator>
-inline _LIBCPP_HIDE_FROM_ABI void __cond_swap(_RandomAccessIterator __x, _RandomAccessIterator __y, _Compare __c) {
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool
+__cond_swap(_RandomAccessIterator __x, _RandomAccessIterator __y, _Compare __c) {
   // Note: this function behaves correctly even with proxy iterators (because it relies on `value_type`).
   using value_type = typename iterator_traits<_RandomAccessIterator>::value_type;
   bool __r         = __c(*__x, *__y);
   value_type __tmp = __r ? *__x : *__y;
   *__y             = __r ? *__y : *__x;
   *__x             = __tmp;
+  return !__r;
 }
 
 // Ensures that *__x, *__y and *__z are ordered according to the comparator __c,
 // under the assumption that *__y and *__z are already ordered.
 template <class _Compare, class _RandomAccessIterator>
-inline _LIBCPP_HIDE_FROM_ABI void
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool
 __partially_sorted_swap(_RandomAccessIterator __x, _RandomAccessIterator __y, _RandomAccessIterator __z, _Compare __c) {
   // Note: this function behaves correctly even with proxy iterators (because it relies on `value_type`).
   using value_type = typename iterator_traits<_RandomAccessIterator>::value_type;
-  bool __r         = __c(*__z, *__x);
-  value_type __tmp = __r ? *__z : *__x;
-  *__z             = __r ? *__x : *__z;
-  __r              = __c(__tmp, *__y);
-  *__x             = __r ? *__x : *__y;
-  *__y             = __r ? *__y : __tmp;
+  bool __r1        = __c(*__z, *__x);
+  value_type __tmp = __r1 ? *__z : *__x;
+  *__z             = __r1 ? *__x : *__z;
+  bool __r2        = __c(__tmp, *__y);
+  *__x             = __r2 ? *__x : *__y;
+  *__y             = __r2 ? *__y : __tmp;
+  return !__r1 || !__r2;
 }
 
+// stable, 2-3 compares, 0-2 swaps
+
 template <class,
           class _Compare,
           class _RandomAccessIterator,
-          __enable_if_t<__use_branchless_sort<_Compare, _RandomAccessIterator>::value, int> = 0>
-inline _LIBCPP_HIDE_FROM_ABI void __sort3_maybe_branchless(
-    _RandomAccessIterator __x1, _RandomAccessIterator __x2, _RandomAccessIterator __x3, _Compare __c) {
-  std::__cond_swap<_Compare>(__x2, __x3, __c);
-  std::__partially_sorted_swap<_Compare>(__x1, __x2, __x3, __c);
+          __enable_if_t<__use_branchless_sort<_Compare, _RandomAccessIterator>, int> = 0>
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool
+__sort3(_RandomAccessIterator __x1, _RandomAccessIterator __x2, _RandomAccessIterator __x3, _Compare __c) {
+  bool __swapped1 = std::__cond_swap<_Compare>(__x2, __x3, __c);
+  bool __swapped2 = std::__partially_sorted_swap<_Compare>(__x1, __x2, __x3, __c);
+  return __swapped1 || __swapped2;
 }
 
 template <class _AlgPolicy,
           class _Compare,
           class _RandomAccessIterator,
-          __enable_if_t<!__use_branchless_sort<_Compare, _RandomAccessIterator>::value, int> = 0>
-inline _LIBCPP_HIDE_FROM_ABI void __sort3_maybe_branchless(
-    _RandomAccessIterator __x1, _RandomAccessIterator __x2, _RandomAccessIterator __x3, _Compare __c) {
-  std::__sort3<_AlgPolicy, _Compare>(__x1, __x2, __x3, __c);
-}
+          __enable_if_t<!__use_branchless_sort<_Compare, _RandomAccessIterator>, int> = 0>
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool
+__sort3(_RandomAccessIterator __x, _RandomAccessIterator __y, _RandomAccessIterator __z, _Compare __c) {
+  using _Ops = _IterOps<_AlgPolicy>;
+
+  if (!__c(*__y, *__x)) // if x <= y
+  {
+    if (!__c(*__z, *__y))        // if y <= z
+      return false;              // x <= y && y <= z
+                                 // x <= y && y > z
+    _Ops::iter_swap(__y, __z);   // x <= z && y < z
+    if (__c(*__y, *__x))         // if x > y
+      _Ops::iter_swap(__x, __y); // x < y && y <= z
+    return true;                 // x <= y && y < z
+  }
+  if (__c(*__z, *__y)) // x > y, if y > z
+  {
+    _Ops::iter_swap(__x, __z); // x < y && y < z
+    return true;
+  }
+  _Ops::iter_swap(__x, __y); // x > y && y <= z
+  // x < y && x <= z
+  if (__c(*__z, *__y))         // if y > z
+    _Ops::iter_swap(__y, __z); // x <= y && y < z
+  return true;
+} // x <= y && y <= z
+
+// stable, 3-6 compares, 0-5 swaps
 
 template <class,
           class _Compare,
           class _RandomAccessIterator,
-          __enable_if_t<__use_branchless_sort<_Compare, _RandomAccessIterator>::value, int> = 0>
-inline _LIBCPP_HIDE_FROM_ABI void __sort4_maybe_branchless(
-    _RandomAccessIterator __x1,
-    _RandomAccessIterator __x2,
-    _RandomAccessIterator __x3,
-    _RandomAccessIterator __x4,
-    _Compare __c) {
+          __enable_if_t<__use_branchless_sort<_Compare, _RandomAccessIterator>, int> = 0>
+inline _LIBCPP_HIDE_FROM_ABI void
+__sort4(_RandomAccessIterator __x1,
+        _RandomAccessIterator __x2,
+        _RandomAccessIterator __x3,
+        _RandomAccessIterator __x4,
+        _Compare __c) {
   std::__cond_swap<_Compare>(__x1, __x3, __c);
   std::__cond_swap<_Compare>(__x2, __x4, __c);
   std::__cond_swap<_Compare>(__x1, __x2, __c);
@@ -224,27 +156,39 @@ inline _LIBCPP_HIDE_FROM_ABI void __sort4_maybe_branchless(
 template <class _AlgPolicy,
           class _Compare,
           class _RandomAccessIterator,
-          __enable_if_t<!__use_branchless_sort<_Compare, _RandomAccessIterator>::value, int> = 0>
-inline _LIBCPP_HIDE_FROM_ABI void __sort4_maybe_branchless(
-    _RandomAccessIterator __x1,
-    _RandomAccessIterator __x2,
-    _RandomAccessIterator __x3,
-    _RandomAccessIterator __x4,
-    _Compare __c) {
-  std::__sort4<_AlgPolicy, _Compare>(__x1, __x2, __x3, __x4, __c);
+          __enable_if_t<!__use_branchless_sort<_Compare, _RandomAccessIterator>, int> = 0>
+inline _LIBCPP_HIDE_FROM_ABI void
+__sort4(_RandomAccessIterator __x1,
+        _RandomAccessIterator __x2,
+        _RandomAccessIterator __x3,
+        _RandomAccessIterator __x4,
+        _Compare __c) {
+  using _Ops = _IterOps<_AlgPolicy>;
+  std::__sort3<_AlgPolicy, _Compare>(__x1, __x2, __x3, __c);
+  if (__c(*__x4, *__x3)) {
+    _Ops::iter_swap(__x3, __x4);
+    if (__c(*__x3, *__x2)) {
+      _Ops::iter_swap(__x2, __x3);
+      if (__c(*__x2, *__x1)) {
+        _Ops::iter_swap(__x1, __x2);
+      }
+    }
+  }
 }
 
+// stable, 4-10 compares, 0-9 swaps
+
 template <class _AlgPolicy,
           class _Compare,
           class _RandomAccessIterator,
-          __enable_if_t<__use_branchless_sort<_Compare, _RandomAccessIterator>::value, int> = 0>
-inline _LIBCPP_HIDE_FROM_ABI void __sort5_maybe_branchless(
-    _RandomAccessIterator __x1,
-    _RandomAccessIterator __x2,
-    _RandomAccessIterator __x3,
-    _RandomAccessIterator __x4,
-    _RandomAccessIterator __x5,
-    _Compare __c) {
+          __enable_if_t<__use_branchless_sort<_Compare, _RandomAccessIterator>, int> = 0>
+inline _LIBCPP_HIDE_FROM_ABI void
+__sort5(_RandomAccessIterator __x1,
+        _RandomAccessIterator __x2,
+        _RandomAccessIterator __x3,
+        _RandomAccessIterator __x4,
+        _RandomAccessIterator __x5,
+        _Compare __c) {
   std::__cond_swap<_Compare>(__x1, __x2, __c);
   std::__cond_swap<_Compare>(__x4, __x5, __c);
   std::__partially_sorted_swap<_Compare>(__x3, __x4, __x5, __c);
@@ -256,16 +200,29 @@ inline _LIBCPP_HIDE_FROM_ABI void __sort5_maybe_branchless(
 template <class _AlgPolicy,
           class _Compare,
           class _RandomAccessIterator,
-          __enable_if_t<!__use_branchless_sort<_Compare, _RandomAccessIterator>::value, int> = 0>
-inline _LIBCPP_HIDE_FROM_ABI void __sort5_maybe_branchless(
-    _RandomAccessIterator __x1,
-    _RandomAccessIterator __x2,
-    _RandomAccessIterator __x3,
-    _RandomAccessIterator __x4,
-    _RandomAccessIterator __x5,
-    _Compare __c) {
-  std::__sort5<_AlgPolicy, _Compare, _RandomAccessIterator>(
-      std::move(__x1), std::move(__x2), std::move(__x3), std::move(__x4), std::move(__x5), __c);
+          __enable_if_t<!__use_branchless_sort<_Compare, _RandomAccessIterator>, int> = 0>
+inline _LIBCPP_HIDE_FROM_ABI void
+__sort5(_RandomAccessIterator __x1,
+        _RandomAccessIterator __x2,
+        _RandomAccessIterator __x3,
+        _RandomAccessIterator __x4,
+        _RandomAccessIterator __x5,
+        _Compare __comp) {
+  using _Ops = _IterOps<_AlgPolicy>;
+
+  std::__sort4<_AlgPolicy, _Compare>(__x1, __x2, __x3, __x4, __comp);
+  if (__comp(*__x5, *__x4)) {
+    _Ops::iter_swap(__x4, __x5);
+    if (__comp(*__x4, *__x3)) {
+      _Ops::iter_swap(__x3, __x4);
+      if (__comp(*__x3, *__x2)) {
+        _Ops::iter_swap(__x2, __x3);
+        if (__comp(*__x2, *__x1)) {
+          _Ops::iter_swap(__x1, __x2);
+        }
+      }
+    }
+  }
 }
 
 // Assumes size > 0
@@ -355,14 +312,14 @@ __insertion_sort_incomplete(_RandomAccessIterator __first, _RandomAccessIterator
       _Ops::iter_swap(__first, __last);
     return true;
   case 3:
-    std::__sort3_maybe_branchless<_AlgPolicy, _Comp>(__first, __first + difference_type(1), --__last, __comp);
+    std::__sort3<_AlgPolicy, _Comp>(__first, __first + difference_type(1), --__last, __comp);
     return true;
   case 4:
-    std::__sort4_maybe_branchless<_AlgPolicy, _Comp>(
+    std::__sort4<_AlgPolicy, _Comp>(
         __first, __first + difference_type(1), __first + difference_type(2), --__last, __comp);
     return true;
   case 5:
-    std::__sort5_maybe_branchless<_AlgPolicy, _Comp>(
+    std::__sort5<_AlgPolicy, _Comp>(
         __first,
         __first + difference_type(1),
         __first + difference_type(2),
@@ -373,7 +330,7 @@ __insertion_sort_incomplete(_RandomAccessIterator __first, _RandomAccessIterator
   }
   typedef typename iterator_traits<_RandomAccessIterator>::value_type value_type;
   _RandomAccessIterator __j = __first + difference_type(2);
-  std::__sort3_maybe_branchless<_AlgPolicy, _Comp>(__first, __first + difference_type(1), __j, __comp);
+  std::__sort3<_AlgPolicy, _Comp>(__first, __first + difference_type(1), __j, __comp);
   const unsigned __limit = 8;
   unsigned __count       = 0;
   for (_RandomAccessIterator __i = __j + difference_type(1); __i != __last; ++__i) {
@@ -780,14 +737,14 @@ void __introsort(_RandomAccessIterator __first,
         _Ops::iter_swap(__first, __last);
       return;
     case 3:
-      std::__sort3_maybe_branchless<_AlgPolicy, _Compare>(__first, __first + difference_type(1), --__last, __comp);
+      std::__sort3<_AlgPolicy, _Compare>(__first, __first + difference_type(1), --__last, __comp);
       return;
     case 4:
-      std::__sort4_maybe_branchless<_AlgPolicy, _Compare>(
+      std::__sort4<_AlgPolicy, _Compare>(
           __first, __first + difference_type(1), __first + difference_type(2), --__last, __comp);
       return;
     case 5:
-      std::__sort5_maybe_branchless<_AlgPolicy, _Compare>(
+      std::__sort5<_AlgPolicy, _Compare>(
           __first,
           __first + difference_type(1),
           __first + difference_type(2),
@@ -928,10 +885,8 @@ __sort_dispatch(_RandomAccessIterator __first, _RandomAccessIterator __last, _Co
   // Only use bitset partitioning for arithmetic types.  We should also check
   // that the default comparator is in use so that we are sure that there are no
   // branches in the comparator.
-  std::__introsort<_AlgPolicy,
-                   _Comp&,
-                   _RandomAccessIterator,
-                   __use_branchless_sort<_Comp, _RandomAccessIterator>::value>(__first, __last, __comp, __depth_limit);
+  std::__introsort<_AlgPolicy, _Comp&, _RandomAccessIterator, __use_branchless_sort<_Comp, _RandomAccessIterator> >(
+      __first, __last, __comp, __depth_limit);
 }
 
 template <class _Type, class... _Options>
diff --git a/libcxx/include/__functional/operations.h b/libcxx/include/__functional/operations.h
index 6022bd679ed3e3..67d9da289aead3 100644
--- a/libcxx/include/__functional/operations.h
+++ b/libcxx/include/__functional/operations.h
@@ -362,6 +362,9 @@ struct _LIBCPP_TEMPLATE_VIS less : __binary_function<_Tp, _Tp, bool> {
 };
 _LIBCPP_CTAD_SUPPORTED_FOR_TYPE(less);
 
+template <class _Tp>
+inline const bool __desugars_to_v<__less_tag, less<_Tp>, _Tp, _Tp> = true;
+
 template <class _Tp>
 inline const bool __desugars_to_v<__totally_ordered_less_tag, less<_Tp>, _Tp, _Tp> = is_integral<_Tp>::value;
 
@@ -377,6 +380,9 @@ struct _LIBCPP_TEMPLATE_VIS less<void> {
   typedef void is_transparent;
 };
 
+template <class _Tp, class _Up>
+inline const bool __desugars_to_v<__less_tag, less<>, _Tp, _Up> = true;
+
 template <class _Tp>
 inline const bool __desugars_to_v<__totally_ordered_less_tag, less<>, _Tp, _Tp> = is_integral<_Tp>::value;
 #endif
@@ -446,6 +452,9 @@ struct _LIBCPP_TEMPLATE_VIS greater : __binary_function<_Tp, _Tp, bool> {
 };
 _LIBCPP_CTAD_SUPPORTED_FOR_TYPE(greater);
 
+template <class _Tp>
+inline const bool __desugars_to_v<__greater_tag, greater<_Tp>, _Tp, _Tp> = true;
+
 #if _LIBCPP_STD_VER >= 14
 template <>
 struct _LIBCPP_TEMPLATE_VIS greater<void> {
@@ -457,6 +466,9 @@ struct _LIBCPP_TEMPLATE_VIS greater<void> {
   }
   typedef void is_transparent;
 };
+
+template <class _Tp, class _Up>
+inline const bool __desugars_to_v<__greater_tag, greater<>, _Tp, _Up> = true;
 #endif
 
 // Logical operations
diff --git a/libcxx/include/__functional/ranges_operations.h b/libcxx/include/__functional/ranges_operations.h
index f023d765a6c8ab..df95843e7c9af6 100644
--- a/libcxx/include/__functional/ranges_operations.h
+++ b/libcxx/include/__functional/ranges_operations.h
@@ -102,6 +102,12 @@ inline const bool __desugars_to_v<__equal_tag, ranges::equal_to, _Tp, _Up> = tru
 template <class _Tp, class _Up>
 inline const bool __desugars_to_v<__totally_ordered_less_tag, ranges::less, _Tp, _Up> = true;
 
+template <class _Tp, class _Up>
+inline const bool __desugars_to_v<__less_tag, ranges::less, _Tp, _Up> = true;
+
+template <class _Tp, class _Up>
+inline const bool __desugars_to_v<__greater_tag, ranges::greater, _Tp, _Up> = true;
+
 #endif // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__type_traits/desugars_to.h b/libcxx/include/__type_traits/desugars_to.h
index b0ce7c414e5d77..452c70bfbad66d 100644
--- a/libcxx/include/__type_traits/desugars_to.h
+++ b/libcxx/include/__type_traits/desugars_to.h
@@ -25,6 +25,12 @@ struct __equal_tag {};
 // syntactically, the operation is equivalent to calling `a + b`
 struct __plus_tag {};
 
+// syntactically, the operation is equivalent to calling `a < b`
+struct __less_tag {};
+
+// syntactically, t...
[truncated]

Copy link
Member

@ldionne ldionne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM w/ CI.

@philnik777
Copy link
Contributor Author

The CI failures look unrelated.

@philnik777 philnik777 merged commit 0fb76ba into llvm:main Oct 30, 2024
63 of 65 checks passed
NoumanAmir657 pushed a commit to NoumanAmir657/llvm-project that referenced this pull request Nov 4, 2024
…m#104902)" (llvm#114023)

This reverts commit ef44e46. The patch was originally reverted
because it was
deemed to introduce a performance regression for small inputs, however
it also fixed
a previous performance regression for larger inputs. So overall, this
patch is desirable.
@philnik777 philnik777 deleted the revert_sort_simplification_revert branch March 29, 2025 08:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants