[libcxx-commits] [libcxx] [libc++] std::cmp_less and other integer comparison functions could be improved (PR #151332)
via libcxx-commits
libcxx-commits at lists.llvm.org
Sat Sep 6 05:48:50 PDT 2025
https://github.com/kisuhorikka updated https://github.com/llvm/llvm-project/pull/151332
>From 711144331e712f31e09d71ecbfbca12b4d07e8e1 Mon Sep 17 00:00:00 2001
From: kisuhorikka <kisuhorikka at gmail.com>
Date: Wed, 30 Jul 2025 20:53:05 +0800
Subject: [PATCH 1/2] [libc++] std::cmp_less and other integer comparison
functions could be improved
---
libcxx/include/__utility/cmp.h | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/libcxx/include/__utility/cmp.h b/libcxx/include/__utility/cmp.h
index 14dc0c154c040..4c29f09628095 100644
--- a/libcxx/include/__utility/cmp.h
+++ b/libcxx/include/__utility/cmp.h
@@ -28,7 +28,13 @@ _LIBCPP_BEGIN_NAMESPACE_STD
template <__signed_or_unsigned_integer _Tp, __signed_or_unsigned_integer _Up>
_LIBCPP_HIDE_FROM_ABI constexpr bool cmp_equal(_Tp __t, _Up __u) noexcept {
- if constexpr (is_signed_v<_Tp> == is_signed_v<_Up>)
+ if constexpr (sizeof(_Tp) < sizeof(int) && sizeof(_Up) < sizeof(int)) {
+ __builtin_assume(__t < numeric_limits<int>::max() && __u < numeric_limits<int>::max());
+ return static_cast<int>(__t) == static_cast<int>(__u);
+ } else if constexpr (sizeof(_Tp) < sizeof(long long) && sizeof(_Up) < sizeof(long long)) {
+ __builtin_assume(__t < numeric_limits<long long>::max() && __u < numeric_limits<long long>::max());
+ return static_cast<long long>(__t) == static_cast<long long>(__u);
+ } else if constexpr (is_signed_v<_Tp> == is_signed_v<_Up>)
return __t == __u;
else if constexpr (is_signed_v<_Tp>)
return __t < 0 ? false : make_unsigned_t<_Tp>(__t) == __u;
@@ -43,7 +49,13 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool cmp_not_equal(_Tp __t, _Up __u) noexcept {
template <__signed_or_unsigned_integer _Tp, __signed_or_unsigned_integer _Up>
_LIBCPP_HIDE_FROM_ABI constexpr bool cmp_less(_Tp __t, _Up __u) noexcept {
- if constexpr (is_signed_v<_Tp> == is_signed_v<_Up>)
+ if constexpr (sizeof(_Tp) < sizeof(int) && sizeof(_Up) < sizeof(int)) {
+ __builtin_assume(__t < numeric_limits<int>::max() && __u < numeric_limits<int>::max());
+ return static_cast<int>(__t) < static_cast<int>(__u);
+ } else if constexpr (sizeof(_Tp) < sizeof(long long) && sizeof(_Up) < sizeof(long long)) {
+ __builtin_assume(__t < numeric_limits<long long>::max() && __u < numeric_limits<long long>::max());
+ return static_cast<long long>(__t) < static_cast<long long>(__u);
+ } else if constexpr (is_signed_v<_Tp> == is_signed_v<_Up>)
return __t < __u;
else if constexpr (is_signed_v<_Tp>)
return __t < 0 ? true : make_unsigned_t<_Tp>(__t) < __u;
>From 1d74e7a4d6d5fd876cd19e5e9e86f2a6315c834a Mon Sep 17 00:00:00 2001
From: kisuhorikka <kisuhorikka at gmail.com>
Date: Thu, 31 Jul 2025 13:08:18 +0800
Subject: [PATCH 2/2] [libc++] std::cmp_less and other integer comparison
functions could be improved with _LIBCPP_ASSUME
---
libcxx/include/__utility/cmp.h | 24 ++--
libcxx/test/benchmarks/utility/cmp.bench.cpp | 118 +++++++++++++++++++
2 files changed, 130 insertions(+), 12 deletions(-)
create mode 100644 libcxx/test/benchmarks/utility/cmp.bench.cpp
diff --git a/libcxx/include/__utility/cmp.h b/libcxx/include/__utility/cmp.h
index 4c29f09628095..68864e23e0397 100644
--- a/libcxx/include/__utility/cmp.h
+++ b/libcxx/include/__utility/cmp.h
@@ -26,16 +26,18 @@ _LIBCPP_BEGIN_NAMESPACE_STD
#if _LIBCPP_STD_VER >= 20
+template <typename _Tp, typename _Ip>
+concept __comparison_can_promote_to =
+ sizeof(_Tp) < sizeof(_Ip) || (sizeof(_Tp) == sizeof(_Ip) && __signed_integer<_Tp>);
+
template <__signed_or_unsigned_integer _Tp, __signed_or_unsigned_integer _Up>
_LIBCPP_HIDE_FROM_ABI constexpr bool cmp_equal(_Tp __t, _Up __u) noexcept {
- if constexpr (sizeof(_Tp) < sizeof(int) && sizeof(_Up) < sizeof(int)) {
- __builtin_assume(__t < numeric_limits<int>::max() && __u < numeric_limits<int>::max());
+ if constexpr (is_signed_v<_Tp> == is_signed_v<_Up>)
+ return __t == __u;
+ else if constexpr (__comparison_can_promote_to<_Tp, int> && __comparison_can_promote_to<_Up, int>)
return static_cast<int>(__t) == static_cast<int>(__u);
- } else if constexpr (sizeof(_Tp) < sizeof(long long) && sizeof(_Up) < sizeof(long long)) {
- __builtin_assume(__t < numeric_limits<long long>::max() && __u < numeric_limits<long long>::max());
+ else if constexpr (__comparison_can_promote_to<_Tp, long long> && __comparison_can_promote_to<_Up, long long>)
return static_cast<long long>(__t) == static_cast<long long>(__u);
- } else if constexpr (is_signed_v<_Tp> == is_signed_v<_Up>)
- return __t == __u;
else if constexpr (is_signed_v<_Tp>)
return __t < 0 ? false : make_unsigned_t<_Tp>(__t) == __u;
else
@@ -49,14 +51,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool cmp_not_equal(_Tp __t, _Up __u) noexcept {
template <__signed_or_unsigned_integer _Tp, __signed_or_unsigned_integer _Up>
_LIBCPP_HIDE_FROM_ABI constexpr bool cmp_less(_Tp __t, _Up __u) noexcept {
- if constexpr (sizeof(_Tp) < sizeof(int) && sizeof(_Up) < sizeof(int)) {
- __builtin_assume(__t < numeric_limits<int>::max() && __u < numeric_limits<int>::max());
+ if constexpr (is_signed_v<_Tp> == is_signed_v<_Up>)
+ return __t < __u;
+ else if constexpr (__comparison_can_promote_to<_Tp, int> && __comparison_can_promote_to<_Up, int>)
return static_cast<int>(__t) < static_cast<int>(__u);
- } else if constexpr (sizeof(_Tp) < sizeof(long long) && sizeof(_Up) < sizeof(long long)) {
- __builtin_assume(__t < numeric_limits<long long>::max() && __u < numeric_limits<long long>::max());
+ else if constexpr (__comparison_can_promote_to<_Tp, long long> && __comparison_can_promote_to<_Up, long long>)
return static_cast<long long>(__t) < static_cast<long long>(__u);
- } else if constexpr (is_signed_v<_Tp> == is_signed_v<_Up>)
- return __t < __u;
else if constexpr (is_signed_v<_Tp>)
return __t < 0 ? true : make_unsigned_t<_Tp>(__t) < __u;
else
diff --git a/libcxx/test/benchmarks/utility/cmp.bench.cpp b/libcxx/test/benchmarks/utility/cmp.bench.cpp
new file mode 100644
index 0000000000000..7db834679ea33
--- /dev/null
+++ b/libcxx/test/benchmarks/utility/cmp.bench.cpp
@@ -0,0 +1,118 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include <utility>
+#include "../CartesianBenchmarks.h"
+#include "benchmark/benchmark.h"
+
+namespace {
+
+enum ValueType : size_t {
+ SChar,
+ UChar,
+ Short,
+ UShort,
+ Int,
+ UInt,
+ Long,
+ ULong,
+ LongLong,
+ ULongLong,
+#ifndef TEST_HAS_NO_INT128
+ Int128,
+ UInt128,
+#endif
+};
+
+struct AllValueTypes : EnumValuesAsTuple<AllValueTypes, ValueType, 6> {
+ static constexpr const char* Names[] = {
+ "schar",
+ "uchar",
+ "short",
+ "ushort",
+ "int",
+ "uint",
+ "long",
+ "ulong",
+ "longlong",
+ "ulonglong",
+#ifndef TEST_HAS_NO_INT128
+ "int128",
+ "uint128"
+#endif
+ };
+};
+
+using TestType =
+ std::tuple< signed char,
+ unsigned char,
+ short,
+ unsigned short,
+ int,
+ unsigned int,
+ long,
+ unsigned long,
+ long long,
+ unsigned long long,
+#ifndef TEST_HAS_NO_INT128
+ __int128_t,
+ __uint128_t
+#endif
+ >;
+
+template <typename TType, typename UType>
+struct CmpEqual {
+ static void run(benchmark::State& state) {
+ using T = std::tuple_element_t<TType::value, TestType>;
+ using U = std::tuple_element_t<UType::value, TestType>;
+
+ T x = T{127};
+ U y = U{123};
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(x);
+ benchmark::DoNotOptimize(y);
+ benchmark::DoNotOptimize(std::cmp_equal(x, y));
+ }
+ }
+
+ static std::string name() { return "BM_CmpEqual" + TType::name() + UType::name(); }
+};
+
+template <typename TType, typename UType>
+struct CmpLess {
+ static void run(benchmark::State& state) {
+ using T = std::tuple_element_t<TType::value, TestType>;
+ using U = std::tuple_element_t<UType::value, TestType>;
+
+ T x = T{127};
+ U y = U{123};
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(x);
+ benchmark::DoNotOptimize(y);
+ benchmark::DoNotOptimize(std::cmp_less(x, y));
+ }
+ }
+
+ static std::string name() { return "BM_CmpLess" + TType::name() + UType::name(); }
+};
+
+} // namespace
+
+int main(int argc, char** argv) {
+ benchmark::Initialize(&argc, argv);
+ if (benchmark::ReportUnrecognizedArguments(argc, argv))
+ return 1;
+
+ makeCartesianProductBenchmark<CmpEqual, AllValueTypes, AllValueTypes>();
+ makeCartesianProductBenchmark<CmpLess, AllValueTypes, AllValueTypes>();
+ benchmark::RunSpecifiedBenchmarks();
+
+ return 0;
+}
More information about the libcxx-commits
mailing list