https://github.com/vhscampos updated https://github.com/llvm/llvm-project/pull/198797
>From b7eb019be4b4885cbd0815844d7c81a0c80ae29b Mon Sep 17 00:00:00 2001 From: Victor Campos <[email protected]> Date: Wed, 20 May 2026 12:57:24 +0100 Subject: [PATCH 1/2] [libc] Add Annex K qsort_s definition and tests This patch introduces Annex K's `qsort_s`. Since its semantics are very similar to the ones for `qsort_r`, the two share the same testing logic, except that for `qsort_s` we also test the constraint violation semantics. --- libc/config/baremetal/aarch64/entrypoints.txt | 1 + libc/config/baremetal/arm/entrypoints.txt | 1 + libc/config/linux/aarch64/entrypoints.txt | 1 + libc/config/linux/x86_64/entrypoints.txt | 1 + libc/include/CMakeLists.txt | 1 + libc/include/llvm-libc-types/CMakeLists.txt | 1 + .../llvm-libc-types/__qsortscompare_t.h | 14 ++++ libc/include/stdlib.yaml | 13 ++++ libc/src/stdlib/CMakeLists.txt | 16 +++++ libc/src/stdlib/qsort_s.cpp | 47 +++++++++++++ libc/src/stdlib/qsort_s.h | 24 +++++++ libc/test/src/stdlib/CMakeLists.txt | 16 +++++ libc/test/src/stdlib/qsort_s_test.cpp | 68 +++++++++++++++++++ 13 files changed, 204 insertions(+) create mode 100644 libc/include/llvm-libc-types/__qsortscompare_t.h create mode 100644 libc/src/stdlib/qsort_s.cpp create mode 100644 libc/src/stdlib/qsort_s.h create mode 100644 libc/test/src/stdlib/qsort_s_test.cpp diff --git a/libc/config/baremetal/aarch64/entrypoints.txt b/libc/config/baremetal/aarch64/entrypoints.txt index dcb50135232e2..d7dacdedb479e 100644 --- a/libc/config/baremetal/aarch64/entrypoints.txt +++ b/libc/config/baremetal/aarch64/entrypoints.txt @@ -257,6 +257,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.stdlib.memalignment libc.src.stdlib.qsort libc.src.stdlib.qsort_r + libc.src.stdlib.qsort_s libc.src.stdlib.rand libc.src.stdlib.realloc libc.src.stdlib.srand diff --git a/libc/config/baremetal/arm/entrypoints.txt b/libc/config/baremetal/arm/entrypoints.txt index fac62bac939cc..29e58cb4d24ab 100644 --- a/libc/config/baremetal/arm/entrypoints.txt +++ b/libc/config/baremetal/arm/entrypoints.txt @@ -257,6 +257,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.stdlib.memalignment libc.src.stdlib.qsort libc.src.stdlib.qsort_r + libc.src.stdlib.qsort_s libc.src.stdlib.rand libc.src.stdlib.realloc libc.src.stdlib.srand diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt index 35f91c18f4bfb..7047cc6c3e853 100644 --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -196,6 +196,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.stdlib.memalignment libc.src.stdlib.qsort libc.src.stdlib.qsort_r + libc.src.stdlib.qsort_s libc.src.stdlib.rand libc.src.stdlib.srand libc.src.stdlib.strfromd diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index eda0426ff2578..5cc74378b3c26 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -208,6 +208,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.stdlib.memalignment libc.src.stdlib.qsort libc.src.stdlib.qsort_r + libc.src.stdlib.qsort_s libc.src.stdlib.rand libc.src.stdlib.srand libc.src.stdlib.strfromd diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt index 6a02241b7d6a2..241639e9a95bf 100644 --- a/libc/include/CMakeLists.txt +++ b/libc/include/CMakeLists.txt @@ -434,6 +434,7 @@ add_header_macro( .llvm-libc-types.__atexithandler_t .llvm-libc-types.__qsortcompare_t .llvm-libc-types.__qsortrcompare_t + .llvm-libc-types.__qsortscompare_t .llvm-libc-types.__search_compare_t .llvm-libc-types.constraint_handler_t .llvm-libc-types.div_t diff --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt index 2904e0d07f76c..519a714c81406 100644 --- a/libc/include/llvm-libc-types/CMakeLists.txt +++ b/libc/include/llvm-libc-types/CMakeLists.txt @@ -24,6 +24,7 @@ add_header(__pthread_start_t HDR __pthread_start_t.h) add_header(__pthread_tss_dtor_t HDR __pthread_tss_dtor_t.h) add_header(__qsortcompare_t HDR __qsortcompare_t.h) add_header(__qsortrcompare_t HDR __qsortrcompare_t.h) +add_header(__qsortscompare_t HDR __qsortscompare_t.h) add_header(__thread_type HDR __thread_type.h) add_header(blkcnt_t HDR blkcnt_t.h) add_header(blksize_t HDR blksize_t.h) diff --git a/libc/include/llvm-libc-types/__qsortscompare_t.h b/libc/include/llvm-libc-types/__qsortscompare_t.h new file mode 100644 index 0000000000000..8f88687bb1de5 --- /dev/null +++ b/libc/include/llvm-libc-types/__qsortscompare_t.h @@ -0,0 +1,14 @@ +//===-- Definition of type __qsortscompare_t ------------------------------===// +// +// 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 LLVM_LIBC_TYPES___QSORTSCOMPARE_T_H +#define LLVM_LIBC_TYPES___QSORTSCOMPARE_T_H + +typedef int (*__qsortscompare_t)(const void *, const void *, void *); + +#endif // LLVM_LIBC_TYPES___QSORTSCOMPARE_T_H diff --git a/libc/include/stdlib.yaml b/libc/include/stdlib.yaml index 4c958cd9d28ad..25c898c33e09d 100644 --- a/libc/include/stdlib.yaml +++ b/libc/include/stdlib.yaml @@ -14,6 +14,8 @@ types: - type_name: __atexithandler_t - type_name: __qsortcompare_t - type_name: __qsortrcompare_t + - type_name: __qsortscompare_t + guard: LIBC_HAS_ANNEX_K - type_name: __search_compare_t - type_name: constraint_handler_t - type_name: div_t @@ -191,6 +193,17 @@ functions: - type: size_t - type: __qsortrcompare_t - type: void * + - name: qsort_s + standards: + - stdc + return_type: errno_t + arguments: + - type: void * + - type: rsize_t + - type: rsize_t + - type: __qsortscompare_t + - type: void * + guard: LIBC_HAS_ANNEX_K - name: quick_exit standards: - stdc diff --git a/libc/src/stdlib/CMakeLists.txt b/libc/src/stdlib/CMakeLists.txt index 16c9874f01017..8e5ebdec96196 100644 --- a/libc/src/stdlib/CMakeLists.txt +++ b/libc/src/stdlib/CMakeLists.txt @@ -356,6 +356,22 @@ add_entrypoint_object( libc.hdr.types.size_t ) +add_entrypoint_object( + qsort_s + SRCS + qsort_s.cpp + HDRS + qsort_s.h + DEPENDS + .qsort_util + libc.hdr.stdint_proxy + libc.hdr.types.errno_t + libc.hdr.types.rsize_t + libc.src.__support.common + libc.src.__support.constraint_handler + libc.src.__support.macros.config +) + add_object_library( rand_util SRCS diff --git a/libc/src/stdlib/qsort_s.cpp b/libc/src/stdlib/qsort_s.cpp new file mode 100644 index 0000000000000..bd7480a86b487 --- /dev/null +++ b/libc/src/stdlib/qsort_s.cpp @@ -0,0 +1,47 @@ +//===-- Implementation of qsort -------------------------------------------===// +// +// 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 "src/stdlib/qsort_s.h" +#define __STDC_WANT_LIB_EXT1__ 1 +#include "hdr/stdint_proxy.h" +#undef __STDC_WANT_LIB_EXT1__ +#include "src/__support/common.h" +#include "src/__support/constraint_handler.h" +#include "src/__support/macros/config.h" +#include "src/stdlib/qsort_util.h" + +#define ERRNO_T_FAIL 1 + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(errno_t, qsort_s, + (void *array, rsize_t array_size, rsize_t elem_size, + int (*compare)(const void *, const void *, void *), + void *context)) { + const char *constraint_violation_msg = 0; + if (array_size > RSIZE_MAX) { + constraint_violation_msg = + "qsort_s: array_size cannot be greater than RSIZE_MAX"; + } else if (elem_size > RSIZE_MAX) { + constraint_violation_msg = + "qsort_s: elem_size cannot be greater than RSIZE_MAX"; + } else if ((array_size != 0) && (array == 0 || compare == 0)) { + constraint_violation_msg = + "qsort_s: if array_size is not equal to zero, then neither array nor " + "compare can be a null pointer"; + } + if (constraint_violation_msg) { + libc_global_constraint_handler(constraint_violation_msg, 0, ERRNO_T_FAIL); + return ERRNO_T_FAIL; + } + + internal::unstable_sort(array, array_size, elem_size, compare, context); + return 0; +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdlib/qsort_s.h b/libc/src/stdlib/qsort_s.h new file mode 100644 index 0000000000000..74ac2f9e49c7a --- /dev/null +++ b/libc/src/stdlib/qsort_s.h @@ -0,0 +1,24 @@ +//===-- Implementation header for qsort_s------------------------*- C++ -*-===// +// +// 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 LLVM_LIBC_SRC_STDLIB_QSORT_S_H +#define LLVM_LIBC_SRC_STDLIB_QSORT_S_H + +#include "hdr/types/errno_t.h" +#include "hdr/types/rsize_t.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +errno_t qsort_s(void *array, rsize_t array_size, rsize_t elem_size, + int (*compare)(const void *, const void *, void *), + void *context); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_STDLIB_QSORT_H diff --git a/libc/test/src/stdlib/CMakeLists.txt b/libc/test/src/stdlib/CMakeLists.txt index 270a7a2291293..d7b60bbe3344a 100644 --- a/libc/test/src/stdlib/CMakeLists.txt +++ b/libc/test/src/stdlib/CMakeLists.txt @@ -366,6 +366,22 @@ add_libc_test( libc.src.stdlib.qsort_r ) +add_libc_test( + qsort_s_test + SUITE + libc-stdlib-tests + SRCS + qsort_s_test.cpp + HDRS + QsortReentrantTest.h + DEPENDS + libc.hdr.stdint_proxy + libc.hdr.types.errno_t + libc.hdr.types.rsize_t + libc.src.stdlib.qsort_s + libc.test.UnitTest.ConstraintHandlerCheckingTest +) + add_libc_test( rand_test SUITE diff --git a/libc/test/src/stdlib/qsort_s_test.cpp b/libc/test/src/stdlib/qsort_s_test.cpp new file mode 100644 index 0000000000000..798e9827077c2 --- /dev/null +++ b/libc/test/src/stdlib/qsort_s_test.cpp @@ -0,0 +1,68 @@ +//===-- Unittests for qsort_s ---------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#define __STDC_WANT_LIB_EXT1__ 1 +#include "QsortReentrantTest.h" +#include "hdr/stdint_proxy.h" +#include "hdr/types/errno_t.h" +#include "hdr/types/rsize_t.h" +#include "src/stdlib/qsort_s.h" +#include "test/UnitTest/ConstraintHandlerCheckingTest.h" +#include "test/UnitTest/Test.h" + +QSORTREENTRANT_TEST(QsortS, LIBC_NAMESPACE::qsort_s, rsize_t) + +using LlvmLibcQsortSConstraintTest = + LIBC_NAMESPACE::testing::ConstraintHandlerCheckingTest; + +static int compare(const void *a, const void *b, void *) { + const int *a_int = static_cast<const int *>(a); + const int *b_int = static_cast<const int *>(b); + return *a_int - *b_int; +} + +TEST_F(LlvmLibcQsortSConstraintTest, ArraySizeGreaterThanRsizeMax) { + int array[] = {1, 3, 2}; + errno_t retval = + LIBC_NAMESPACE::qsort_s(array, RSIZE_MAX + 1, sizeof(int), compare, 0); + EXPECT_NE(retval, 0); + EXPECT_STREQ(buffer, "qsort_s: array_size cannot be greater than RSIZE_MAX"); + EXPECT_EQ(array[0], 1); + EXPECT_EQ(array[1], 3); + EXPECT_EQ(array[2], 2); +} + +TEST_F(LlvmLibcQsortSConstraintTest, ElemSizeGreaterThanRsizeMax) { + int array[] = {1, 3, 2}; + errno_t retval = LIBC_NAMESPACE::qsort_s(array, sizeof(array) / sizeof(int), + RSIZE_MAX + 1, compare, 0); + EXPECT_NE(retval, 0); + EXPECT_STREQ(buffer, "qsort_s: elem_size cannot be greater than RSIZE_MAX"); + EXPECT_EQ(array[0], 1); + EXPECT_EQ(array[1], 3); + EXPECT_EQ(array[2], 2); +} + +TEST_F(LlvmLibcQsortSConstraintTest, ArrayPointerIsNull) { + errno_t retval = LIBC_NAMESPACE::qsort_s(0, 5, sizeof(int), compare, 0); + EXPECT_NE(retval, 0); + EXPECT_STREQ(buffer, "qsort_s: if array_size is not equal to zero, then " + "neither array nor compare can be a null pointer"); +} + +TEST_F(LlvmLibcQsortSConstraintTest, ComparePointerIsNull) { + int array[] = {1, 3, 2}; + errno_t retval = LIBC_NAMESPACE::qsort_s(array, sizeof(array) / sizeof(int), + sizeof(int), 0, 0); + EXPECT_NE(retval, 0); + EXPECT_STREQ(buffer, "qsort_s: if array_size is not equal to zero, then " + "neither array nor compare can be a null pointer"); + EXPECT_EQ(array[0], 1); + EXPECT_EQ(array[1], 3); + EXPECT_EQ(array[2], 2); +} >From 9698d0bd7b48afdc8f8a34ce94b07fcdd13f9caa Mon Sep 17 00:00:00 2001 From: Victor Campos <[email protected]> Date: Fri, 19 Jun 2026 17:08:33 +0100 Subject: [PATCH 2/2] Address comments --- libc/src/stdlib/qsort_s.cpp | 13 ++++++++----- libc/src/stdlib/qsort_s.h | 9 +++++++-- libc/test/src/stdlib/qsort_s_test.cpp | 21 ++++++++++++++------- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/libc/src/stdlib/qsort_s.cpp b/libc/src/stdlib/qsort_s.cpp index bd7480a86b487..695f687eab420 100644 --- a/libc/src/stdlib/qsort_s.cpp +++ b/libc/src/stdlib/qsort_s.cpp @@ -1,10 +1,15 @@ -//===-- Implementation of qsort -------------------------------------------===// +//===----------------------------------------------------------------------===// // // 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 // //===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains the implementation of qsort_s. +/// +//===----------------------------------------------------------------------===// #include "src/stdlib/qsort_s.h" #define __STDC_WANT_LIB_EXT1__ 1 @@ -15,8 +20,6 @@ #include "src/__support/macros/config.h" #include "src/stdlib/qsort_util.h" -#define ERRNO_T_FAIL 1 - namespace LIBC_NAMESPACE_DECL { LLVM_LIBC_FUNCTION(errno_t, qsort_s, @@ -36,8 +39,8 @@ LLVM_LIBC_FUNCTION(errno_t, qsort_s, "compare can be a null pointer"; } if (constraint_violation_msg) { - libc_global_constraint_handler(constraint_violation_msg, 0, ERRNO_T_FAIL); - return ERRNO_T_FAIL; + libc_global_constraint_handler(constraint_violation_msg, 0, 1); + return 1; } internal::unstable_sort(array, array_size, elem_size, compare, context); diff --git a/libc/src/stdlib/qsort_s.h b/libc/src/stdlib/qsort_s.h index 74ac2f9e49c7a..a01bc29061ef4 100644 --- a/libc/src/stdlib/qsort_s.h +++ b/libc/src/stdlib/qsort_s.h @@ -1,10 +1,15 @@ -//===-- Implementation header for qsort_s------------------------*- C++ -*-===// +//===----------------------------------------------------------------------===// // // 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 // //===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains the implementation header for qsort_s. +/// +//===----------------------------------------------------------------------===// #ifndef LLVM_LIBC_SRC_STDLIB_QSORT_S_H #define LLVM_LIBC_SRC_STDLIB_QSORT_S_H @@ -21,4 +26,4 @@ errno_t qsort_s(void *array, rsize_t array_size, rsize_t elem_size, } // namespace LIBC_NAMESPACE_DECL -#endif // LLVM_LIBC_SRC_STDLIB_QSORT_H +#endif // LLVM_LIBC_SRC_STDLIB_QSORT_S_H diff --git a/libc/test/src/stdlib/qsort_s_test.cpp b/libc/test/src/stdlib/qsort_s_test.cpp index 798e9827077c2..13370b7e7bb14 100644 --- a/libc/test/src/stdlib/qsort_s_test.cpp +++ b/libc/test/src/stdlib/qsort_s_test.cpp @@ -1,10 +1,15 @@ -//===-- Unittests for qsort_s ---------------------------------------------===// +//===----------------------------------------------------------------------===// // // 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 // //===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains unit tests for qsort_s. +/// +//===----------------------------------------------------------------------===// #define __STDC_WANT_LIB_EXT1__ 1 #include "QsortReentrantTest.h" @@ -28,10 +33,11 @@ static int compare(const void *a, const void *b, void *) { TEST_F(LlvmLibcQsortSConstraintTest, ArraySizeGreaterThanRsizeMax) { int array[] = {1, 3, 2}; + EXPECT_FALSE(error_flag); errno_t retval = LIBC_NAMESPACE::qsort_s(array, RSIZE_MAX + 1, sizeof(int), compare, 0); EXPECT_NE(retval, 0); - EXPECT_STREQ(buffer, "qsort_s: array_size cannot be greater than RSIZE_MAX"); + EXPECT_TRUE(error_flag); EXPECT_EQ(array[0], 1); EXPECT_EQ(array[1], 3); EXPECT_EQ(array[2], 2); @@ -39,29 +45,30 @@ TEST_F(LlvmLibcQsortSConstraintTest, ArraySizeGreaterThanRsizeMax) { TEST_F(LlvmLibcQsortSConstraintTest, ElemSizeGreaterThanRsizeMax) { int array[] = {1, 3, 2}; + EXPECT_FALSE(error_flag); errno_t retval = LIBC_NAMESPACE::qsort_s(array, sizeof(array) / sizeof(int), RSIZE_MAX + 1, compare, 0); EXPECT_NE(retval, 0); - EXPECT_STREQ(buffer, "qsort_s: elem_size cannot be greater than RSIZE_MAX"); + EXPECT_TRUE(error_flag); EXPECT_EQ(array[0], 1); EXPECT_EQ(array[1], 3); EXPECT_EQ(array[2], 2); } TEST_F(LlvmLibcQsortSConstraintTest, ArrayPointerIsNull) { + EXPECT_FALSE(error_flag); errno_t retval = LIBC_NAMESPACE::qsort_s(0, 5, sizeof(int), compare, 0); EXPECT_NE(retval, 0); - EXPECT_STREQ(buffer, "qsort_s: if array_size is not equal to zero, then " - "neither array nor compare can be a null pointer"); + EXPECT_TRUE(error_flag); } TEST_F(LlvmLibcQsortSConstraintTest, ComparePointerIsNull) { int array[] = {1, 3, 2}; + EXPECT_FALSE(error_flag); errno_t retval = LIBC_NAMESPACE::qsort_s(array, sizeof(array) / sizeof(int), sizeof(int), 0, 0); EXPECT_NE(retval, 0); - EXPECT_STREQ(buffer, "qsort_s: if array_size is not equal to zero, then " - "neither array nor compare can be a null pointer"); + EXPECT_TRUE(error_flag); EXPECT_EQ(array[0], 1); EXPECT_EQ(array[1], 3); EXPECT_EQ(array[2], 2); _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
