Skip to content

Commit dc07b8d

Browse files
lntueyuxuanchen1997
authored andcommitted
[libc][stdlib] Implement heap sort. (#98582)
1 parent c676120 commit dc07b8d

File tree

18 files changed

+768
-393
lines changed

18 files changed

+768
-393
lines changed

libc/cmake/modules/LLVMLibCCompileOptionRules.cmake

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,21 @@ function(_get_compile_options_from_flags output_var)
4949
set(${output_var} ${compile_options} PARENT_SCOPE)
5050
endfunction(_get_compile_options_from_flags)
5151

52+
function(_get_compile_options_from_config output_var)
53+
set(config_options "")
54+
55+
if(LIBC_CONF_QSORT_IMPL)
56+
list(APPEND config_options "-DLIBC_QSORT_IMPL=${LIBC_CONF_QSORT_IMPL}")
57+
endif()
58+
59+
set(${output_var} ${config_options} PARENT_SCOPE)
60+
endfunction(_get_compile_options_from_config)
61+
5262
function(_get_common_compile_options output_var flags)
5363
_get_compile_options_from_flags(compile_flags ${flags})
64+
_get_compile_options_from_config(config_flags)
5465

55-
set(compile_options ${LIBC_COMPILE_OPTIONS_DEFAULT} ${compile_flags})
66+
set(compile_options ${LIBC_COMPILE_OPTIONS_DEFAULT} ${compile_flags} ${config_flags})
5667

5768
if(LLVM_COMPILER_IS_GCC_COMPATIBLE)
5869
list(APPEND compile_options "-fpie")

libc/config/baremetal/config.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,10 @@
2222
"LIBC_CONF_FREELIST_MALLOC_BUFFER_SIZE": {
2323
"value": 102400
2424
}
25+
},
26+
"qsort": {
27+
"LIBC_CONF_QSORT_IMPL": {
28+
"value": "LIBC_QSORT_HEAP_SORT"
29+
}
2530
}
2631
}

libc/config/config.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,5 +82,11 @@
8282
"value": 0,
8383
"doc": "Configures optimizations for math functions. Values accepted are LIBC_MATH_SKIP_ACCURATE_PASS, LIBC_MATH_SMALL_TABLES, LIBC_MATH_NO_ERRNO, LIBC_MATH_NO_EXCEPT, and LIBC_MATH_FAST."
8484
}
85+
},
86+
"qsort": {
87+
"LIBC_CONF_QSORT_IMPL": {
88+
"value": "LIBC_QSORT_QUICK_SORT",
89+
"doc": "Configures sorting algorithm for qsort and qsort_r. Values accepted are LIBC_QSORT_QUICK_SORT, LIBC_QSORT_HEAP_SORT."
90+
}
8591
}
8692
}

libc/docs/configure.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ to learn about the defaults for your platform and target.
4444
- ``LIBC_CONF_RAW_MUTEX_DEFAULT_SPIN_COUNT``: Default number of spins before blocking if a mutex is in contention (default to 100).
4545
- ``LIBC_CONF_RWLOCK_DEFAULT_SPIN_COUNT``: Default number of spins before blocking if a rwlock is in contention (default to 100).
4646
- ``LIBC_CONF_TIMEOUT_ENSURE_MONOTONICITY``: Automatically adjust timeout to CLOCK_MONOTONIC (default to true). POSIX API may require CLOCK_REALTIME, which can be unstable and leading to unexpected behavior. This option will convert the real-time timestamp to monotonic timestamp relative to the time of call.
47+
* **"qsort" options**
48+
- ``LIBC_CONF_QSORT_IMPL``: Configures sorting algorithm for qsort and qsort_r. Values accepted are LIBC_QSORT_QUICK_SORT, LIBC_QSORT_HEAP_SORT.
4749
* **"scanf" options**
4850
- ``LIBC_CONF_SCANF_DISABLE_FLOAT``: Disable parsing floating point values in scanf and friends.
4951
- ``LIBC_CONF_SCANF_DISABLE_INDEX_MODE``: Disable index mode in the scanf format string.

