llvm-libc: update to LLVM 22

This commit is contained in:
Alex Rønne Petersen
2026-04-26 13:43:13 +02:00
parent 9f2f6aaef5
commit c47c05386e
37 changed files with 1277 additions and 281 deletions
+3 -1
View File
@@ -15,7 +15,9 @@
#include <linux/errno.h>
#include "include/llvm-libc-macros/error-number-macros.h"
#else // __linux__
#elif defined(__APPLE__)
#include <sys/errno.h>
#else // __APPLE__
#include "include/llvm-libc-macros/generic-error-number-macros.h"
#endif
+18
View File
@@ -0,0 +1,18 @@
//===-- stdint.h ----------------------------------------------------------===//
//
// 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_HDR_STDINT_PROXY_H
#define LLVM_LIBC_HDR_STDINT_PROXY_H
// This target is to make sure we have correct build order in full build mode,
// that is `libc.include.stdint` is added to the dependency of all targets
// that use <stdint.h> header.
#include <stdint.h>
#endif // LLVM_LIBC_HDR_STDINT_PROXY_H
+23
View File
@@ -0,0 +1,23 @@
//===-- Definition of wchar_t.h -------------------------------------------===//
//
// 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_HDR_TYPES_WCHAR_T_H
#define LLVM_LIBC_HDR_TYPES_WCHAR_T_H
#ifdef LIBC_FULL_BUILD
#include "include/llvm-libc-types/wchar_t.h"
#else // overlay mode
#include "hdr/wchar_overlay.h"
#endif // LLVM_LIBC_FULL_BUILD
#endif // LLVM_LIBC_HDR_TYPES_WCHAR_T_H
+69
View File
@@ -0,0 +1,69 @@
//===-- Including wchar.h in overlay mode ---------------------------------===//
//
// 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_HDR_WCHAR_OVERLAY_H
#define LLVM_LIBC_HDR_WCHAR_OVERLAY_H
#ifdef LIBC_FULL_BUILD
#error "This header should only be included in overlay mode"
#endif
// Overlay mode
// glibc <wchar.h> header might provide extern inline definitions for few
// functions, causing external alias errors. They are guarded by
// `__USE_EXTERN_INLINES` macro. We temporarily disable `__USE_EXTERN_INLINES`
// macro by defining `__NO_INLINE__` before including <wchar.h>.
// And the same with `__USE_FORTIFY_LEVEL`, which will be temporarily disabled
// with `_FORTIFY_SOURCE`.
#ifdef _FORTIFY_SOURCE
#define LIBC_OLD_FORTIFY_SOURCE _FORTIFY_SOURCE
#undef _FORTIFY_SOURCE
#endif
#ifndef __NO_INLINE__
#define __NO_INLINE__ 1
#define LIBC_SET_NO_INLINE
#endif
#ifdef __USE_EXTERN_INLINES
#define LIBC_OLD_USE_EXTERN_INLINES
#undef __USE_EXTERN_INLINES
#endif
#ifdef __USE_FORTIFY_LEVEL
#define LIBC_OLD_USE_FORTIFY_LEVEL __USE_FORTIFY_LEVEL
#undef __USE_FORTIFY_LEVEL
#define __USE_FORTIFY_LEVEL 0
#endif
#include <wchar.h>
#ifdef LIBC_OLD_FORTIFY_SOURCE
#define _FORTIFY_SOURCE LIBC_OLD_FORTIFY_SOURCE
#undef LIBC_OLD_FORTIFY_SOURCE
#endif
#ifdef LIBC_SET_NO_INLINE
#undef __NO_INLINE__
#undef LIBC_SET_NO_INLINE
#endif
#ifdef LIBC_OLD_USE_FORTIFY_LEVEL
#undef __USE_FORTIFY_LEVEL
#define __USE_FORTIFY_LEVEL LIBC_OLD_USE_FORTIFY_LEVEL
#undef LIBC_OLD_USE_FORTIFY_LEVEL
#endif
#ifdef LIBC_OLD_USE_EXTERN_INLINES
#define __USE_EXTERN_INLINES
#undef LIBC_OLD_USE_EXTERN_INLINES
#endif
#endif // LLVM_LIBC_HDR_WCHAR_OVERLAY_H
@@ -0,0 +1,41 @@
//===-- Detection of _Complex _Float128 compiler builtin type -------------===//
//
// 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_MACROS_CFLOAT128_MACROS_H
#define LLVM_LIBC_MACROS_CFLOAT128_MACROS_H
#include "float-macros.h" // LDBL_MANT_DIG
// Currently, the complex variant of C23 `_Float128` type is only defined as a
// built-in type in GCC 7 or later, for C and in GCC 13 or later, for C++. For
// clang, the complex variant of `__float128` is defined instead, and only on
// x86-64 targets for clang 11 or later.
//
// TODO: Update the complex variant of C23 `_Float128` type detection again when
// clang supports it.
#ifdef __clang__
#if (__clang_major__ >= 11) && \
(defined(__FLOAT128__) || defined(__SIZEOF_FLOAT128__))
// Use _Complex __float128 type. clang uses __SIZEOF_FLOAT128__ or __FLOAT128__
// macro to notify the availability of __float128 type:
// https://reviews.llvm.org/D15120
#define LIBC_TYPES_HAS_CFLOAT128
#endif
#elif defined(__GNUC__)
#if (defined(__STDC_IEC_60559_COMPLEX__) || defined(__SIZEOF_FLOAT128__)) && \
(__GNUC__ >= 13 || (!defined(__cplusplus)))
#define LIBC_TYPES_HAS_CFLOAT128
#endif
#endif
#if !defined(LIBC_TYPES_HAS_CFLOAT128) && (LDBL_MANT_DIG == 113)
#define LIBC_TYPES_HAS_CFLOAT128
#define LIBC_TYPES_CFLOAT128_IS_COMPLEX_LONG_DOUBLE
#endif
#endif // LLVM_LIBC_MACROS_CFLOAT128_MACROS_H
@@ -0,0 +1,20 @@
//===-- Detection of _Complex _Float16 compiler builtin type --------------===//
//
// 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_MACROS_CFLOAT16_MACROS_H
#define LLVM_LIBC_MACROS_CFLOAT16_MACROS_H
#if defined(__FLT16_MANT_DIG__) && \
(!defined(__GNUC__) || __GNUC__ >= 13 || \
(defined(__clang__) && __clang_major__ >= 14)) && \
!defined(__arm__) && !defined(_M_ARM) && !defined(__riscv) && \
!defined(_WIN32)
#define LIBC_TYPES_HAS_CFLOAT16
#endif
#endif // LLVM_LIBC_MACROS_CFLOAT16_MACROS_H
+18
View File
@@ -0,0 +1,18 @@
//===-- Macros defined in wchar.h header file -----------------------------===//
//
// 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_MACROS_WCHAR_MACROS_H
#define LLVM_LIBC_MACROS_WCHAR_MACROS_H
#include "../llvm-libc-types/wint_t.h"
#ifndef WEOF
#define WEOF ((wint_t)(0xffffffffu))
#endif
#endif // LLVM_LIBC_MACROS_WCHAR_MACROS_H
+11 -28
View File
@@ -9,36 +9,19 @@
#ifndef LLVM_LIBC_TYPES_CFLOAT128_H
#define LLVM_LIBC_TYPES_CFLOAT128_H
#include "../llvm-libc-macros/float-macros.h" // LDBL_MANT_DIG
#include "../llvm-libc-macros/cfloat128-macros.h"
// Currently, the complex variant of C23 `_Float128` type is only defined as a
// built-in type in GCC 7 or later, for C and in GCC 13 or later, for C++. For
// clang, the complex variant of `__float128` is defined instead, and only on
// x86-64 targets for clang 11 or later.
//
// TODO: Update the complex variant of C23 `_Float128` type detection again when
// clang supports it.
#ifdef __clang__
#if (__clang_major__ >= 11) && \
(defined(__FLOAT128__) || defined(__SIZEOF_FLOAT128__))
// Use _Complex __float128 type. clang uses __SIZEOF_FLOAT128__ or __FLOAT128__
// macro to notify the availability of __float128 type:
// https://reviews.llvm.org/D15120
#define LIBC_TYPES_HAS_CFLOAT128
#ifdef LIBC_TYPES_HAS_CFLOAT128
#ifndef LIBC_TYPES_CFLOAT128_IS_COMPLEX_LONG_DOUBLE
#if defined(__GNUC__) && !defined(__clang__)
// Remove the workaround when https://gcc.gnu.org/PR32187 gets fixed.
typedef __typeof__(_Complex __float128) cfloat128;
#else // ^^^ workaround / no workaround vvv
typedef _Complex __float128 cfloat128;
#endif
#elif defined(__GNUC__)
#if (defined(__STDC_IEC_60559_COMPLEX__) || defined(__SIZEOF_FLOAT128__)) && \
(__GNUC__ >= 13 || (!defined(__cplusplus)))
#define LIBC_TYPES_HAS_CFLOAT128
typedef _Complex _Float128 cfloat128;
#endif
#endif
#if !defined(LIBC_TYPES_HAS_CFLOAT128) && (LDBL_MANT_DIG == 113)
#define LIBC_TYPES_HAS_CFLOAT128
#define LIBC_TYPES_CFLOAT128_IS_COMPLEX_LONG_DOUBLE
#endif // ^^^ no workaround ^^^
#else
typedef _Complex long double cfloat128;
#endif
#endif // LIBC_TYPES_CFLOAT128_IS_COMPLEX_LONG_DOUBLE
#endif // LIBC_TYPES_HAS_CFLOAT128
#endif // LLVM_LIBC_TYPES_CFLOAT128_H
+4 -7
View File
@@ -9,13 +9,10 @@
#ifndef LLVM_LIBC_TYPES_CFLOAT16_H
#define LLVM_LIBC_TYPES_CFLOAT16_H
#if defined(__FLT16_MANT_DIG__) && \
(!defined(__GNUC__) || __GNUC__ >= 13 || \
(defined(__clang__) && __clang_major__ >= 14)) && \
!defined(__arm__) && !defined(_M_ARM) && !defined(__riscv) && \
!defined(_WIN32)
#define LIBC_TYPES_HAS_CFLOAT16
#include "../llvm-libc-macros/cfloat16-macros.h"
#ifdef LIBC_TYPES_HAS_CFLOAT16
typedef _Complex _Float16 cfloat16;
#endif
#endif // LIBC_TYPES_HAS_CFLOAT16
#endif // LLVM_LIBC_TYPES_CFLOAT16_H
+14
View File
@@ -0,0 +1,14 @@
//===-- Definition of wint_t types ----------------------------------------===//
//
// 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_WINT_T_H
#define LLVM_LIBC_TYPES_WINT_T_H
typedef __WINT_TYPE__ wint_t;
#endif // LLVM_LIBC_TYPES_WINT_T_H
+5
View File
@@ -19,6 +19,11 @@
#define LIBC_ERRNO_MODE LIBC_ERRNO_MODE_SYSTEM_INLINE
#endif // LIBC_ERRNO_MODE
// Use system fenv functions in math implementations.
#ifndef LIBC_MATH_USE_SYSTEM_FENV
#define LIBC_MATH_USE_SYSTEM_FENV
#endif // LIBC_MATH_USE_SYSTEM_FENV
#ifndef LIBC_NAMESPACE
#define LIBC_NAMESPACE __llvm_libc
#endif // LIBC_NAMESPACE
+50 -12
View File
@@ -11,14 +11,14 @@
#ifndef LLVM_LIBC_SRC___SUPPORT_CPP_BIT_H
#define LLVM_LIBC_SRC___SUPPORT_CPP_BIT_H
#include "hdr/stdint_proxy.h"
#include "src/__support/CPP/limits.h" // numeric_limits
#include "src/__support/CPP/type_traits.h"
#include "src/__support/macros/attributes.h"
#include "src/__support/macros/config.h"
#include "src/__support/macros/properties/compiler.h"
#include "src/__support/macros/sanitizer.h"
#include <stdint.h>
namespace LIBC_NAMESPACE_DECL {
namespace cpp {
@@ -26,6 +26,16 @@ namespace cpp {
#define LLVM_LIBC_HAS_BUILTIN_MEMCPY_INLINE
#endif
template <unsigned N>
LIBC_INLINE static void inline_copy(const char *from, char *to) {
#if __has_builtin(__builtin_memcpy_inline)
__builtin_memcpy_inline(to, from, N);
#else
for (unsigned i = 0; i < N; ++i)
to[i] = from[i];
#endif // __has_builtin(__builtin_memcpy_inline)
}
// This implementation of bit_cast requires trivially-constructible To, to avoid
// UB in the implementation.
template <typename To, typename From>
@@ -37,22 +47,32 @@ LIBC_INLINE constexpr cpp::enable_if_t<
To>
bit_cast(const From &from) {
MSAN_UNPOISON(&from, sizeof(From));
#if __has_builtin(__builtin_bit_cast)
#if __has_builtin(__builtin_bit_cast) || defined(LIBC_COMPILER_IS_MSVC)
return __builtin_bit_cast(To, from);
#else
To to;
To to{};
char *dst = reinterpret_cast<char *>(&to);
const char *src = reinterpret_cast<const char *>(&from);
#if __has_builtin(__builtin_memcpy_inline)
__builtin_memcpy_inline(dst, src, sizeof(To));
#else
for (unsigned i = 0; i < sizeof(To); ++i)
dst[i] = src[i];
#endif // __has_builtin(__builtin_memcpy_inline)
inline_copy<sizeof(From)>(src, dst);
return to;
#endif // __has_builtin(__builtin_bit_cast)
}
// The following simple bit copy from a smaller type to maybe-larger type.
template <typename To, typename From>
LIBC_INLINE constexpr cpp::enable_if_t<
(sizeof(To) >= sizeof(From)) &&
cpp::is_trivially_constructible<To>::value &&
cpp::is_trivially_copyable<To>::value &&
cpp::is_trivially_copyable<From>::value,
void>
bit_copy(const From &from, To &to) {
MSAN_UNPOISON(&from, sizeof(From));
char *dst = reinterpret_cast<char *>(&to);
const char *src = reinterpret_cast<const char *>(&from);
inline_copy<sizeof(From)>(src, dst);
}
template <typename T>
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>,
bool>
@@ -105,10 +125,16 @@ countr_zero(T value) {
}
#if __has_builtin(__builtin_ctzs)
ADD_SPECIALIZATION(countr_zero, unsigned short, __builtin_ctzs)
#endif
#endif // __has_builtin(__builtin_ctzs)
#if __has_builtin(__builtin_ctz)
ADD_SPECIALIZATION(countr_zero, unsigned int, __builtin_ctz)
#endif // __has_builtin(__builtin_ctz)
#if __has_builtin(__builtin_ctzl)
ADD_SPECIALIZATION(countr_zero, unsigned long, __builtin_ctzl)
#endif // __has_builtin(__builtin_ctzl)
#if __has_builtin(__builtin_ctzll)
ADD_SPECIALIZATION(countr_zero, unsigned long long, __builtin_ctzll)
#endif // __has_builtin(__builtin_ctzll)
#endif // __has_builtin(__builtin_ctzg)
/// Count number of 0's from the most significant bit to the least
@@ -144,10 +170,16 @@ countl_zero(T value) {
}
#if __has_builtin(__builtin_clzs)
ADD_SPECIALIZATION(countl_zero, unsigned short, __builtin_clzs)
#endif
#endif // __has_builtin(__builtin_clzs)
#if __has_builtin(__builtin_clz)
ADD_SPECIALIZATION(countl_zero, unsigned int, __builtin_clz)
#endif // __has_builtin(__builtin_clz)
#if __has_builtin(__builtin_clzl)
ADD_SPECIALIZATION(countl_zero, unsigned long, __builtin_clzl)
#endif // __has_builtin(__builtin_clzl)
#if __has_builtin(__builtin_clzll)
ADD_SPECIALIZATION(countl_zero, unsigned long long, __builtin_clzll)
#endif // __has_builtin(__builtin_clzll)
#endif // __has_builtin(__builtin_clzg)
#undef ADD_SPECIALIZATION
@@ -284,11 +316,17 @@ popcount(T value) {
[[nodiscard]] LIBC_INLINE constexpr int popcount<TYPE>(TYPE value) { \
return BUILTIN(value); \
}
#if __has_builtin(__builtin_popcount)
ADD_SPECIALIZATION(unsigned char, __builtin_popcount)
ADD_SPECIALIZATION(unsigned short, __builtin_popcount)
ADD_SPECIALIZATION(unsigned, __builtin_popcount)
#endif // __builtin_popcount
#if __has_builtin(__builtin_popcountl)
ADD_SPECIALIZATION(unsigned long, __builtin_popcountl)
#endif // __builtin_popcountl
#if __has_builtin(__builtin_popcountll)
ADD_SPECIALIZATION(unsigned long long, __builtin_popcountll)
#endif // __builtin_popcountll
#endif // __builtin_popcountg
#undef ADD_SPECIALIZATION
@@ -13,12 +13,17 @@
#include "src/__support/macros/attributes.h"
#include "src/__support/macros/config.h"
// LIBC_TYPES_HAS_CFLOAT16 && LIBC_TYPES_HAS_CFLOAT128
#include "src/__support/macros/properties/compiler.h"
#include "src/__support/macros/properties/complex_types.h"
namespace LIBC_NAMESPACE_DECL {
namespace cpp {
// is_complex
#ifdef LIBC_COMPILER_IS_MSVC
// TODO: Add support for complex types with MSVC.
template <typename T> struct is_complex : false_type {};
#else
template <typename T> struct is_complex {
private:
template <typename Head, typename... Args>
@@ -40,6 +45,8 @@ public:
#endif
>();
};
#endif // LIBC_COMPILER_IS_MSVC
template <typename T>
LIBC_INLINE_VAR constexpr bool is_complex_v = is_complex<T>::value;
template <typename T1, typename T2>
@@ -15,6 +15,7 @@
#include "src/__support/CPP/type_traits/remove_all_extents.h"
#include "src/__support/CPP/type_traits/true_type.h"
#include "src/__support/CPP/type_traits/type_identity.h"
#include "src/__support/CPP/utility/declval.h"
#include "src/__support/macros/attributes.h"
#include "src/__support/macros/config.h"
@@ -22,7 +23,7 @@ namespace LIBC_NAMESPACE_DECL {
namespace cpp {
// is_destructible
#if __has_builtin(__is_destructible)
#if __has_builtin(__is_destructible) || defined(LIBC_COMPILER_IS_MSVC)
template <typename T>
struct is_destructible : bool_constant<__is_destructible(T)> {};
#else
@@ -16,6 +16,8 @@
#include "src/__support/macros/attributes.h"
#include "src/__support/macros/config.h"
#include <stddef.h>
namespace LIBC_NAMESPACE_DECL {
namespace cpp {
@@ -46,6 +48,10 @@ public:
LIBC_INLINE constexpr bool operator()() const { return is_unsigned::value; }
};
#endif // LIBC_COMPILER_HAS_FIXED_POINT
#if LIBC_HAS_VECTOR_TYPE
template <typename T, size_t N>
struct is_unsigned<T [[clang::ext_vector_type(N)]]> : bool_constant<false> {};
#endif
template <typename T>
LIBC_INLINE_VAR constexpr bool is_unsigned_v = is_unsigned<T>::value;
@@ -5,12 +5,15 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_SRC___SUPPORT_CPP_UTILITY_INTEGER_SEQUENCE_H
#define LLVM_LIBC_SRC___SUPPORT_CPP_UTILITY_INTEGER_SEQUENCE_H
#include "src/__support/CPP/type_traits/is_integral.h"
#include "src/__support/macros/config.h"
#include <stddef.h>
namespace LIBC_NAMESPACE_DECL {
namespace cpp {
@@ -34,6 +37,13 @@ template <typename T, int N>
using make_integer_sequence =
typename detail::make_integer_sequence<T, N - 1>::type;
// index sequence
template <size_t... Ints>
using index_sequence = integer_sequence<size_t, Ints...>;
template <int N>
using make_index_sequence =
typename detail::make_integer_sequence<size_t, N - 1>::type;
} // namespace cpp
} // namespace LIBC_NAMESPACE_DECL
+6 -7
View File
@@ -15,6 +15,7 @@
#ifndef LLVM_LIBC_SRC___SUPPORT_FPUTIL_FPBITS_H
#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_FPBITS_H
#include "hdr/stdint_proxy.h"
#include "src/__support/CPP/bit.h"
#include "src/__support/CPP/type_traits.h"
#include "src/__support/common.h"
@@ -26,8 +27,6 @@
#include "src/__support/sign.h" // Sign
#include "src/__support/uint128.h"
#include <stdint.h>
namespace LIBC_NAMESPACE_DECL {
namespace fputil {
@@ -790,16 +789,16 @@ struct FPRep : public FPRepImpl<fp_type, FPRep<fp_type>> {
// Returns the FPType corresponding to C++ type T on the host.
template <typename T> LIBC_INLINE static constexpr FPType get_fp_type() {
using UnqualT = cpp::remove_cv_t<T>;
if constexpr (cpp::is_same_v<UnqualT, float> && __FLT_MANT_DIG__ == 24)
if constexpr (cpp::is_same_v<UnqualT, float> && FLT_MANT_DIG == 24)
return FPType::IEEE754_Binary32;
else if constexpr (cpp::is_same_v<UnqualT, double> && __DBL_MANT_DIG__ == 53)
else if constexpr (cpp::is_same_v<UnqualT, double> && DBL_MANT_DIG == 53)
return FPType::IEEE754_Binary64;
else if constexpr (cpp::is_same_v<UnqualT, long double>) {
if constexpr (__LDBL_MANT_DIG__ == 53)
if constexpr (LDBL_MANT_DIG == 53)
return FPType::IEEE754_Binary64;
else if constexpr (__LDBL_MANT_DIG__ == 64)
else if constexpr (LDBL_MANT_DIG == 64)
return FPType::X86_Binary80;
else if constexpr (__LDBL_MANT_DIG__ == 113)
else if constexpr (LDBL_MANT_DIG == 113)
return FPType::IEEE754_Binary128;
}
#if defined(LIBC_TYPES_HAS_FLOAT16)
+50 -4
View File
@@ -10,18 +10,21 @@
#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_ROUNDING_MODE_H
#include "hdr/fenv_macros.h"
#include "src/__support/CPP/type_traits.h" // is_constant_evaluated
#include "src/__support/macros/attributes.h" // LIBC_INLINE
#include "src/__support/macros/config.h"
namespace LIBC_NAMESPACE_DECL {
namespace fputil {
namespace generic {
// Quick free-standing test whether fegetround() == FE_UPWARD.
// Using the following observation:
// 1.0f + 2^-25 = 1.0f for FE_TONEAREST, FE_DOWNWARD, FE_TOWARDZERO
// = 0x1.000002f for FE_UPWARD.
LIBC_INLINE bool fenv_is_round_up() {
volatile float x = 0x1.0p-25f;
static volatile float x = 0x1.0p-25f;
return (1.0f + x != 1.0f);
}
@@ -30,7 +33,7 @@ LIBC_INLINE bool fenv_is_round_up() {
// -1.0f - 2^-25 = -1.0f for FE_TONEAREST, FE_UPWARD, FE_TOWARDZERO
// = -0x1.000002f for FE_DOWNWARD.
LIBC_INLINE bool fenv_is_round_down() {
volatile float x = 0x1.0p-25f;
static volatile float x = 0x1.0p-25f;
return (-1.0f - x != -1.0f);
}
@@ -42,8 +45,8 @@ LIBC_INLINE bool fenv_is_round_down() {
// = 0x1.0ffffep-1f for FE_DOWNWARD, FE_TOWARDZERO
LIBC_INLINE bool fenv_is_round_to_nearest() {
static volatile float x = 0x1.0p-24f;
float y = x;
return (1.5f + y == 1.5f - y);
float y = 1.5f + x;
return (y == 1.5f - x);
}
// Quick free-standing test whether fegetround() == FE_TOWARDZERO.
@@ -75,6 +78,49 @@ LIBC_INLINE int quick_get_round() {
return (2.0f + y == 2.0f) ? FE_TONEAREST : FE_UPWARD;
}
} // namespace generic
LIBC_INLINE static constexpr bool fenv_is_round_up() {
if (cpp::is_constant_evaluated()) {
return false;
} else {
return generic::fenv_is_round_up();
}
}
LIBC_INLINE static constexpr bool fenv_is_round_down() {
if (cpp::is_constant_evaluated()) {
return false;
} else {
return generic::fenv_is_round_down();
}
}
LIBC_INLINE static constexpr bool fenv_is_round_to_nearest() {
if (cpp::is_constant_evaluated()) {
return true;
} else {
return generic::fenv_is_round_to_nearest();
}
}
LIBC_INLINE static constexpr bool fenv_is_round_to_zero() {
if (cpp::is_constant_evaluated()) {
return false;
} else {
return generic::fenv_is_round_to_zero();
}
}
// Quick free standing get rounding mode based on the above observations.
LIBC_INLINE static constexpr int quick_get_round() {
if (cpp::is_constant_evaluated()) {
return FE_TONEAREST;
} else {
return generic::quick_get_round();
}
}
} // namespace fputil
} // namespace LIBC_NAMESPACE_DECL
+14 -14
View File
@@ -9,6 +9,7 @@
#ifndef LLVM_LIBC_SRC___SUPPORT_BIG_INT_H
#define LLVM_LIBC_SRC___SUPPORT_BIG_INT_H
#include "hdr/stdint_proxy.h"
#include "src/__support/CPP/array.h"
#include "src/__support/CPP/bit.h" // countl_zero
#include "src/__support/CPP/limits.h"
@@ -23,7 +24,6 @@
#include "src/__support/number_pair.h"
#include <stddef.h> // For size_t
#include <stdint.h>
namespace LIBC_NAMESPACE_DECL {
@@ -95,10 +95,10 @@ LIBC_INLINE constexpr DoubleWide<word> mul2(word a, word b) {
#endif
else {
using half_word = half_width_t<word>;
const auto shiftl = [](word value) -> word {
constexpr auto shiftl = [](word value) -> word {
return value << cpp::numeric_limits<half_word>::digits;
};
const auto shiftr = [](word value) -> word {
constexpr auto shiftr = [](word value) -> word {
return value >> cpp::numeric_limits<half_word>::digits;
};
// Here we do a one digit multiplication where 'a' and 'b' are of type
@@ -111,19 +111,19 @@ LIBC_INLINE constexpr DoubleWide<word> mul2(word a, word b) {
// c result
// We convert 'lo' and 'hi' from 'half_word' to 'word' so multiplication
// doesn't overflow.
const word a_lo = lo(a);
const word b_lo = lo(b);
const word a_hi = hi(a);
const word b_hi = hi(b);
const word step1 = b_lo * a_lo; // no overflow;
const word step2 = b_lo * a_hi; // no overflow;
const word step3 = b_hi * a_lo; // no overflow;
const word step4 = b_hi * a_hi; // no overflow;
word a_lo = lo(a);
word b_lo = lo(b);
word a_hi = hi(a);
word b_hi = hi(b);
word step1 = b_lo * a_lo; // no overflow;
word step2 = b_lo * a_hi; // no overflow;
word step3 = b_hi * a_lo; // no overflow;
word step4 = b_hi * a_hi; // no overflow;
word lo_digit = step1;
word hi_digit = step4;
const word no_carry = 0;
word carry;
word _; // unused carry variable.
word no_carry = 0;
word carry = 0;
[[maybe_unused]] word _ = 0; // unused carry variable.
lo_digit = add_with_carry<word>(lo_digit, shiftl(step2), no_carry, carry);
hi_digit = add_with_carry<word>(hi_digit, shiftr(step2), carry, _);
lo_digit = add_with_carry<word>(lo_digit, shiftl(step3), no_carry, carry);
+3 -2
View File
@@ -16,6 +16,7 @@
#include "src/__support/macros/attributes.h"
#include "src/__support/macros/config.h"
#include "src/__support/macros/properties/architectures.h"
#include "src/__support/macros/properties/compiler.h"
#ifndef LLVM_LIBC_FUNCTION_ATTR
#define LLVM_LIBC_FUNCTION_ATTR
@@ -41,12 +42,12 @@
// to cleanly export and alias the C++ symbol `LIBC_NAMESPACE::func` with the C
// symbol `func`. So for public packaging on MacOS, we will only export the C
// symbol. Moreover, a C symbol `func` in macOS is mangled as `_func`.
#if defined(LIBC_COPT_PUBLIC_PACKAGING)
#if defined(LIBC_COPT_PUBLIC_PACKAGING) && !defined(LIBC_COMPILER_IS_MSVC)
#ifndef __APPLE__
#define LLVM_LIBC_FUNCTION_IMPL(type, name, arglist) \
LLVM_LIBC_ATTR(name) \
LLVM_LIBC_FUNCTION_ATTR decltype(LIBC_NAMESPACE::name) \
__##name##_impl__ __asm__(#name); \
__##name##_impl__ asm(#name); \
decltype(LIBC_NAMESPACE::name) name [[gnu::alias(#name)]]; \
type __##name##_impl__ arglist
#else // __APPLE__
+19 -12
View File
@@ -27,7 +27,7 @@ namespace internal {
// as well as a way to support non-ASCII character encodings.
// Similarly, do not change these functions to use case ranges. e.g.
// bool islower(int ch) {
// bool islower(char ch) {
// switch(ch) {
// case 'a'...'z':
// return true;
@@ -37,7 +37,7 @@ namespace internal {
// EBCDIC. Technically we could use some smaller ranges, but that's even harder
// to read.
LIBC_INLINE static constexpr bool islower(int ch) {
LIBC_INLINE static constexpr bool islower(char ch) {
switch (ch) {
case 'a':
case 'b':
@@ -71,7 +71,7 @@ LIBC_INLINE static constexpr bool islower(int ch) {
}
}
LIBC_INLINE static constexpr bool isupper(int ch) {
LIBC_INLINE static constexpr bool isupper(char ch) {
switch (ch) {
case 'A':
case 'B':
@@ -105,7 +105,7 @@ LIBC_INLINE static constexpr bool isupper(int ch) {
}
}
LIBC_INLINE static constexpr bool isdigit(int ch) {
LIBC_INLINE static constexpr bool isdigit(char ch) {
switch (ch) {
case '0':
case '1':
@@ -123,7 +123,7 @@ LIBC_INLINE static constexpr bool isdigit(int ch) {
}
}
LIBC_INLINE static constexpr int tolower(int ch) {
LIBC_INLINE static constexpr char tolower(char ch) {
switch (ch) {
case 'A':
return 'a';
@@ -182,7 +182,7 @@ LIBC_INLINE static constexpr int tolower(int ch) {
}
}
LIBC_INLINE static constexpr int toupper(int ch) {
LIBC_INLINE static constexpr char toupper(char ch) {
switch (ch) {
case 'a':
return 'A';
@@ -241,7 +241,7 @@ LIBC_INLINE static constexpr int toupper(int ch) {
}
}
LIBC_INLINE static constexpr bool isalpha(int ch) {
LIBC_INLINE static constexpr bool isalpha(char ch) {
switch (ch) {
case 'a':
case 'b':
@@ -301,7 +301,7 @@ LIBC_INLINE static constexpr bool isalpha(int ch) {
}
}
LIBC_INLINE static constexpr bool isalnum(int ch) {
LIBC_INLINE static constexpr bool isalnum(char ch) {
switch (ch) {
case 'a':
case 'b':
@@ -371,7 +371,7 @@ LIBC_INLINE static constexpr bool isalnum(int ch) {
}
}
LIBC_INLINE static constexpr int b36_char_to_int(int ch) {
LIBC_INLINE static constexpr int b36_char_to_int(char ch) {
switch (ch) {
case '0':
return 0;
@@ -476,7 +476,7 @@ LIBC_INLINE static constexpr int b36_char_to_int(int ch) {
}
}
LIBC_INLINE static constexpr int int_to_b36_char(int num) {
LIBC_INLINE static constexpr char int_to_b36_char(int num) {
// Can't actually use LIBC_ASSERT here because it depends on integer_to_string
// which depends on this.
@@ -559,7 +559,7 @@ LIBC_INLINE static constexpr int int_to_b36_char(int num) {
}
}
LIBC_INLINE static constexpr bool isspace(int ch) {
LIBC_INLINE static constexpr bool isspace(char ch) {
switch (ch) {
case ' ':
case '\t':
@@ -574,10 +574,17 @@ LIBC_INLINE static constexpr bool isspace(int ch) {
}
// not yet encoding independent.
LIBC_INLINE static constexpr bool isgraph(int ch) {
LIBC_INLINE static constexpr bool isgraph(char ch) {
return 0x20 < ch && ch < 0x7f;
}
// An overload which provides a way to compare input with specific character
// values, when input can be of a regular or a wide character type.
LIBC_INLINE static constexpr bool is_char_or_wchar(char ch, char c_value,
[[maybe_unused]] wchar_t) {
return (ch == c_value);
}
} // namespace internal
} // namespace LIBC_NAMESPACE_DECL
+1 -2
View File
@@ -9,11 +9,10 @@
#ifndef LLVM_LIBC_SRC___SUPPORT_DETAILED_POWERS_OF_TEN_H
#define LLVM_LIBC_SRC___SUPPORT_DETAILED_POWERS_OF_TEN_H
#include "hdr/stdint_proxy.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
#include <stdint.h>
namespace LIBC_NAMESPACE_DECL {
namespace internal {
+33 -14
View File
@@ -15,11 +15,12 @@
#ifndef LLVM_LIBC_SRC___SUPPORT_HIGH_PRECISION_DECIMAL_H
#define LLVM_LIBC_SRC___SUPPORT_HIGH_PRECISION_DECIMAL_H
#include "hdr/stdint_proxy.h"
#include "src/__support/CPP/limits.h"
#include "src/__support/ctype_utils.h"
#include "src/__support/macros/config.h"
#include "src/__support/str_to_integer.h"
#include <stdint.h>
#include "src/__support/wctype_utils.h"
namespace LIBC_NAMESPACE_DECL {
namespace internal {
@@ -38,6 +39,24 @@ struct LShiftTableEntry {
// TODO: Figure out where to put this.
enum class RoundDirection { Up, Down, Nearest };
// These constants are used in both this file and in the main str_to_float.h.
// TODO: Figure out where to put this.
template <typename CharType> struct constants;
template <> struct constants<char> {
static constexpr char DECIMAL_POINT = '.';
static constexpr char DECIMAL_EXPONENT_MARKER = 'e';
static constexpr char HEX_EXPONENT_MARKER = 'p';
static constexpr char INF_STRING[] = "infinity";
static constexpr char NAN_STRING[] = "nan";
};
template <> struct constants<wchar_t> {
static constexpr wchar_t DECIMAL_POINT = L'.';
static constexpr wchar_t DECIMAL_EXPONENT_MARKER = L'e';
static constexpr wchar_t HEX_EXPONENT_MARKER = L'p';
static constexpr wchar_t INF_STRING[] = L"infinity";
static constexpr wchar_t NAN_STRING[] = L"nan";
};
// This is based on the HPD data structure described as part of the Simple
// Decimal Conversion algorithm by Nigel Tao, described at this link:
// https://nigeltao.github.io/blog/2020/parse-number-f64-simple.html
@@ -314,9 +333,9 @@ private:
public:
// num_string is assumed to be a string of numeric characters. It doesn't
// handle leading spaces.
LIBC_INLINE
HighPrecisionDecimal(
const char *__restrict num_string,
template <typename CharType>
LIBC_INLINE HighPrecisionDecimal(
const CharType *__restrict num_string,
const size_t num_len = cpp::numeric_limits<size_t>::max()) {
bool saw_dot = false;
size_t num_cur = 0;
@@ -324,25 +343,26 @@ public:
// them all.
uint32_t total_digits = 0;
while (num_cur < num_len &&
(isdigit(num_string[num_cur]) || num_string[num_cur] == '.')) {
if (num_string[num_cur] == '.') {
(isdigit(num_string[num_cur]) ||
num_string[num_cur] == constants<CharType>::DECIMAL_POINT)) {
if (num_string[num_cur] == constants<CharType>::DECIMAL_POINT) {
if (saw_dot) {
break;
}
this->decimal_point = static_cast<int32_t>(total_digits);
saw_dot = true;
} else {
if (num_string[num_cur] == '0' && this->num_digits == 0) {
int digit = b36_char_to_int(num_string[num_cur]);
if (digit == 0 && this->num_digits == 0) {
--this->decimal_point;
++num_cur;
continue;
}
++total_digits;
if (this->num_digits < MAX_NUM_DIGITS) {
this->digits[this->num_digits] = static_cast<uint8_t>(
internal::b36_char_to_int(num_string[num_cur]));
this->digits[this->num_digits] = static_cast<uint8_t>(digit);
++this->num_digits;
} else if (num_string[num_cur] != '0') {
} else if (digit != 0) {
this->truncated = true;
}
}
@@ -352,11 +372,10 @@ public:
if (!saw_dot)
this->decimal_point = static_cast<int32_t>(total_digits);
if (num_cur < num_len &&
(num_string[num_cur] == 'e' || num_string[num_cur] == 'E')) {
if (num_cur < num_len && tolower(num_string[num_cur]) ==
constants<CharType>::DECIMAL_EXPONENT_MARKER) {
++num_cur;
if (isdigit(num_string[num_cur]) || num_string[num_cur] == '+' ||
num_string[num_cur] == '-') {
if (isdigit(num_string[num_cur]) || get_sign(num_string + num_cur) != 0) {
auto result =
strtointeger<int32_t>(num_string + num_cur, 10, num_len - num_cur);
if (result.has_error()) {
+2
View File
@@ -14,9 +14,11 @@
// The build is configured to just use the public <assert.h> API
// for libc's internal assertions.
#ifndef LIBC_ASSERT
#include <assert.h>
#define LIBC_ASSERT(COND) assert(COND)
#endif // LIBC_ASSERT
#else // Not LIBC_COPT_USE_C_ASSERT
+44 -1
View File
@@ -17,6 +17,7 @@
#ifndef LLVM_LIBC_SRC___SUPPORT_MACROS_ATTRIBUTES_H
#define LLVM_LIBC_SRC___SUPPORT_MACROS_ATTRIBUTES_H
#include "config.h"
#include "properties/architectures.h"
#ifndef __has_attribute
@@ -28,7 +29,32 @@
#define LIBC_INLINE_ASM __asm__ __volatile__
#define LIBC_UNUSED __attribute__((unused))
#ifdef LIBC_TARGET_ARCH_IS_GPU
// Uses the platform specific specialization
#define LIBC_THREAD_MODE_PLATFORM 0
// Mutex guards nothing, used in single-threaded implementations
#define LIBC_THREAD_MODE_SINGLE 1
// Vendor provides implementation
#define LIBC_THREAD_MODE_EXTERNAL 2
// libcxx doesn't define LIBC_THREAD_MODE, unless that is passed in the command
// line in the CMake invocation. This defaults to the original implementation
// (before changes in https://github.com/llvm/llvm-project/pull/145358)
#ifndef LIBC_THREAD_MODE
#define LIBC_THREAD_MODE LIBC_THREAD_MODE_PLATFORM
#endif // LIBC_THREAD_MODE
#if LIBC_THREAD_MODE != LIBC_THREAD_MODE_PLATFORM && \
LIBC_THREAD_MODE != LIBC_THREAD_MODE_SINGLE && \
LIBC_THREAD_MODE != LIBC_THREAD_MODE_EXTERNAL
#error LIBC_THREAD_MODE must be one of the following values: \
LIBC_THREAD_MODE_PLATFORM, \
LIBC_THREAD_MODE_SINGLE, \
LIBC_THREAD_MODE_EXTERNAL.
#endif
#if LIBC_THREAD_MODE == LIBC_THREAD_MODE_SINGLE
#define LIBC_THREAD_LOCAL
#else
#define LIBC_THREAD_LOCAL thread_local
@@ -48,4 +74,21 @@
#define LIBC_PREFERED_TYPE(TYPE)
#endif
#if __has_attribute(ext_vector_type) && \
LIBC_HAS_FEATURE(ext_vector_type_boolean)
#define LIBC_HAS_VECTOR_TYPE 1
#else
#define LIBC_HAS_VECTOR_TYPE 0
#endif
#if __has_attribute(no_sanitize)
// Disable regular and hardware-supported ASan for functions that may
// intentionally make out-of-bounds access. Disable TSan as well, as it detects
// out-of-bounds accesses to heap memory.
#define LIBC_NO_SANITIZE_OOB_ACCESS \
__attribute__((no_sanitize("address", "hwaddress", "thread")))
#else
#define LIBC_NO_SANITIZE_OOB_ACCESS
#endif
#endif // LLVM_LIBC_SRC___SUPPORT_MACROS_ATTRIBUTES_H
+23
View File
@@ -13,6 +13,13 @@
#ifndef LLVM_LIBC_SRC___SUPPORT_MACROS_CONFIG_H
#define LLVM_LIBC_SRC___SUPPORT_MACROS_CONFIG_H
#include "src/__support/macros/properties/architectures.h"
#include "src/__support/macros/properties/compiler.h"
#ifdef LIBC_COMPILER_IS_MSVC
#include <intrin.h>
#endif // LIBC_COMPILER_IS_MSVC
// Workaround for compilers that do not support builtin detection.
// FIXME: This is only required for the GPU portion which should be moved.
#ifndef __has_builtin
@@ -27,6 +34,22 @@
#define LIBC_HAS_FEATURE(f) 0
#endif
#ifdef LIBC_COMPILER_IS_MSVC
// __builtin_trap replacement
#ifdef LIBC_TARGET_ARCH_IS_X86
#define __builtin_trap __ud2
#else // arm64
#define __builtin_trap() __break(1)
#endif
#define __builtin_expect(value, expectation) (value)
#define __builtin_unreachable() __assume(0)
#define __builtin_prefetch(X, Y, Z)
#endif // LIBC_COMPILER_IS_MSVC
#ifdef __clang__
// Declare a LIBC_NAMESPACE with hidden visibility. `namespace
// LIBC_NAMESPACE_DECL {` should be used around all declarations and definitions
+1 -2
View File
@@ -11,9 +11,8 @@
#include "src/__support/macros/config.h"
#include "src/__support/macros/optimization.h"
#include "src/__support/macros/sanitizer.h"
#if defined(LIBC_ADD_NULL_CHECKS) && !defined(LIBC_HAS_SANITIZER)
#if defined(LIBC_ADD_NULL_CHECKS)
#define LIBC_CRASH_ON_NULLPTR(ptr) \
do { \
if (LIBC_UNLIKELY((ptr) == nullptr)) \
+3
View File
@@ -34,6 +34,9 @@ LIBC_INLINE constexpr bool expects_bool_condition(T value, T expected) {
#elif defined(LIBC_COMPILER_IS_GCC)
#define LIBC_LOOP_NOUNROLL _Pragma("GCC unroll 0")
#define LIBC_LOOP_UNROLL _Pragma("GCC unroll 2048")
#elif defined(LIBC_COMPILER_IS_MSVC)
#define LIBC_LOOP_NOUNROLL
#define LIBC_LOOP_UNROLL
#else
#error "Unhandled compiler"
#endif
@@ -21,7 +21,7 @@
#define LIBC_TARGET_ARCH_IS_GPU
#endif
#if defined(__pnacl__) || defined(__CLR_VER) || defined(LIBC_TARGET_ARCH_IS_GPU)
#if defined(__CLR_VER) || defined(LIBC_TARGET_ARCH_IS_GPU)
#define LIBC_TARGET_ARCH_IS_VM
#endif
@@ -41,6 +41,10 @@
#define LIBC_TARGET_ARCH_IS_ARM
#endif
#if defined(__wasm__)
#define LIBC_TARGET_ARCH_IS_WASM
#endif
#if defined(__aarch64__) || defined(__arm64__) || defined(_M_ARM64)
#define LIBC_TARGET_ARCH_IS_AARCH64
#endif
+8 -3
View File
@@ -34,10 +34,15 @@
#define LIBC_COMPILER_GCC_VER (__GNUC__ * 100 + __GNUC_MINOR__)
#endif
#if defined(_MSC_VER)
#define LIBC_COMPILER_IS_MSC
#if defined(_MSC_VER) && !defined(__clang__)
#define LIBC_COMPILER_IS_MSVC
// https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros
#define LIBC_COMPILER_MSC_VER (_MSC_VER)
#define LIBC_COMPILER_MSVC_VER (_MSC_VER)
#ifdef _M_X64
#define LIBC_COMPILER_IS_MSVC_X64
#else
#define LIBC_COMPILER_IS_MSVC_X86
#endif
#endif
#endif // LLVM_LIBC_SRC___SUPPORT_MACROS_PROPERTIES_COMPILER_H
@@ -18,6 +18,18 @@
#define LIBC_TARGET_CPU_HAS_FULLFP16
#endif
#if defined(__ARM_FEATURE_SVE)
#define LIBC_TARGET_CPU_HAS_SVE
#endif
#if defined(__ARM_FEATURE_SVE2)
#define LIBC_TARGET_CPU_HAS_SVE2
#endif
#if defined(__ARM_FEATURE_MOPS)
#define LIBC_TARGET_CPU_HAS_MOPS
#endif
#if defined(__SSE2__)
#define LIBC_TARGET_CPU_HAS_SSE2
#define LIBC_TARGET_CPU_HAS_FPU_FLOAT
@@ -59,6 +71,10 @@
#endif // LIBC_TARGET_CPU_HAS_ARM_FPU_DOUBLE
#endif // __ARM_FP
#if defined(__ARM_NEON)
#define LIBC_TARGET_CPU_HAS_ARM_NEON
#endif
#if defined(__riscv_flen)
// https://github.com/riscv-non-isa/riscv-c-api-doc/blob/main/src/c-api.adoc
#if defined(__riscv_zfhmin)
@@ -81,7 +97,7 @@
#endif
#if defined(__ARM_FEATURE_FMA) || (defined(__AVX2__) && defined(__FMA__)) || \
defined(__NVPTX__) || defined(__AMDGPU__) || defined(__LIBC_RISCV_USE_FMA)
defined(__NVPTX__) || defined(__AMDGPU__) || defined(__riscv_flen)
#define LIBC_TARGET_CPU_HAS_FMA
// Provide a more fine-grained control of FMA instruction for ARM targets.
#if defined(LIBC_TARGET_CPU_HAS_FPU_HALF)
+2 -3
View File
@@ -10,7 +10,8 @@
#ifndef LLVM_LIBC_SRC___SUPPORT_MACROS_PROPERTIES_TYPES_H
#define LLVM_LIBC_SRC___SUPPORT_MACROS_PROPERTIES_TYPES_H
#include "hdr/float_macros.h" // LDBL_MANT_DIG
#include "hdr/float_macros.h" // LDBL_MANT_DIG
#include "hdr/stdint_proxy.h" // UINT64_MAX, __SIZEOF_INT128__
#include "include/llvm-libc-macros/float16-macros.h" // LIBC_TYPES_HAS_FLOAT16
#include "include/llvm-libc-types/float128.h" // float128
#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL
@@ -19,8 +20,6 @@
#include "src/__support/macros/properties/cpu_features.h"
#include "src/__support/macros/properties/os.h"
#include <stdint.h> // UINT64_MAX, __SIZEOF_INT128__
// 'long double' properties.
#if (LDBL_MANT_DIG == 53)
#define LIBC_TYPES_LONG_DOUBLE_IS_FLOAT64
-10
View File
@@ -23,16 +23,6 @@
#define LIBC_HAS_MEMORY_SANITIZER
#endif
#if LIBC_HAS_FEATURE(undefined_behavior_sanitizer)
#define LIBC_HAS_UNDEFINED_BEHAVIOR_SANITIZER
#endif
#if defined(LIBC_HAS_ADDRESS_SANITIZER) || \
defined(LIBC_HAS_MEMORY_SANITIZER) || \
defined(LIBC_HAS_UNDEFINED_BEHAVIOR_SANITIZER)
#define LIBC_HAS_SANITIZER
#endif
#ifdef LIBC_HAS_MEMORY_SANITIZER
// Only perform MSAN unpoison in non-constexpr context.
#include <sanitizer/msan_interface.h>
+20 -4
View File
@@ -25,7 +25,13 @@ LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, T>
mask_trailing_ones() {
constexpr unsigned T_BITS = CHAR_BIT * sizeof(T);
static_assert(count <= T_BITS && "Invalid bit index");
return count == 0 ? 0 : (T(-1) >> (T_BITS - count));
// MSVC complains about out of range shifts.
if constexpr (count == 0)
return 0;
else if constexpr (count >= T_BITS)
return T(-1);
else
return T(-1) >> (T_BITS - count);
}
// Create a bitmask with the count left-most bits set to 1, and all other bits
@@ -55,18 +61,28 @@ mask_leading_zeros() {
// Returns whether 'a + b' overflows, the result is stored in 'res'.
template <typename T>
[[nodiscard]] LIBC_INLINE constexpr bool add_overflow(T a, T b, T &res) {
#if __has_builtin(__builtin_add_overflow)
return __builtin_add_overflow(a, b, &res);
#else
res = a + b;
return (res < a) || (res < b);
#endif // __builtin_add_overflow
}
// Returns whether 'a - b' overflows, the result is stored in 'res'.
template <typename T>
[[nodiscard]] LIBC_INLINE constexpr bool sub_overflow(T a, T b, T &res) {
#if __has_builtin(__builtin_sub_overflow)
return __builtin_sub_overflow(a, b, &res);
#else
res = a - b;
return (res > a);
#endif // __builtin_sub_overflow
}
#define RETURN_IF(TYPE, BUILTIN) \
if constexpr (cpp::is_same_v<T, TYPE>) \
return BUILTIN(a, b, carry_in, carry_out);
return BUILTIN(a, b, carry_in, &carry_out);
// Returns the result of 'a + b' taking into account 'carry_in'.
// The carry out is stored in 'carry_out' it not 'nullptr', dropped otherwise.
@@ -74,7 +90,7 @@ template <typename T>
template <typename T>
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, T>
add_with_carry(T a, T b, T carry_in, T &carry_out) {
if constexpr (!cpp::is_constant_evaluated()) {
if (!cpp::is_constant_evaluated()) {
#if __has_builtin(__builtin_addcb)
RETURN_IF(unsigned char, __builtin_addcb)
#elif __has_builtin(__builtin_addcs)
@@ -100,7 +116,7 @@ add_with_carry(T a, T b, T carry_in, T &carry_out) {
template <typename T>
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, T>
sub_with_borrow(T a, T b, T carry_in, T &carry_out) {
if constexpr (!cpp::is_constant_evaluated()) {
if (!cpp::is_constant_evaluated()) {
#if __has_builtin(__builtin_subcb)
RETURN_IF(unsigned char, __builtin_subcb)
#elif __has_builtin(__builtin_subcs)
+98 -125
View File
@@ -16,6 +16,7 @@
#define LLVM_LIBC_SRC___SUPPORT_STR_TO_FLOAT_H
#include "hdr/errno_macros.h" // For ERANGE
#include "hdr/stdint_proxy.h"
#include "src/__support/CPP/bit.h"
#include "src/__support/CPP/limits.h"
#include "src/__support/CPP/optional.h"
@@ -32,8 +33,7 @@
#include "src/__support/str_to_integer.h"
#include "src/__support/str_to_num_result.h"
#include "src/__support/uint128.h"
#include <stdint.h>
#include "src/__support/wctype_utils.h"
namespace LIBC_NAMESPACE_DECL {
namespace internal {
@@ -335,9 +335,9 @@ constexpr int32_t NUM_POWERS_OF_TWO =
// the Eisel-Lemire algorithm fails, it's slower but more accurate. It's based
// on the Simple Decimal Conversion algorithm by Nigel Tao, described at this
// link: https://nigeltao.github.io/blog/2020/parse-number-f64-simple.html
template <class T>
template <typename T, typename CharType>
LIBC_INLINE FloatConvertReturn<T> simple_decimal_conversion(
const char *__restrict numStart,
const CharType *__restrict numStart,
const size_t num_len = cpp::numeric_limits<size_t>::max(),
RoundDirection round = RoundDirection::Nearest) {
using FPBits = typename fputil::FPBits<T>;
@@ -677,12 +677,11 @@ template <> LIBC_INLINE constexpr int32_t get_lower_bound<double>() {
// Takes a mantissa and base 10 exponent and converts it into its closest
// floating point type T equivalient. First we try the Eisel-Lemire algorithm,
// then if that fails then we fall back to a more accurate algorithm for
// accuracy. The resulting mantissa and exponent are placed in outputMantissa
// and outputExp2.
template <class T>
// accuracy.
template <typename T, typename CharType>
LIBC_INLINE FloatConvertReturn<T> decimal_exp_to_float(
ExpandedFloat<T> init_num, bool truncated, RoundDirection round,
const char *__restrict numStart,
const CharType *__restrict numStart,
const size_t num_len = cpp::numeric_limits<size_t>::max()) {
using FPBits = typename fputil::FPBits<T>;
using StorageType = typename FPBits::StorageType;
@@ -861,36 +860,42 @@ LIBC_INLINE FloatConvertReturn<T> binary_exp_to_float(ExpandedFloat<T> init_num,
return output;
}
// checks if the next 4 characters of the string pointer are the start of a
// Checks if the first characters of the string pointer are the start of a
// hexadecimal floating point number. Does not advance the string pointer.
LIBC_INLINE bool is_float_hex_start(const char *__restrict src,
const char decimalPoint) {
if (!(src[0] == '0' && tolower(src[1]) == 'x')) {
template <typename CharType>
LIBC_INLINE static bool is_float_hex_start(const CharType *__restrict src) {
if (!is_char_or_wchar(src[0], '0', L'0') ||
!is_char_or_wchar(tolower(src[1]), 'x', L'x')) {
return false;
}
size_t first_digit = 2;
if (src[2] == decimalPoint) {
if (src[2] == constants<CharType>::DECIMAL_POINT) {
++first_digit;
}
return isalnum(src[first_digit]) && b36_char_to_int(src[first_digit]) < 16;
}
// Takes the start of a string representing a decimal float, as well as the
// local decimalPoint. It returns if it suceeded in parsing any digits, and if
// the return value is true then the outputs are pointer to the end of the
// number, and the mantissa and exponent for the closest float T representation.
// If the return value is false, then it is assumed that there is no number
// here.
template <class T>
LIBC_INLINE StrToNumResult<ExpandedFloat<T>>
decimal_string_to_float(const char *__restrict src, const char DECIMAL_POINT,
RoundDirection round) {
// Verifies that first prefix_len characters of str, when lowercased, match the
// specified prefix.
template <typename CharType>
LIBC_INLINE static bool tolower_starts_with(const CharType *str,
size_t prefix_len,
const CharType *prefix) {
for (size_t i = 0; i < prefix_len; ++i) {
if (tolower(str[i]) != prefix[i])
return false;
}
return true;
}
// Attempts parsing a decimal floating point number at the start of the string.
template <typename T, typename CharType>
LIBC_INLINE static StrToNumResult<ExpandedFloat<T>>
decimal_string_to_float(const CharType *__restrict src, RoundDirection round) {
using FPBits = typename fputil::FPBits<T>;
using StorageType = typename FPBits::StorageType;
constexpr uint32_t BASE = 10;
constexpr char EXPONENT_MARKER = 'e';
bool truncated = false;
bool seen_digit = false;
bool after_decimal = false;
@@ -927,7 +932,7 @@ decimal_string_to_float(const char *__restrict src, const char DECIMAL_POINT,
++index;
continue;
}
if (src[index] == DECIMAL_POINT) {
if (src[index] == constants<CharType>::DECIMAL_POINT) {
if (after_decimal) {
break; // this means that src[index] points to a second decimal point,
// ending the number.
@@ -944,13 +949,10 @@ decimal_string_to_float(const char *__restrict src, const char DECIMAL_POINT,
return output;
// TODO: When adding max length argument, handle the case of a trailing
// EXPONENT MARKER, see scanf for more details.
if (tolower(src[index]) == EXPONENT_MARKER) {
bool has_sign = false;
if (src[index + 1] == '+' || src[index + 1] == '-') {
has_sign = true;
}
if (isdigit(src[index + 1 + static_cast<size_t>(has_sign)])) {
// exponent marker, see scanf for more details.
if (tolower(src[index]) == constants<CharType>::DECIMAL_EXPONENT_MARKER) {
int sign = get_sign(src + index + 1);
if (isdigit(src[index + 1 + static_cast<size_t>(sign != 0)])) {
++index;
auto result = strtointeger<int32_t>(src + index, 10);
if (result.has_error())
@@ -986,22 +988,16 @@ decimal_string_to_float(const char *__restrict src, const char DECIMAL_POINT,
return output;
}
// Takes the start of a string representing a hexadecimal float, as well as the
// local decimal point. It returns if it suceeded in parsing any digits, and if
// the return value is true then the outputs are pointer to the end of the
// number, and the mantissa and exponent for the closest float T representation.
// If the return value is false, then it is assumed that there is no number
// here.
template <class T>
LIBC_INLINE StrToNumResult<ExpandedFloat<T>>
hexadecimal_string_to_float(const char *__restrict src,
const char DECIMAL_POINT, RoundDirection round) {
// Attempts parsing a hexadecimal floating point number at the start of the
// string.
template <typename T, typename CharType>
LIBC_INLINE static StrToNumResult<ExpandedFloat<T>>
hexadecimal_string_to_float(const CharType *__restrict src,
RoundDirection round) {
using FPBits = typename fputil::FPBits<T>;
using StorageType = typename FPBits::StorageType;
constexpr uint32_t BASE = 16;
constexpr char EXPONENT_MARKER = 'p';
bool truncated = false;
bool seen_digit = false;
bool after_decimal = false;
@@ -1039,7 +1035,7 @@ hexadecimal_string_to_float(const char *__restrict src,
++index;
continue;
}
if (src[index] == DECIMAL_POINT) {
if (src[index] == constants<CharType>::DECIMAL_POINT) {
if (after_decimal) {
break; // this means that src[index] points to a second decimal point,
// ending the number.
@@ -1058,12 +1054,9 @@ hexadecimal_string_to_float(const char *__restrict src,
// Convert the exponent from having a base of 16 to having a base of 2.
exponent *= 4;
if (tolower(src[index]) == EXPONENT_MARKER) {
bool has_sign = false;
if (src[index + 1] == '+' || src[index + 1] == '-') {
has_sign = true;
}
if (isdigit(src[index + 1 + static_cast<size_t>(has_sign)])) {
if (tolower(src[index]) == constants<CharType>::HEX_EXPONENT_MARKER) {
int sign = get_sign(src + index + 1);
if (isdigit(src[index + 1 + static_cast<size_t>(sign != 0)])) {
++index;
auto result = strtointeger<int32_t>(src + index, 10);
if (result.has_error())
@@ -1099,21 +1092,21 @@ hexadecimal_string_to_float(const char *__restrict src,
return output;
}
template <class T>
template <typename T, typename CharType>
LIBC_INLINE typename fputil::FPBits<T>::StorageType
nan_mantissa_from_ncharseq(const cpp::string_view ncharseq) {
nan_mantissa_from_ncharseq(const CharType *str, size_t len) {
using FPBits = typename fputil::FPBits<T>;
using StorageType = typename FPBits::StorageType;
StorageType nan_mantissa = 0;
if (ncharseq.data() != nullptr && isdigit(ncharseq[0])) {
if (len > 0 && isdigit(str[0])) {
StrToNumResult<StorageType> strtoint_result =
strtointeger<StorageType>(ncharseq.data(), 0);
strtointeger<StorageType>(str, 0, len);
if (!strtoint_result.has_error())
nan_mantissa = strtoint_result.value;
if (strtoint_result.parsed_len != static_cast<ptrdiff_t>(ncharseq.size()))
if (strtoint_result.parsed_len != static_cast<ptrdiff_t>(len))
nan_mantissa = 0;
}
@@ -1124,59 +1117,44 @@ nan_mantissa_from_ncharseq(const cpp::string_view ncharseq) {
// is used as the backend for all of the string to float functions.
// TODO: Add src_len member to match strtointeger.
// TODO: Next, move from char* and length to string_view
template <class T>
LIBC_INLINE StrToNumResult<T> strtofloatingpoint(const char *__restrict src) {
template <typename T, typename CharType>
LIBC_INLINE StrToNumResult<T>
strtofloatingpoint(const CharType *__restrict src) {
using FPBits = typename fputil::FPBits<T>;
using StorageType = typename FPBits::StorageType;
FPBits result = FPBits();
bool seen_digit = false;
char sign = '+';
int error = 0;
size_t index = first_non_whitespace(src);
int sign = get_sign(src + index);
bool is_positive = (sign >= 0);
index += (sign != 0);
if (src[index] == '+' || src[index] == '-') {
sign = src[index];
++index;
}
if (sign == '-') {
if (sign < 0) {
result.set_sign(Sign::NEG);
}
static constexpr char DECIMAL_POINT = '.';
static const char *inf_string = "infinity";
static const char *nan_string = "nan";
if (isdigit(src[index]) || src[index] == DECIMAL_POINT) { // regular number
if (isdigit(src[index]) ||
src[index] == constants<CharType>::DECIMAL_POINT) { // regular number
int base = 10;
if (is_float_hex_start(src + index, DECIMAL_POINT)) {
if (is_float_hex_start(src + index)) {
base = 16;
index += 2;
seen_digit = true;
}
RoundDirection round_direction = RoundDirection::Nearest;
switch (fputil::quick_get_round()) {
case FE_TONEAREST:
round_direction = RoundDirection::Nearest;
break;
case FE_UPWARD:
if (sign == '+') {
round_direction = RoundDirection::Up;
} else {
round_direction = RoundDirection::Down;
}
round_direction = is_positive ? RoundDirection::Up : RoundDirection::Down;
break;
case FE_DOWNWARD:
if (sign == '+') {
round_direction = RoundDirection::Down;
} else {
round_direction = RoundDirection::Up;
}
round_direction = is_positive ? RoundDirection::Down : RoundDirection::Up;
break;
case FE_TOWARDZERO:
round_direction = RoundDirection::Down;
@@ -1185,58 +1163,53 @@ LIBC_INLINE StrToNumResult<T> strtofloatingpoint(const char *__restrict src) {
StrToNumResult<ExpandedFloat<T>> parse_result({0, 0});
if (base == 16) {
parse_result = hexadecimal_string_to_float<T>(src + index, DECIMAL_POINT,
round_direction);
parse_result =
hexadecimal_string_to_float<T>(src + index, round_direction);
} else { // base is 10
parse_result = decimal_string_to_float<T>(src + index, DECIMAL_POINT,
round_direction);
parse_result = decimal_string_to_float<T>(src + index, round_direction);
}
seen_digit = parse_result.parsed_len != 0;
result.set_mantissa(parse_result.value.mantissa);
result.set_biased_exponent(parse_result.value.exponent);
index += parse_result.parsed_len;
error = parse_result.error;
} else if (tolower(src[index]) == 'n') { // NaN
if (tolower(src[index + 1]) == nan_string[1] &&
tolower(src[index + 2]) == nan_string[2]) {
seen_digit = true;
index += 3;
StorageType nan_mantissa = 0;
// this handles the case of `NaN(n-character-sequence)`, where the
// n-character-sequence is made of 0 or more letters, numbers, or
// underscore characters in any order.
if (src[index] == '(') {
size_t left_paren = index;
} else if (tolower_starts_with(src + index, 3,
constants<CharType>::NAN_STRING)) {
// NAN
seen_digit = true;
index += 3;
StorageType nan_mantissa = 0;
// this handles the case of `NaN(n-character-sequence)`, where the
// n-character-sequence is made of 0 or more letters, numbers, or
// underscore characters in any order.
if (is_char_or_wchar(src[index], '(', L'(')) {
size_t left_paren = index;
++index;
while (isalnum(src[index]) || is_char_or_wchar(src[index], '_', L'_'))
++index;
while (isalnum(src[index]) || src[index] == '_')
++index;
if (src[index] == ')') {
++index;
nan_mantissa = nan_mantissa_from_ncharseq<T>(
cpp::string_view(src + (left_paren + 1), index - left_paren - 2));
} else {
index = left_paren;
}
}
result = FPBits(result.quiet_nan(result.sign(), nan_mantissa));
}
} else if (tolower(src[index]) == 'i') { // INF
if (tolower(src[index + 1]) == inf_string[1] &&
tolower(src[index + 2]) == inf_string[2]) {
seen_digit = true;
result = FPBits(result.inf(result.sign()));
if (tolower(src[index + 3]) == inf_string[3] &&
tolower(src[index + 4]) == inf_string[4] &&
tolower(src[index + 5]) == inf_string[5] &&
tolower(src[index + 6]) == inf_string[6] &&
tolower(src[index + 7]) == inf_string[7]) {
// if the string is "INFINITY" then consume 8 characters.
index += 8;
if (is_char_or_wchar(src[index], ')', L')')) {
++index;
nan_mantissa = nan_mantissa_from_ncharseq<T>(src + (left_paren + 1),
index - left_paren - 2);
} else {
index += 3;
index = left_paren;
}
}
result = FPBits(result.quiet_nan(result.sign(), nan_mantissa));
} else if (tolower_starts_with(src + index, 8,
constants<CharType>::INF_STRING)) {
// INFINITY
seen_digit = true;
result = FPBits(result.inf(result.sign()));
index += 8;
} else if (tolower_starts_with(src + index, 3,
constants<CharType>::INF_STRING)) {
// INF
seen_digit = true;
result = FPBits(result.inf(result.sign()));
index += 3;
}
if (!seen_digit) { // If there is nothing to actually parse, then return 0.
return {T(0), 0, error};
}
@@ -1263,7 +1236,7 @@ template <class T> LIBC_INLINE StrToNumResult<T> strtonan(const char *arg) {
++index;
if (arg[index] == '\0')
nan_mantissa = nan_mantissa_from_ncharseq<T>(cpp::string_view(arg, index));
nan_mantissa = nan_mantissa_from_ncharseq<T>(arg, index);
result = FPBits::quiet_nan(Sign::POS, nan_mantissa);
return {result.get_val(), 0, error};
+39 -27
View File
@@ -25,36 +25,51 @@
#include "src/__support/macros/config.h"
#include "src/__support/str_to_num_result.h"
#include "src/__support/uint128.h"
#include "src/__support/wctype_utils.h"
namespace LIBC_NAMESPACE_DECL {
namespace internal {
// Returns the idx to the first character in src that is not a whitespace
// character (as determined by isspace())
template <typename CharType>
LIBC_INLINE size_t
first_non_whitespace(const char *__restrict src,
first_non_whitespace(const CharType *__restrict src,
size_t src_len = cpp::numeric_limits<size_t>::max()) {
size_t src_cur = 0;
while (src_cur < src_len && internal::isspace(src[src_cur])) {
++src_cur;
}
for (; src_cur < src_len && internal::isspace(src[src_cur]); ++src_cur)
;
return src_cur;
}
// Returns +1, -1, or 0 if 'src' starts with (respectively)
// plus sign, minus sign, or neither.
template <typename CharType>
LIBC_INLINE static int get_sign(const CharType *__restrict src) {
if (is_char_or_wchar(src[0], '+', L'+'))
return 1;
if (is_char_or_wchar(src[0], '-', L'-'))
return -1;
return 0;
}
// checks if the next 3 characters of the string pointer are the start of a
// hexadecimal number. Does not advance the string pointer.
LIBC_INLINE bool
is_hex_start(const char *__restrict src,
size_t src_len = cpp::numeric_limits<size_t>::max()) {
template <typename CharType>
LIBC_INLINE static bool is_hex_start(const CharType *__restrict src,
size_t src_len) {
if (src_len < 3)
return false;
return *src == '0' && tolower(*(src + 1)) == 'x' && isalnum(*(src + 2)) &&
b36_char_to_int(*(src + 2)) < 16;
return is_char_or_wchar(src[0], '0', L'0') &&
is_char_or_wchar(tolower(src[1]), 'x', L'x') && isalnum(src[2]) &&
b36_char_to_int(src[2]) < 16;
}
// Takes the address of the string pointer and parses the base from the start of
// it.
LIBC_INLINE int infer_base(const char *__restrict src, size_t src_len) {
template <typename CharType>
LIBC_INLINE static int infer_base(const CharType *__restrict src,
size_t src_len) {
// A hexadecimal number is defined as "the prefix 0x or 0X followed by a
// sequence of the decimal digits and the letters a (or A) through f (or F)
// with values 10 through 15 respectively." (C standard 6.4.4.1)
@@ -63,8 +78,9 @@ LIBC_INLINE int infer_base(const char *__restrict src, size_t src_len) {
// An octal number is defined as "the prefix 0 optionally followed by a
// sequence of the digits 0 through 7 only" (C standard 6.4.4.1) and so any
// number that starts with 0, including just 0, is an octal number.
if (src_len > 0 && src[0] == '0')
if (src_len > 0 && is_char_or_wchar(src[0], '0', L'0')) {
return 8;
}
// A decimal number is defined as beginning "with a nonzero digit and
// consist[ing] of a sequence of decimal digits." (C standard 6.4.4.1)
return 10;
@@ -77,32 +93,27 @@ LIBC_INLINE int infer_base(const char *__restrict src, size_t src_len) {
// -----------------------------------------------------------------------------
// Takes a pointer to a string and the base to convert to. This function is used
// as the backend for all of the string to int functions.
template <class T>
template <typename T, typename CharType>
LIBC_INLINE StrToNumResult<T>
strtointeger(const char *__restrict src, int base,
strtointeger(const CharType *__restrict src, int base,
const size_t src_len = cpp::numeric_limits<size_t>::max()) {
using ResultType = make_integral_or_big_int_unsigned_t<T>;
ResultType result = 0;
bool is_number = false;
size_t src_cur = 0;
int error_val = 0;
if (src_len == 0)
return {0, 0, 0};
if (base < 0 || base == 1 || base > 36)
return {0, 0, EINVAL};
src_cur = first_non_whitespace(src, src_len);
char result_sign = '+';
if (src[src_cur] == '+' || src[src_cur] == '-') {
result_sign = src[src_cur];
++src_cur;
size_t src_cur = first_non_whitespace(src, src_len);
if (src_cur == src_len) {
return {0, 0, 0};
}
int sign = get_sign(src + src_cur);
bool is_positive = (sign >= 0);
src_cur += (sign != 0);
if (base == 0)
base = infer_base(src + src_cur, src_len - src_cur);
@@ -110,8 +121,6 @@ strtointeger(const char *__restrict src, int base,
src_cur = src_cur + 2;
constexpr bool IS_UNSIGNED = cpp::is_unsigned_v<T>;
const bool is_positive = (result_sign == '+');
ResultType constexpr NEGATIVE_MAX =
!IS_UNSIGNED ? static_cast<ResultType>(cpp::numeric_limits<T>::max()) + 1
: cpp::numeric_limits<T>::max();
@@ -120,6 +129,9 @@ strtointeger(const char *__restrict src, int base,
ResultType const abs_max_div_by_base =
abs_max / static_cast<ResultType>(base);
bool is_number = false;
int error_val = 0;
ResultType result = 0;
while (src_cur < src_len && isalnum(src[src_cur])) {
int cur_digit = b36_char_to_int(src[src_cur]);
if (cur_digit >= base)
+588
View File
@@ -0,0 +1,588 @@
//===-- Collection of utils for implementing wide char functions --*-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___SUPPORT_WCTYPE_UTILS_H
#define LLVM_LIBC_SRC___SUPPORT_WCTYPE_UTILS_H
#include "hdr/types/wchar_t.h"
#include "src/__support/macros/attributes.h" // LIBC_INLINE
#include "src/__support/macros/config.h"
namespace LIBC_NAMESPACE_DECL {
namespace internal {
// -----------------------------------------------------------------------------
// ****************** WARNING ******************
// ****************** DO NOT TRY TO OPTIMIZE THESE FUNCTIONS! ******************
// -----------------------------------------------------------------------------
// This switch/case form is easier for the compiler to understand, and is
// optimized into a form that is almost always the same as or better than
// versions written by hand (see https://godbolt.org/z/qvrebqvvr). Also this
// form makes these functions encoding independent. If you want to rewrite these
// functions, make sure you have benchmarks to show your new solution is faster,
// as well as a way to support non-ASCII character encodings.
// Similarly, do not change these fumarks to show your new solution is faster,
// as well as a way to support non-Anctions to use case ranges. e.g.
// bool islower(wchar_t ch) {
// switch(ch) {
// case L'a'...L'z':
// return true;
// }
// }
// This assumes the character ranges are contiguous, which they aren't in
// EBCDIC. Technically we could use some smaller ranges, but that's even harder
// to read.
LIBC_INLINE static constexpr bool islower(wchar_t wch) {
switch (wch) {
case L'a':
case L'b':
case L'c':
case L'd':
case L'e':
case L'f':
case L'g':
case L'h':
case L'i':
case L'j':
case L'k':
case L'l':
case L'm':
case L'n':
case L'o':
case L'p':
case L'q':
case L'r':
case L's':
case L't':
case L'u':
case L'v':
case L'w':
case L'x':
case L'y':
case L'z':
return true;
default:
return false;
}
}
LIBC_INLINE static constexpr bool isupper(wchar_t wch) {
switch (wch) {
case L'A':
case L'B':
case L'C':
case L'D':
case L'E':
case L'F':
case L'G':
case L'H':
case L'I':
case L'J':
case L'K':
case L'L':
case L'M':
case L'N':
case L'O':
case L'P':
case L'Q':
case L'R':
case L'S':
case L'T':
case L'U':
case L'V':
case L'W':
case L'X':
case L'Y':
case L'Z':
return true;
default:
return false;
}
}
LIBC_INLINE static constexpr bool isdigit(wchar_t wch) {
switch (wch) {
case L'0':
case L'1':
case L'2':
case L'3':
case L'4':
case L'5':
case L'6':
case L'7':
case L'8':
case L'9':
return true;
default:
return false;
}
}
LIBC_INLINE static constexpr wchar_t tolower(wchar_t wch) {
switch (wch) {
case L'A':
return L'a';
case L'B':
return L'b';
case L'C':
return L'c';
case L'D':
return L'd';
case L'E':
return L'e';
case L'F':
return L'f';
case L'G':
return L'g';
case L'H':
return L'h';
case L'I':
return L'i';
case L'J':
return L'j';
case L'K':
return L'k';
case L'L':
return L'l';
case L'M':
return L'm';
case L'N':
return L'n';
case L'O':
return L'o';
case L'P':
return L'p';
case L'Q':
return L'q';
case L'R':
return L'r';
case L'S':
return L's';
case L'T':
return L't';
case L'U':
return L'u';
case L'V':
return L'v';
case L'W':
return L'w';
case L'X':
return L'x';
case L'Y':
return L'y';
case L'Z':
return L'z';
default:
return wch;
}
}
LIBC_INLINE static constexpr wchar_t toupper(wchar_t wch) {
switch (wch) {
case L'a':
return L'A';
case L'b':
return L'B';
case L'c':
return L'C';
case L'd':
return L'D';
case L'e':
return L'E';
case L'f':
return L'F';
case L'g':
return L'G';
case L'h':
return L'H';
case L'i':
return L'I';
case L'j':
return L'J';
case L'k':
return L'K';
case L'l':
return L'L';
case L'm':
return L'M';
case L'n':
return L'N';
case L'o':
return L'O';
case L'p':
return L'P';
case L'q':
return L'Q';
case L'r':
return L'R';
case L's':
return L'S';
case L't':
return L'T';
case L'u':
return L'U';
case L'v':
return L'V';
case L'w':
return L'W';
case L'x':
return L'X';
case L'y':
return L'Y';
case L'z':
return L'Z';
default:
return wch;
}
}
LIBC_INLINE static constexpr bool isalpha(wchar_t wch) {
switch (wch) {
case L'a':
case L'b':
case L'c':
case L'd':
case L'e':
case L'f':
case L'g':
case L'h':
case L'i':
case L'j':
case L'k':
case L'l':
case L'm':
case L'n':
case L'o':
case L'p':
case L'q':
case L'r':
case L's':
case L't':
case L'u':
case L'v':
case L'w':
case L'x':
case L'y':
case L'z':
case L'A':
case L'B':
case L'C':
case L'D':
case L'E':
case L'F':
case L'G':
case L'H':
case L'I':
case L'J':
case L'K':
case L'L':
case L'M':
case L'N':
case L'O':
case L'P':
case L'Q':
case L'R':
case L'S':
case L'T':
case L'U':
case L'V':
case L'W':
case L'X':
case L'Y':
case L'Z':
return true;
default:
return false;
}
}
LIBC_INLINE static constexpr bool isalnum(wchar_t wch) {
switch (wch) {
case L'a':
case L'b':
case L'c':
case L'd':
case L'e':
case L'f':
case L'g':
case L'h':
case L'i':
case L'j':
case L'k':
case L'l':
case L'm':
case L'n':
case L'o':
case L'p':
case L'q':
case L'r':
case L's':
case L't':
case L'u':
case L'v':
case L'w':
case L'x':
case L'y':
case L'z':
case L'A':
case L'B':
case L'C':
case L'D':
case L'E':
case L'F':
case L'G':
case L'H':
case L'I':
case L'J':
case L'K':
case L'L':
case L'M':
case L'N':
case L'O':
case L'P':
case L'Q':
case L'R':
case L'S':
case L'T':
case L'U':
case L'V':
case L'W':
case L'X':
case L'Y':
case L'Z':
case L'0':
case L'1':
case L'2':
case L'3':
case L'4':
case L'5':
case L'6':
case L'7':
case L'8':
case L'9':
return true;
default:
return false;
}
}
LIBC_INLINE static constexpr int b36_char_to_int(wchar_t wch) {
switch (wch) {
case L'0':
return 0;
case L'1':
return 1;
case L'2':
return 2;
case L'3':
return 3;
case L'4':
return 4;
case L'5':
return 5;
case L'6':
return 6;
case L'7':
return 7;
case L'8':
return 8;
case L'9':
return 9;
case L'a':
case L'A':
return 10;
case L'b':
case L'B':
return 11;
case L'c':
case L'C':
return 12;
case L'd':
case L'D':
return 13;
case L'e':
case L'E':
return 14;
case L'f':
case L'F':
return 15;
case L'g':
case L'G':
return 16;
case L'h':
case L'H':
return 17;
case L'i':
case L'I':
return 18;
case L'j':
case L'J':
return 19;
case L'k':
case L'K':
return 20;
case L'l':
case L'L':
return 21;
case L'm':
case L'M':
return 22;
case L'n':
case L'N':
return 23;
case L'o':
case L'O':
return 24;
case L'p':
case L'P':
return 25;
case L'q':
case L'Q':
return 26;
case L'r':
case L'R':
return 27;
case L's':
case L'S':
return 28;
case L't':
case L'T':
return 29;
case L'u':
case L'U':
return 30;
case L'v':
case L'V':
return 31;
case L'w':
case L'W':
return 32;
case L'x':
case L'X':
return 33;
case L'y':
case L'Y':
return 34;
case L'z':
case L'Z':
return 35;
default:
return 0;
}
}
LIBC_INLINE static constexpr wchar_t int_to_b36_wchar(int num) {
// Can't actually use LIBC_ASSERT here because it depends on integer_to_string
// which depends on this.
// LIBC_ASSERT(num < 36);
switch (num) {
case 0:
return L'0';
case 1:
return L'1';
case 2:
return L'2';
case 3:
return L'3';
case 4:
return L'4';
case 5:
return L'5';
case 6:
return L'6';
case 7:
return L'7';
case 8:
return L'8';
case 9:
return L'9';
case 10:
return L'a';
case 11:
return L'b';
case 12:
return L'c';
case 13:
return L'd';
case 14:
return L'e';
case 15:
return L'f';
case 16:
return L'g';
case 17:
return L'h';
case 18:
return L'i';
case 19:
return L'j';
case 20:
return L'k';
case 21:
return L'l';
case 22:
return L'm';
case 23:
return L'n';
case 24:
return L'o';
case 25:
return L'p';
case 26:
return L'q';
case 27:
return L'r';
case 28:
return L's';
case 29:
return L't';
case 30:
return L'u';
case 31:
return L'v';
case 32:
return L'w';
case 33:
return L'x';
case 34:
return L'y';
case 35:
return L'z';
default:
return L'!';
}
}
LIBC_INLINE static constexpr bool isspace(wchar_t wch) {
switch (wch) {
case L' ':
case L'\t':
case L'\n':
case L'\v':
case L'\f':
case L'\r':
return true;
default:
return false;
}
}
// An overload which provides a way to compare input with specific character
// values, when input can be of a regular or a wide character type.
LIBC_INLINE static constexpr bool
is_char_or_wchar(wchar_t ch, [[maybe_unused]] char, wchar_t wc_value) {
return (ch == wc_value);
}
} // namespace internal
} // namespace LIBC_NAMESPACE_DECL
#endif // LLVM_LIBC_SRC___SUPPORT_WCTYPE_UTILS_H