Skip to content

Commit

Permalink
[libc++][ranges] Implement ranges::sort.
Browse files Browse the repository at this point in the history
Differential Revision: https://reviews.llvm.org/D127557
  • Loading branch information
var-const committed Jun 16, 2022
1 parent dba2ff5 commit ff3989e
Show file tree
Hide file tree
Showing 16 changed files with 514 additions and 16 deletions.
1 change: 1 addition & 0 deletions libcxx/benchmarks/CMakeLists.txt
Expand Up @@ -166,6 +166,7 @@ set(BENCHMARK_TESTS
algorithms/min_max_element.bench.cpp
algorithms/pop_heap.bench.cpp
algorithms/push_heap.bench.cpp
algorithms/ranges_sort.bench.cpp
algorithms/sort.bench.cpp
algorithms/sort_heap.bench.cpp
algorithms/stable_sort.bench.cpp
Expand Down
39 changes: 39 additions & 0 deletions libcxx/benchmarks/algorithms/ranges_sort.bench.cpp
@@ -0,0 +1,39 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include <algorithm>

#include "common.h"

namespace {
template <class ValueType, class Order>
struct Sort {
size_t Quantity;

void run(benchmark::State& state) const {
runOpOnCopies<ValueType>(
state, Quantity, Order(), BatchSize::CountElements,
[](auto& Copy) { std::ranges::sort(Copy); });
}

bool skip() const { return Order() == ::Order::Heap; }

std::string name() const {
return "BM_RangesSort" + ValueType::name() + Order::name() + "_" +
std::to_string(Quantity);
}
};
} // namespace

int main(int argc, char** argv) {
benchmark::Initialize(&argc, argv);
if (benchmark::ReportUnrecognizedArguments(argc, argv))
return 1;
makeCartesianProductBenchmark<Sort, AllValueTypes, AllOrders>(Quantities);
benchmark::RunSpecifiedBenchmarks();
}
2 changes: 1 addition & 1 deletion libcxx/docs/Status/RangesAlgorithms.csv
Expand Up @@ -73,7 +73,7 @@ Permutation,shuffle,Not assigned,n/a,Not started
Permutation,unique,Not assigned,n/a,Not started
Permutation,partition,Not assigned,n/a,Not started
Permutation,stable_partition,Not assigned,n/a,Not started
Permutation,sort,Konstantin Varlamov,n/a,In progress
Permutation,sort,`D127557 <https://llvm.org/D127557>`_,Konstantin Varlamov,✅
Permutation,stable_sort,Konstantin Varlamov,n/a,In progress
Permutation,partial_sort,Konstantin Varlamov,n/a,In progress
Permutation,nth_element,Not assigned,n/a,Not started
Expand Down
2 changes: 2 additions & 0 deletions libcxx/include/CMakeLists.txt
Expand Up @@ -45,6 +45,7 @@ set(files
__algorithm/lexicographical_compare.h
__algorithm/lower_bound.h
__algorithm/make_heap.h
__algorithm/make_projected.h
__algorithm/max.h
__algorithm/max_element.h
__algorithm/merge.h
Expand Down Expand Up @@ -102,6 +103,7 @@ set(files
__algorithm/ranges_replace.h
__algorithm/ranges_replace_if.h
__algorithm/ranges_reverse.h
__algorithm/ranges_sort.h
__algorithm/ranges_swap_ranges.h
__algorithm/ranges_transform.h
__algorithm/ranges_upper_bound.h
Expand Down
51 changes: 51 additions & 0 deletions libcxx/include/__algorithm/make_projected.h
@@ -0,0 +1,51 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef _LIBCPP___ALGORITHM_MAKE_PROJECTED_H
#define _LIBCPP___ALGORITHM_MAKE_PROJECTED_H

#include <__concepts/same_as.h>
#include <__config>
#include <__functional/identity.h>
#include <__functional/invoke.h>
#include <__utility/forward.h>

#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif

#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)

_LIBCPP_BEGIN_NAMESPACE_STD

namespace ranges {

template <class _Comp, class _Proj>
_LIBCPP_HIDE_FROM_ABI constexpr static
decltype(auto) __make_projected_comp(_Comp& __comp, _Proj& __proj) {
if constexpr (same_as<_Proj, identity>) {
// Avoid creating the lambda and just use the pristine comparator -- for certain algorithms, this would enable
// optimizations that rely on the type of the comparator.
return __comp;

} else {
return [&](auto&& __lhs, auto&& __rhs) {
return std::invoke(__comp,
std::invoke(__proj, std::forward<decltype(__lhs)>(__lhs)),
std::invoke(__proj, std::forward<decltype(__rhs)>(__rhs)));
};
}
}

} // namespace ranges

_LIBCPP_END_NAMESPACE_STD

#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)

#endif // _LIBCPP___ALGORITHM_MAKE_PROJECTED_H
79 changes: 79 additions & 0 deletions libcxx/include/__algorithm/ranges_sort.h
@@ -0,0 +1,79 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef _LIBCPP___ALGORITHM_RANGES_SORT_H
#define _LIBCPP___ALGORITHM_RANGES_SORT_H

#include <__algorithm/make_projected.h>
#include <__algorithm/sort.h>
#include <__concepts/same_as.h>
#include <__config>
#include <__functional/identity.h>
#include <__functional/invoke.h>
#include <__functional/ranges_operations.h>
#include <__iterator/concepts.h>
#include <__iterator/iterator_traits.h>
#include <__iterator/next.h>
#include <__iterator/projected.h>
#include <__iterator/sortable.h>
#include <__ranges/access.h>
#include <__ranges/concepts.h>
#include <__ranges/dangling.h>
#include <__utility/forward.h>
#include <__utility/move.h>

#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif

#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)

_LIBCPP_BEGIN_NAMESPACE_STD

namespace ranges {
namespace __sort {

struct __fn {
template <class _Iter, class _Sent, class _Comp, class _Proj>
_LIBCPP_HIDE_FROM_ABI constexpr static
_Iter __sort_fn_impl(_Iter __first, _Sent __last, _Comp& __comp, _Proj& __proj) {
auto __last_iter = ranges::next(__first, __last);

auto&& __projected_comp = __make_projected_comp(__comp, __proj);
std::__sort_impl(std::move(__first), __last_iter, __projected_comp);

return __last_iter;
}

template <random_access_iterator _Iter, sentinel_for<_Iter> _Sent, class _Comp = ranges::less, class _Proj = identity>
requires sortable<_Iter, _Comp, _Proj>
_LIBCPP_HIDE_FROM_ABI constexpr
_Iter operator()(_Iter __first, _Sent __last, _Comp __comp = {}, _Proj __proj = {}) const {
return __sort_fn_impl(std::move(__first), std::move(__last), __comp, __proj);
}

template <random_access_range _Range, class _Comp = ranges::less, class _Proj = identity>
requires sortable<iterator_t<_Range>, _Comp, _Proj>
_LIBCPP_HIDE_FROM_ABI constexpr
borrowed_iterator_t<_Range> operator()(_Range&& __r, _Comp __comp = {}, _Proj __proj = {}) const {
return __sort_fn_impl(ranges::begin(__r), ranges::end(__r), __comp, __proj);
}
};

} // namespace __sort

inline namespace __cpo {
inline constexpr auto sort = __sort::__fn{};
} // namespace __cpo
} // namespace ranges

_LIBCPP_END_NAMESPACE_STD

#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)

#endif // _LIBCPP___ALGORITHM_RANGES_SORT_H
32 changes: 23 additions & 9 deletions libcxx/include/__algorithm/sort.h
Expand Up @@ -18,6 +18,7 @@
#include <__config>
#include <__debug>
#include <__functional/operations.h>
#include <__functional/ranges_operations.h>
#include <__iterator/iterator_traits.h>
#include <__utility/swap.h>
#include <climits>
Expand Down Expand Up @@ -115,6 +116,7 @@ _LIBCPP_HIDDEN unsigned __sort5(_ForwardIterator __x1, _ForwardIterator __x2, _F
return __r;
}

// The comparator being simple is a prerequisite for using the branchless optimization.
template <class _Tp>
struct __is_simple_comparator : false_type {};
template <class _Tp>
Expand All @@ -123,6 +125,12 @@ 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 > 17
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 =
Expand Down Expand Up @@ -571,22 +579,28 @@ extern template _LIBCPP_FUNC_VIS bool __insertion_sort_incomplete<__less<long do

extern template _LIBCPP_FUNC_VIS unsigned __sort5<__less<long double>&, long double*>(long double*, long double*, long double*, long double*, long double*, __less<long double>&);

template <class _RandomAccessIterator, class _Compare>
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 void
sort(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare __comp) {
template <class _RandomAccessIterator, class _Comp>
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17
void __sort_impl(_RandomAccessIterator __first, _RandomAccessIterator __last, _Comp& __comp) {
_LIBCPP_DEBUG_RANDOMIZE_RANGE(__first, __last);
typedef typename __comp_ref_type<_Compare>::type _Comp_ref;
using _Comp_ref = typename __comp_ref_type<_Comp>::type;
if (__libcpp_is_constant_evaluated()) {
_VSTD::__partial_sort<_Comp_ref>(__first, __last, __last, _Comp_ref(__comp));
std::__partial_sort<_Comp_ref>(__first, __last, __last, _Comp_ref(__comp));
} else {
_VSTD::__sort<_Comp_ref>(_VSTD::__unwrap_iter(__first), _VSTD::__unwrap_iter(__last), _Comp_ref(__comp));
std::__sort<_Comp_ref>(std::__unwrap_iter(__first), std::__unwrap_iter(__last), _Comp_ref(__comp));
}
}

template <class _RandomAccessIterator, class _Comp>
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17
void sort(_RandomAccessIterator __first, _RandomAccessIterator __last, _Comp __comp) {
std::__sort_impl(std::move(__first), std::move(__last), __comp);
}

template <class _RandomAccessIterator>
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 void sort(_RandomAccessIterator __first,
_RandomAccessIterator __last) {
_VSTD::sort(__first, __last, __less<typename iterator_traits<_RandomAccessIterator>::value_type>());
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17
void sort(_RandomAccessIterator __first, _RandomAccessIterator __last) {
std::sort(__first, __last, __less<typename iterator_traits<_RandomAccessIterator>::value_type>());
}

_LIBCPP_END_NAMESPACE_STD
Expand Down
13 changes: 12 additions & 1 deletion libcxx/include/algorithm
Expand Up @@ -53,7 +53,6 @@ namespace ranges {
indirect_strict_weak_order<projected<iterator_t<R>, Proj>> Comp = ranges::less>
constexpr borrowed_iterator_t<R> ranges::max_element(R&& r, Comp comp = {}, Proj proj = {}); // since C++20
template <input_iterator I1, sentinel_for<_I1> S1, input_iterator I2, sentinel_for<_I2> S2,
class Pred = ranges::equal_to, class Proj1 = identity, class Proj2 = identity>
requires indirectly_comparable<I1, I2, Pred, Proj1, Proj2>
Expand Down Expand Up @@ -283,6 +282,17 @@ namespace ranges {
requires permutable<iterator_t<R>>
constexpr borrowed_iterator_t<R> ranges::reverse(R&& r); // since C++20
template<random_access_iterator I, sentinel_for<I> S, class Comp = ranges::less,
class Proj = identity>
requires sortable<I, Comp, Proj>
constexpr I
sort(I first, S last, Comp comp = {}, Proj proj = {}); // since C++20
template<random_access_range R, class Comp = ranges::less, class Proj = identity>
requires sortable<iterator_t<R>, Comp, Proj>
constexpr borrowed_iterator_t<R>
sort(R&& r, Comp comp = {}, Proj proj = {}); // since C++20
template<class T, output_iterator<const T&> O, sentinel_for<O> S>
constexpr O ranges::fill(O first, S last, const T& value); // since C++20
Expand Down Expand Up @@ -1190,6 +1200,7 @@ template <class BidirectionalIterator, class Compare>
#include <__algorithm/ranges_replace.h>
#include <__algorithm/ranges_replace_if.h>
#include <__algorithm/ranges_reverse.h>
#include <__algorithm/ranges_sort.h>
#include <__algorithm/ranges_swap_ranges.h>
#include <__algorithm/ranges_transform.h>
#include <__algorithm/ranges_upper_bound.h>
Expand Down
2 changes: 2 additions & 0 deletions libcxx/include/module.modulemap.in
Expand Up @@ -284,6 +284,7 @@ module std [system] {
module lexicographical_compare { private header "__algorithm/lexicographical_compare.h" }
module lower_bound { private header "__algorithm/lower_bound.h" }
module make_heap { private header "__algorithm/make_heap.h" }
module make_projected { private header "__algorithm/make_projected.h" }
module max { private header "__algorithm/max.h" }
module max_element { private header "__algorithm/max_element.h" }
module merge { private header "__algorithm/merge.h" }
Expand Down Expand Up @@ -341,6 +342,7 @@ module std [system] {
module ranges_replace { private header "__algorithm/ranges_replace.h" }
module ranges_replace_if { private header "__algorithm/ranges_replace_if.h" }
module ranges_reverse { private header "__algorithm/ranges_reverse.h" }
module ranges_sort { private header "__algorithm/ranges_sort.h" }
module ranges_swap_ranges { private header "__algorithm/ranges_swap_ranges.h" }
module ranges_transform { private header "__algorithm/ranges_transform.h" }
module ranges_upper_bound { private header "__algorithm/ranges_upper_bound.h" }
Expand Down
2 changes: 2 additions & 0 deletions libcxx/src/algorithm.cpp
Expand Up @@ -10,6 +10,8 @@

_LIBCPP_BEGIN_NAMESPACE_STD

// TODO(varconst): this currently doesn't benefit `ranges::sort` because it uses `ranges::less` instead of `__less`.

template void __sort<__less<char>&, char*>(char*, char*, __less<char>&);
#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
template void __sort<__less<wchar_t>&, wchar_t*>(wchar_t*, wchar_t*, __less<wchar_t>&);
Expand Down
Expand Up @@ -207,8 +207,8 @@ constexpr bool all_the_algorithms()
//(void)std::ranges::set_symmetric_difference(a, b, first2, Less(&copies)); assert(copies == 0);
//(void)std::ranges::set_union(first, mid, mid, last, first2, Less(&copies)); assert(copies == 0);
//(void)std::ranges::set_union(a, b, first2, Less(&copies)); assert(copies == 0);
//(void)std::ranges::sort(first, last, Less(&copies)); assert(copies == 0);
//(void)std::ranges::sort(a, Less(&copies)); assert(copies == 0);
(void)std::ranges::sort(first, last, Less(&copies)); assert(copies == 0);
(void)std::ranges::sort(a, Less(&copies)); assert(copies == 0);
//(void)std::ranges::sort_heap(first, last, Less(&copies)); assert(copies == 0);
//(void)std::ranges::sort_heap(a, Less(&copies)); assert(copies == 0);
//if (!std::is_constant_evaluated()) { (void)std::ranges::stable_partition(first, last, UnaryTrue(&copies)); assert(copies == 0); }
Expand Down
Expand Up @@ -198,8 +198,8 @@ constexpr bool all_the_algorithms()
//(void)std::ranges::set_symmetric_difference(a, b, first2, Less(), Proj(&copies), Proj(&copies)); assert(copies == 0);
//(void)std::ranges::set_union(first, mid, mid, last, first2, Less(), Proj(&copies), Proj(&copies)); assert(copies == 0);
//(void)std::ranges::set_union(a, b, first2, Less(), Proj(&copies), Proj(&copies)); assert(copies == 0);
//(void)std::ranges::sort(first, last, Less(), Proj(&copies)); assert(copies == 0);
//(void)std::ranges::sort(a, Less(), Proj(&copies)); assert(copies == 0);
(void)std::ranges::sort(first, last, Less(), Proj(&copies)); assert(copies == 0);
(void)std::ranges::sort(a, Less(), Proj(&copies)); assert(copies == 0);
//(void)std::ranges::sort_heap(first, last, Less(), Proj(&copies)); assert(copies == 0);
//(void)std::ranges::sort_heap(a, Less(), Proj(&copies)); assert(copies == 0);
//if (!std::is_constant_evaluated()) { (void)std::ranges::stable_partition(first, last, UnaryTrue(), Proj(&copies)); assert(copies == 0); }
Expand Down
2 changes: 2 additions & 0 deletions libcxx/test/libcxx/private_headers.verify.cpp
Expand Up @@ -82,6 +82,7 @@ END-SCRIPT
#include <__algorithm/lexicographical_compare.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/lexicographical_compare.h'}}
#include <__algorithm/lower_bound.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/lower_bound.h'}}
#include <__algorithm/make_heap.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/make_heap.h'}}
#include <__algorithm/make_projected.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/make_projected.h'}}
#include <__algorithm/max.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/max.h'}}
#include <__algorithm/max_element.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/max_element.h'}}
#include <__algorithm/merge.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/merge.h'}}
Expand Down Expand Up @@ -139,6 +140,7 @@ END-SCRIPT
#include <__algorithm/ranges_replace.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_replace.h'}}
#include <__algorithm/ranges_replace_if.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_replace_if.h'}}
#include <__algorithm/ranges_reverse.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_reverse.h'}}
#include <__algorithm/ranges_sort.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_sort.h'}}
#include <__algorithm/ranges_swap_ranges.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_swap_ranges.h'}}
#include <__algorithm/ranges_transform.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_transform.h'}}
#include <__algorithm/ranges_upper_bound.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_upper_bound.h'}}
Expand Down

0 comments on commit ff3989e

Please sign in to comment.