libc/src/stdlib/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,9 +258,13 @@ add_entrypoint_object(
258258
add_header_library(
259259
qsort_util
260260
HDRS
261+
qsort_data.h
261262
qsort_util.h
263+
heap_sort.h
264+
quick_sort.h
262265
DEPENDS
263266
libc.include.stdlib
267+
libc.src.__support.CPP.cstddef
264268
)
265269

266270
add_entrypoint_object(

libc/src/stdlib/heap_sort.h

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
//===-- Implementation of heap sort -----------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC_STDLIB_HEAP_SORT_H
10+
#define LLVM_LIBC_SRC_STDLIB_HEAP_SORT_H
11+
12+
#include "src/__support/CPP/cstddef.h"
13+
#include "src/stdlib/qsort_data.h"
14+
15+
namespace LIBC_NAMESPACE_DECL {
16+
namespace internal {
17+
18+
// A simple in-place heapsort implementation.
19+
// Follow the implementation in https://en.wikipedia.org/wiki/Heapsort.
20+
21+
LIBC_INLINE void heap_sort(const Array &array) {
22+
size_t end = array.size();
23+
size_t start = end / 2;
24+
25+
auto left_child = [](size_t i) -> size_t { return 2 * i + 1; };
26+
27+
while (end > 1) {
28+
if (start > 0) {
29+
// Select the next unheapified element to sift down.
30+
--start;
31+
} else {
32+
// Extract the max element of the heap, moving a leaf to root to be sifted
33+
// down.
34+
--end;
35+
array.swap(0, end);
36+
}
37+
38+
// Sift start down the heap.
39+
size_t root = start;
40+
while (left_child(root) < end) {
41+
size_t child = left_child(root);
42+
// If there are two children, set child to the greater.
43+
if (child + 1 < end &&
44+
array.elem_compare(child, array.get(child + 1)) < 0)
45+
++child;
46+
47+
// If the root is less than the greater child
48+
if (array.elem_compare(root, array.get(child)) >= 0)
49+
break;
50+
51+
// Swap the root with the greater child and continue sifting down.
52+
array.swap(root, child);
53+
root = child;
54+
}
55+
}
56+
}
57+
58+
} // namespace internal
59+
} // namespace LIBC_NAMESPACE_DECL
60+
61+
#endif // LLVM_LIBC_SRC_STDLIB_HEAP_SORT_H

libc/src/stdlib/qsort.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,11 @@ LLVM_LIBC_FUNCTION(void, qsort,
2121
if (array == nullptr || array_size == 0 || elem_size == 0)
2222
return;
2323
internal::Comparator c(compare);
24-
internal::quicksort(internal::Array(reinterpret_cast<uint8_t *>(array),
25-
array_size, elem_size, c));
24+
25+
auto arr = internal::Array(reinterpret_cast<uint8_t *>(array), array_size,
26+
elem_size, c);
27+
28+
internal::sort(arr);
2629
}
2730

2831
} // namespace LIBC_NAMESPACE_DECL

libc/src/stdlib/qsort_data.h

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
//===-- Data structures for sorting routines --------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC_STDLIB_QSORT_DATA_H
10+
#define LLVM_LIBC_SRC_STDLIB_QSORT_DATA_H
11+
12+
#include "src/__support/CPP/cstddef.h"
13+
#include "src/__support/macros/config.h"
14+
15+
#include <stdint.h>
16+
17+
namespace LIBC_NAMESPACE_DECL {
18+
namespace internal {
19+
20+
using Compare = int(const void *, const void *);
21+
using CompareWithState = int(const void *, const void *, void *);
22+
23+
enum class CompType { COMPARE, COMPARE_WITH_STATE };
24+
25+
struct Comparator {
26+
union {
27+
Compare *comp_func;
28+
CompareWithState *comp_func_r;
29+
};
30+
const CompType comp_type;
31+
32+
void *arg;
33+
34+
Comparator(Compare *func)
35+
: comp_func(func), comp_type(CompType::COMPARE), arg(nullptr) {}
36+
37+
Comparator(CompareWithState *func, void *arg_val)
38+
: comp_func_r(func), comp_type(CompType::COMPARE_WITH_STATE),
39+
arg(arg_val) {}
40+
41+
#if defined(__clang__)
42+
// Recent upstream changes to -fsanitize=function find more instances of
43+
// function type mismatches. One case is with the comparator passed to this
44+
// class. Libraries will tend to pass comparators that take pointers to
45+
// varying types while this comparator expects to accept const void pointers.
46+
// Ideally those tools would pass a function that strictly accepts const
47+
// void*s to avoid UB, or would use qsort_r to pass their own comparator.
48+
[[clang::no_sanitize("function")]]
49+
#endif
50+
int comp_vals(const void *a, const void *b) const {
51+
if (comp_type == CompType::COMPARE) {
52+
return comp_func(a, b);
53+
} else {
54+
return comp_func_r(a, b, arg);
55+
}
56+
}
57+
};
58+
59+
class Array {
60+
uint8_t *array;
61+
size_t array_size;
62+
size_t elem_size;
63+
Comparator compare;
64+
65+
public:
66+
Array(uint8_t *a, size_t s, size_t e, Comparator c)
67+
: array(a), array_size(s), elem_size(e), compare(c) {}
68+
69+
uint8_t *get(size_t i) const { return array + i * elem_size; }
70+
71+
void swap(size_t i, size_t j) const {
72+
uint8_t *elem_i = get(i);
73+
uint8_t *elem_j = get(j);
74+
for (size_t b = 0; b < elem_size; ++b) {
75+
uint8_t temp = elem_i[b];
76+
elem_i[b] = elem_j[b];
77+
elem_j[b] = temp;
78+
}
79+
}
80+
81+
int elem_compare(size_t i, const uint8_t *other) const {
82+
// An element must compare equal to itself so we don't need to consult the
83+
// user provided comparator.
84+
if (get(i) == other)
85+
return 0;
86+
return compare.comp_vals(get(i), other);
87+
}
88+
89+
size_t size() const { return array_size; }
90+
91+
// Make an Array starting at index |i| and size |s|.
92+
Array make_array(size_t i, size_t s) const {
93+
return Array(get(i), s, elem_size, compare);
94+
}
95+
};
96+
97+
using SortingRoutine = void(const Array &);
98+
99+
} // namespace internal
100+
} // namespace LIBC_NAMESPACE_DECL
101+
102+
#endif // LLVM_LIBC_SRC_STDLIB_QSORT_DATA_H

libc/src/stdlib/qsort_r.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@ LLVM_LIBC_FUNCTION(void, qsort_r,
2222
if (array == nullptr || array_size == 0 || elem_size == 0)
2323
return;
2424
internal::Comparator c(compare, arg);
25-
internal::quicksort(internal::Array(reinterpret_cast<uint8_t *>(array),
26-
array_size, elem_size, c));
25+
auto arr = internal::Array(reinterpret_cast<uint8_t *>(array), array_size,
26+
elem_size, c);
27+
28+
internal::sort(arr);
2729
}
2830

2931
} // namespace LIBC_NAMESPACE_DECL

0 commit comments

Comments
 (0)