diff options
Diffstat (limited to 'libc')
50 files changed, 1065 insertions, 268 deletions
diff --git a/libc/cmake/modules/LLVMLibCObjectRules.cmake b/libc/cmake/modules/LLVMLibCObjectRules.cmake index 805da91..030157b 100644 --- a/libc/cmake/modules/LLVMLibCObjectRules.cmake +++ b/libc/cmake/modules/LLVMLibCObjectRules.cmake @@ -1,4 +1,33 @@ set(OBJECT_LIBRARY_TARGET_TYPE "OBJECT_LIBRARY") +set(ENTRYPOINT_OBJ_TARGET_TYPE "ENTRYPOINT_OBJ") +set(ENTRYPOINT_EXT_TARGET_TYPE "ENTRYPOINT_EXT") + +# Rule to check if a list of dependencies contains any entrypoint objects. Returns a list in entrypoint_deps. +function(check_entrypoint_deps entrypoint_deps) + set(PUBLIC_DEPS "") + set(fq_deps_list "") + list(APPEND fq_deps_list ${ARGN}) + + #don't warn for deps that are allowed, such as errno + set(ALLOWED_DEPS + "libc.src.errno.errno" + "libc.src.setjmp.longjmp" + ) + list(REMOVE_ITEM fq_deps_list ${ALLOWED_DEPS}) + + foreach(dep IN LISTS fq_deps_list) + if(NOT TARGET ${dep}) + continue() + endif() + + get_target_property(target_type ${dep} "TARGET_TYPE") + if(${target_type} STREQUAL ${ENTRYPOINT_OBJ_TARGET_TYPE}) + list(APPEND PUBLIC_DEPS ${dep}) + endif() + endforeach() + set(${entrypoint_deps} ${PUBLIC_DEPS} PARENT_SCOPE) +endfunction() + # Rule which is essentially a wrapper over add_library to compile a set of # sources to object files. @@ -65,6 +94,18 @@ function(create_object_library fq_target_name) target_include_directories(${fq_target_name} PRIVATE ${LIBC_SOURCE_DIR}) target_compile_options(${fq_target_name} PRIVATE ${compile_options}) + #loop through the deps, check if any have the TARGET_TYPE of ENTRYPOINT_OBJ_TARGET_TYPE, and print a warning if they do. + if(LIBC_CMAKE_VERBOSE_LOGGING) + set(entrypoint_deps "") + if(NOT "${fq_deps_list}" STREQUAL "") + check_entrypoint_deps(entrypoint_deps ${fq_deps_list}) + endif() + if(NOT "${entrypoint_deps}" STREQUAL "") + message(WARNING "Object ${fq_target_name} depends on public entrypoint(s) ${entrypoint_deps}. + Depending on public entrypoints is not allowed in internal code.") + endif() + endif() + if(SHOW_INTERMEDIATE_OBJECTS) message(STATUS "Adding object library ${fq_target_name}") if(${SHOW_INTERMEDIATE_OBJECTS} STREQUAL "DEPS") @@ -110,7 +151,6 @@ function(add_object_library target_name) ${ARGN}) endfunction(add_object_library) -set(ENTRYPOINT_OBJ_TARGET_TYPE "ENTRYPOINT_OBJ") # A rule for entrypoint object targets. # Usage: @@ -179,7 +219,6 @@ function(create_entrypoint_object fq_target_name) get_target_property(obj_type ${fq_dep_name} "TARGET_TYPE") if((NOT obj_type) OR (NOT ${obj_type} STREQUAL ${ENTRYPOINT_OBJ_TARGET_TYPE})) - message(FATAL_ERROR "The aliasee of an entrypoint alias should be an entrypoint.") endif() @@ -230,6 +269,19 @@ function(create_entrypoint_object fq_target_name) _get_common_compile_options(common_compile_options "${ADD_ENTRYPOINT_OBJ_FLAGS}") list(APPEND common_compile_options ${ADD_ENTRYPOINT_OBJ_COMPILE_OPTIONS}) get_fq_deps_list(fq_deps_list ${ADD_ENTRYPOINT_OBJ_DEPENDS}) + + #loop through the deps, check if any have the TARGET_TYPE of entrypoint_target_type, and print a warning if they do. + if(LIBC_CMAKE_VERBOSE_LOGGING) + set(entrypoint_deps "") + if(NOT "${fq_deps_list}" STREQUAL "") + check_entrypoint_deps(entrypoint_deps ${fq_deps_list}) + endif() + if(NOT "${entrypoint_deps}" STREQUAL "") + message(WARNING "Entrypoint ${fq_target_name} depends on public entrypoint(s) ${entrypoint_deps}. + Depending on public entrypoints is not allowed in internal code.") + endif() + endif() + set(full_deps_list ${fq_deps_list} libc.src.__support.common) if(SHOW_INTERMEDIATE_OBJECTS) @@ -390,8 +442,6 @@ function(add_entrypoint_object target_name) ) endfunction(add_entrypoint_object) -set(ENTRYPOINT_EXT_TARGET_TYPE "ENTRYPOINT_EXT") - # A rule for external entrypoint targets. # Usage: # add_entrypoint_external( diff --git a/libc/config/windows/README.md b/libc/config/windows/README.md index 3ac058e..ee5d5fb 100644 --- a/libc/config/windows/README.md +++ b/libc/config/windows/README.md @@ -59,7 +59,7 @@ libc, and finally, build and test the libc. by Clang, so ensure Clang is specified as the C and C++ compiler. ``` - cmake -G Ninja ../llvm-project/llvm -DCMAKE_C_COMPILER=C:/src/clang-build/bin/clang-cl.exe -DCMAKE_CXX_COMPILER=C:/src/clang-build/bin/clang-cl.exe -DLLVM_TARGETS_TO_BUILD=X86 -DLLVM_FORCE_BUILD_RUNTIME=libc -DLLVM_ENABLE_PROJECTS=libc -DLLVM_NATIVE_ARCH=x86_64 -DLLVM_HOST_TRIPLE=x86_64-window-x86-gnu + cmake -G Ninja ../llvm-project/runtimes -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=C:/src/clang-build/bin/clang-cl.exe -DCMAKE_CXX_COMPILER=C:/src/clang-build/bin/clang-cl.exe -DLLVM_ENABLE_RUNTIMES=libc ``` Some LLVM libc math unittests test correctness/accuracy against results from diff --git a/libc/include/dlfcn.yaml b/libc/include/dlfcn.yaml index 6afeb70..db2893a 100644 --- a/libc/include/dlfcn.yaml +++ b/libc/include/dlfcn.yaml @@ -37,6 +37,57 @@ macros: standards: - gnu macro_value: "((void *) 0)" +enums: + - name: RTLD_DI_LMID + standards: + - gnu + value: 1 + - name: RTLD_DI_LINKMAP + standards: + - gnu + value: 2 + - name: RTLD_DI_CONFIGADDR, + standards: + - gnu + value: 3 + - name: RTLD_DI_SERINFO + standards: + - gnu + value: 4 + - name: RTLD_DI_SERINFOSIZE + standards: + - gnu + value: 5 + - name: RTLD_DI_ORIGIN + standards: + - gnu + value: 6 + - name: RTLD_DI_PROFILENAME + standards: + - gnu + value: 7 + - name: RTLD_DI_PROFILEOUT + standards: + - gnu + value: 8 + - name: RTLD_DI_TLS_MODID + standards: + - gnu + value: 9 + - name: RTLD_DI_TLS_DATA + standards: + - gnu + value: 10 + - name: RTLD_DI_PHDR + standards: + - gnu + value: 11 + - name: RTLD_DI_MAX + standards: + - gnu + value: 11 +types: + - type_name: Dl_info functions: - name: dlclose standards: @@ -63,3 +114,18 @@ functions: arguments: - type: void *__restrict - type: const char *__restrict + - name: dlinfo + standards: + - gnu + return_type: int + arguments: + - type: void *__restrict + - type: int + - type: void *__restrict + - name: dladdr + standards: + - POSIX + return_type: int + arguments: + - type: const void * + - type: Dl_info * diff --git a/libc/include/llvm-libc-types/Dl_info.h b/libc/include/llvm-libc-types/Dl_info.h new file mode 100644 index 0000000..b082e30 --- /dev/null +++ b/libc/include/llvm-libc-types/Dl_info.h @@ -0,0 +1,19 @@ +//===-- Definition of Dl_info 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_TYPES_DL_INFO_H +#define LLVM_LIBC_TYPES_DL_INFO_H + +typedef struct { + const char *dli_fname; + void *dli_fbase; + const char *dli_sname; + void *dli_saddr; +} Dl_info; + +#endif // LLVM_LIBC_TYPES_DL_INFO_H diff --git a/libc/src/__support/FPUtil/CMakeLists.txt b/libc/src/__support/FPUtil/CMakeLists.txt index 6e447fc..37520ea 100644 --- a/libc/src/__support/FPUtil/CMakeLists.txt +++ b/libc/src/__support/FPUtil/CMakeLists.txt @@ -285,6 +285,9 @@ add_header_library( libc.hdr.stdint_proxy libc.src.__support.CPP.bit libc.src.__support.CPP.type_traits + libc.src.__support.FPUtil.generic.add_sub + libc.src.__support.FPUtil.generic.div + libc.src.__support.FPUtil.generic.mul libc.src.__support.macros.config libc.src.__support.macros.properties.types ) diff --git a/libc/src/__support/FPUtil/bfloat16.h b/libc/src/__support/FPUtil/bfloat16.h index fa45d73..3fab2b8 100644 --- a/libc/src/__support/FPUtil/bfloat16.h +++ b/libc/src/__support/FPUtil/bfloat16.h @@ -15,6 +15,9 @@ #include "src/__support/FPUtil/cast.h" #include "src/__support/FPUtil/comparison_operations.h" #include "src/__support/FPUtil/dyadic_float.h" +#include "src/__support/FPUtil/generic/add_sub.h" +#include "src/__support/FPUtil/generic/div.h" +#include "src/__support/FPUtil/generic/mul.h" #include "src/__support/macros/config.h" #include "src/__support/macros/properties/types.h" @@ -81,6 +84,28 @@ struct BFloat16 { LIBC_INLINE bool operator>=(BFloat16 other) const { return fputil::greater_than_or_equals(*this, other); } + + LIBC_INLINE constexpr BFloat16 operator-() const { + fputil::FPBits<bfloat16> result(*this); + result.set_sign(result.is_pos() ? Sign::NEG : Sign::POS); + return result.get_val(); + } + + LIBC_INLINE BFloat16 operator+(BFloat16 other) const { + return fputil::generic::add<BFloat16>(*this, other); + } + + LIBC_INLINE BFloat16 operator-(BFloat16 other) const { + return fputil::generic::sub<BFloat16>(*this, other); + } + + LIBC_INLINE BFloat16 operator*(BFloat16 other) const { + return fputil::generic::mul<bfloat16>(*this, other); + } + + LIBC_INLINE BFloat16 operator/(BFloat16 other) const { + return fputil::generic::div<bfloat16>(*this, other); + } }; // struct BFloat16 } // namespace fputil diff --git a/libc/src/__support/FPUtil/cast.h b/libc/src/__support/FPUtil/cast.h index e999ece..54c80e8 100644 --- a/libc/src/__support/FPUtil/cast.h +++ b/libc/src/__support/FPUtil/cast.h @@ -27,47 +27,47 @@ LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_floating_point_v<OutType> && OutType> cast(InType x) { // Casting to the same type is a no-op. - if constexpr (cpp::is_same_v<InType, OutType>) + if constexpr (cpp::is_same_v<InType, OutType>) { return x; - - // bfloat16 is always defined (for now) - if constexpr (cpp::is_same_v<OutType, bfloat16> || - cpp::is_same_v<InType, bfloat16> + } else { + if constexpr (cpp::is_same_v<OutType, bfloat16> || + cpp::is_same_v<InType, bfloat16> #if defined(LIBC_TYPES_HAS_FLOAT16) && !defined(__LIBC_USE_FLOAT16_CONVERSION) - || cpp::is_same_v<OutType, float16> || - cpp::is_same_v<InType, float16> + || cpp::is_same_v<OutType, float16> || + cpp::is_same_v<InType, float16> #endif - ) { - using InFPBits = FPBits<InType>; - using InStorageType = typename InFPBits::StorageType; - using OutFPBits = FPBits<OutType>; - using OutStorageType = typename OutFPBits::StorageType; + ) { + using InFPBits = FPBits<InType>; + using InStorageType = typename InFPBits::StorageType; + using OutFPBits = FPBits<OutType>; + using OutStorageType = typename OutFPBits::StorageType; - InFPBits x_bits(x); + InFPBits x_bits(x); - if (x_bits.is_nan()) { - if (x_bits.is_signaling_nan()) { - raise_except_if_required(FE_INVALID); - return OutFPBits::quiet_nan().get_val(); - } + if (x_bits.is_nan()) { + if (x_bits.is_signaling_nan()) { + raise_except_if_required(FE_INVALID); + return OutFPBits::quiet_nan().get_val(); + } - InStorageType x_mant = x_bits.get_mantissa(); - if (InFPBits::FRACTION_LEN > OutFPBits::FRACTION_LEN) - x_mant >>= InFPBits::FRACTION_LEN - OutFPBits::FRACTION_LEN; - return OutFPBits::quiet_nan(x_bits.sign(), - static_cast<OutStorageType>(x_mant)) - .get_val(); - } + InStorageType x_mant = x_bits.get_mantissa(); + if (InFPBits::FRACTION_LEN > OutFPBits::FRACTION_LEN) + x_mant >>= InFPBits::FRACTION_LEN - OutFPBits::FRACTION_LEN; + return OutFPBits::quiet_nan(x_bits.sign(), + static_cast<OutStorageType>(x_mant)) + .get_val(); + } - if (x_bits.is_inf()) - return OutFPBits::inf(x_bits.sign()).get_val(); + if (x_bits.is_inf()) + return OutFPBits::inf(x_bits.sign()).get_val(); - constexpr size_t MAX_FRACTION_LEN = - cpp::max(OutFPBits::FRACTION_LEN, InFPBits::FRACTION_LEN); - DyadicFloat<cpp::bit_ceil(MAX_FRACTION_LEN)> xd(x); - return xd.template as<OutType, /*ShouldSignalExceptions=*/true>(); - } else { - return static_cast<OutType>(x); + constexpr size_t MAX_FRACTION_LEN = + cpp::max(OutFPBits::FRACTION_LEN, InFPBits::FRACTION_LEN); + DyadicFloat<cpp::bit_ceil(MAX_FRACTION_LEN)> xd(x); + return xd.template as<OutType, /*ShouldSignalExceptions=*/true>(); + } else { + return static_cast<OutType>(x); + } } } diff --git a/libc/src/__support/FPUtil/dyadic_float.h b/libc/src/__support/FPUtil/dyadic_float.h index 3464e4a..cc0710f 100644 --- a/libc/src/__support/FPUtil/dyadic_float.h +++ b/libc/src/__support/FPUtil/dyadic_float.h @@ -576,7 +576,7 @@ LIBC_INLINE constexpr DyadicFloat<Bits> quick_mul(const DyadicFloat<Bits> &a, // Check the leading bit directly, should be faster than using clz in // normalize(). if (result.mantissa.val[DyadicFloat<Bits>::MantissaType::WORD_COUNT - 1] >> - 63 == + (DyadicFloat<Bits>::MantissaType::WORD_SIZE - 1) == 0) result.shift_left(1); } else { diff --git a/libc/src/__support/FPUtil/generic/CMakeLists.txt b/libc/src/__support/FPUtil/generic/CMakeLists.txt index 117213f..b75efc8 100644 --- a/libc/src/__support/FPUtil/generic/CMakeLists.txt +++ b/libc/src/__support/FPUtil/generic/CMakeLists.txt @@ -68,6 +68,7 @@ add_header_library( libc.src.__support.FPUtil.rounding_mode libc.src.__support.macros.attributes libc.src.__support.macros.optimization + libc.src.__support.macros.properties.types ) add_header_library( @@ -77,6 +78,7 @@ add_header_library( DEPENDS libc.hdr.errno_macros libc.hdr.fenv_macros + libc.src.__support.CPP.algorithm libc.src.__support.CPP.bit libc.src.__support.CPP.type_traits libc.src.__support.FPUtil.basic_operations diff --git a/libc/src/__support/FPUtil/generic/add_sub.h b/libc/src/__support/FPUtil/generic/add_sub.h index d4a4129..b2e9d81 100644 --- a/libc/src/__support/FPUtil/generic/add_sub.h +++ b/libc/src/__support/FPUtil/generic/add_sub.h @@ -104,13 +104,22 @@ add_or_sub(InType x, InType y) { } } - // volatile prevents Clang from converting tmp to OutType and then - // immediately back to InType before negating it, resulting in double - // rounding. - volatile InType tmp = y; - if constexpr (IsSub) - tmp = -tmp; - return cast<OutType>(tmp); + if constexpr (cpp::is_same_v<InType, bfloat16> && + cpp::is_same_v<OutType, bfloat16>) { + OutFPBits y_bits(y); + if constexpr (IsSub) + y_bits.set_sign(y_bits.sign().negate()); + return y_bits.get_val(); + } else { + + // volatile prevents Clang from converting tmp to OutType and then + // immediately back to InType before negating it, resulting in double + // rounding. + volatile InType tmp = y; + if constexpr (IsSub) + tmp = -tmp; + return cast<OutType>(tmp); + } } if (y_bits.is_zero()) @@ -165,8 +174,8 @@ add_or_sub(InType x, InType y) { int alignment = (max_bits.get_biased_exponent() - max_bits.is_normal()) - (min_bits.get_biased_exponent() - min_bits.is_normal()); - InStorageType aligned_min_mant = - min_mant >> cpp::min(alignment, RESULT_MANTISSA_LEN); + InStorageType aligned_min_mant = static_cast<InStorageType>( + min_mant >> cpp::min(alignment, RESULT_MANTISSA_LEN)); bool aligned_min_mant_sticky; if (alignment <= GUARD_BITS_LEN) diff --git a/libc/src/__support/FPUtil/generic/div.h b/libc/src/__support/FPUtil/generic/div.h index 0891ae0..bf7d0b7 100644 --- a/libc/src/__support/FPUtil/generic/div.h +++ b/libc/src/__support/FPUtil/generic/div.h @@ -11,6 +11,7 @@ #include "hdr/errno_macros.h" #include "hdr/fenv_macros.h" +#include "src/__support/CPP/algorithm.h" #include "src/__support/CPP/bit.h" #include "src/__support/CPP/type_traits.h" #include "src/__support/FPUtil/BasicOperations.h" @@ -34,8 +35,9 @@ div(InType x, InType y) { using OutStorageType = typename OutFPBits::StorageType; using InFPBits = FPBits<InType>; using InStorageType = typename InFPBits::StorageType; - using DyadicFloat = - DyadicFloat<cpp::bit_ceil(static_cast<size_t>(InFPBits::SIG_LEN + 1))>; + using DyadicFloat = DyadicFloat<cpp::max( + static_cast<size_t>(16), + cpp::bit_ceil(static_cast<size_t>(InFPBits::SIG_LEN + 1)))>; InFPBits x_bits(x); InFPBits y_bits(y); diff --git a/libc/src/__support/FPUtil/rounding_mode.h b/libc/src/__support/FPUtil/rounding_mode.h index 4ee0a0b..fdc8498 100644 --- a/libc/src/__support/FPUtil/rounding_mode.h +++ b/libc/src/__support/FPUtil/rounding_mode.h @@ -17,30 +17,24 @@ 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 static constexpr bool fenv_is_round_up() { - if (cpp::is_constant_evaluated()) { - return false; - } else { - volatile float x = 0x1.0p-25f; - return (1.0f + x != 1.0f); - } +LIBC_INLINE bool fenv_is_round_up() { + static volatile float x = 0x1.0p-25f; + return (1.0f + x != 1.0f); } // Quick free-standing test whether fegetround() == FE_DOWNWARD. // Using the following observation: // -1.0f - 2^-25 = -1.0f for FE_TONEAREST, FE_UPWARD, FE_TOWARDZERO // = -0x1.000002f for FE_DOWNWARD. -LIBC_INLINE static constexpr bool fenv_is_round_down() { - if (cpp::is_constant_evaluated()) { - return false; - } else { - volatile float x = 0x1.0p-25f; - return (-1.0f - x != -1.0f); - } +LIBC_INLINE bool fenv_is_round_down() { + static volatile float x = 0x1.0p-25f; + return (-1.0f - x != -1.0f); } // Quick free-standing test whether fegetround() == FE_TONEAREST. @@ -49,14 +43,10 @@ LIBC_INLINE static constexpr bool fenv_is_round_down() { // = 0x1.100002p0f for FE_UPWARD, // 1.5f - 2^-24 = 1.5f for FE_TONEAREST, FE_UPWARD // = 0x1.0ffffep-1f for FE_DOWNWARD, FE_TOWARDZERO -LIBC_INLINE static constexpr bool fenv_is_round_to_nearest() { - if (cpp::is_constant_evaluated()) { - return true; - } else { - volatile float x = 0x1.0p-24f; - float y = 1.5f + x; - return (y == 1.5f - x); - } +LIBC_INLINE bool fenv_is_round_to_nearest() { + static volatile float x = 0x1.0p-24f; + float y = 1.5f + x; + return (y == 1.5f - x); } // Quick free-standing test whether fegetround() == FE_TOWARDZERO. @@ -69,13 +59,56 @@ LIBC_INLINE static constexpr bool fenv_is_round_to_nearest() { // (0x1.000002p0f + 2^-24) + (-1.0f - 2^-24) = 2^-23 for FE_TOWARDZERO // = 2^-22 for FE_TONEAREST, FE_UPWARD // = 0 for FE_DOWNWARD +LIBC_INLINE bool fenv_is_round_to_zero() { + static volatile float x = 0x1.0p-24f; + float y = x; + return ((0x1.000002p0f + y) + (-1.0f - y) == 0x1.0p-23f); +} + +// Quick free standing get rounding mode based on the above observations. +LIBC_INLINE int quick_get_round() { + static volatile float x = 0x1.0p-24f; + float y = x; + float z = (0x1.000002p0f + y) + (-1.0f - y); + + if (z == 0.0f) + return FE_DOWNWARD; + if (z == 0x1.0p-23f) + return FE_TOWARDZERO; + 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 { - volatile float x = 0x1.0p-24f; - volatile float y = 0x1.000002p0f + x; - return (y + (-1.0f - x) == 0x1.0p-23f); + return generic::fenv_is_round_to_zero(); } } @@ -84,15 +117,7 @@ LIBC_INLINE static constexpr int quick_get_round() { if (cpp::is_constant_evaluated()) { return FE_TONEAREST; } else { - volatile float x = 0x1.0p-24f; - volatile float y = 0x1.000002p0f + x; - float z = y + (-1.0f - x); - - if (z == 0.0f) - return FE_DOWNWARD; - if (z == 0x1.0p-23f) - return FE_TOWARDZERO; - return (2.0f + x == 2.0f) ? FE_TONEAREST : FE_UPWARD; + return generic::quick_get_round(); } } diff --git a/libc/src/__support/OSUtil/linux/aarch64/vdso.h b/libc/src/__support/OSUtil/linux/aarch64/vdso.h index 3c4c620..ee5777a 100644 --- a/libc/src/__support/OSUtil/linux/aarch64/vdso.h +++ b/libc/src/__support/OSUtil/linux/aarch64/vdso.h @@ -23,6 +23,8 @@ LIBC_INLINE constexpr cpp::string_view symbol_name(VDSOSym sym) { return "__kernel_clock_gettime"; case VDSOSym::ClockGetRes: return "__kernel_clock_getres"; + case VDSOSym::GetRandom: + return "__kernel_getrandom"; default: return ""; } diff --git a/libc/src/__support/OSUtil/linux/vdso_sym.h b/libc/src/__support/OSUtil/linux/vdso_sym.h index 968e153..01f0b72 100644 --- a/libc/src/__support/OSUtil/linux/vdso_sym.h +++ b/libc/src/__support/OSUtil/linux/vdso_sym.h @@ -35,7 +35,8 @@ enum class VDSOSym { RTSigReturn, FlushICache, RiscvHwProbe, - VDSOSymCount + GetRandom, + VDSOSymCount, }; template <VDSOSym sym> LIBC_INLINE constexpr auto dispatcher() { @@ -60,6 +61,9 @@ template <VDSOSym sym> LIBC_INLINE constexpr auto dispatcher() { else if constexpr (sym == VDSOSym::RiscvHwProbe) return static_cast<int (*)(riscv_hwprobe *, size_t, size_t, cpu_set_t *, unsigned)>(nullptr); + else if constexpr (sym == VDSOSym::GetRandom) + return static_cast<int (*)(void *, size_t, unsigned int, void *, size_t)>( + nullptr); else return static_cast<void *>(nullptr); } diff --git a/libc/src/__support/OSUtil/linux/x86_64/vdso.h b/libc/src/__support/OSUtil/linux/x86_64/vdso.h index abe7c33..f46fcb0 100644 --- a/libc/src/__support/OSUtil/linux/x86_64/vdso.h +++ b/libc/src/__support/OSUtil/linux/x86_64/vdso.h @@ -29,6 +29,8 @@ LIBC_INLINE constexpr cpp::string_view symbol_name(VDSOSym sym) { return "__vdso_time"; case VDSOSym::ClockGetRes: return "__vdso_clock_getres"; + case VDSOSym::GetRandom: + return "__vdso_getrandom"; default: return ""; } diff --git a/libc/src/__support/macros/attributes.h b/libc/src/__support/macros/attributes.h index c647467..4ff374b 100644 --- a/libc/src/__support/macros/attributes.h +++ b/libc/src/__support/macros/attributes.h @@ -28,7 +28,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 diff --git a/libc/src/__support/threads/mutex.h b/libc/src/__support/threads/mutex.h index cbef0d0..f64f7e7 100644 --- a/libc/src/__support/threads/mutex.h +++ b/libc/src/__support/threads/mutex.h @@ -12,28 +12,6 @@ #include "src/__support/macros/attributes.h" #include "src/__support/macros/config.h" -// 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 - -#if !defined(LIBC_THREAD_MODE) -#error LIBC_THREAD_MODE is undefined -#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_PLATFORM // Platform independent code will include this header file which pulls diff --git a/libc/src/__support/wchar/character_converter.cpp b/libc/src/__support/wchar/character_converter.cpp index 15d0f47..2667288 100644 --- a/libc/src/__support/wchar/character_converter.cpp +++ b/libc/src/__support/wchar/character_converter.cpp @@ -132,12 +132,6 @@ ErrorOr<char32_t> CharacterConverter::pop_utf32() { return utf32; } -size_t CharacterConverter::sizeAsUTF32() { - return 1; // a single utf-32 value can fit an entire character -} - -size_t CharacterConverter::sizeAsUTF8() { return state->total_bytes; } - ErrorOr<char8_t> CharacterConverter::pop_utf8() { if (isEmpty()) return Error(-1); @@ -170,5 +164,13 @@ ErrorOr<char8_t> CharacterConverter::pop_utf8() { return static_cast<char8_t>(output); } +template <> ErrorOr<char8_t> CharacterConverter::pop() { return pop_utf8(); } +template <> ErrorOr<char32_t> CharacterConverter::pop() { return pop_utf32(); } + +template <> size_t CharacterConverter::sizeAs<char8_t>() { + return state->total_bytes; +} +template <> size_t CharacterConverter::sizeAs<char32_t>() { return 1; } + } // namespace internal } // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/__support/wchar/character_converter.h b/libc/src/__support/wchar/character_converter.h index b6d918f..2cc28ab 100644 --- a/libc/src/__support/wchar/character_converter.h +++ b/libc/src/__support/wchar/character_converter.h @@ -12,6 +12,7 @@ #include "hdr/types/char32_t.h" #include "hdr/types/char8_t.h" #include "hdr/types/size_t.h" +#include "src/__support/CPP/type_traits.h" #include "src/__support/common.h" #include "src/__support/error_or.h" #include "src/__support/wchar/mbstate.h" @@ -31,14 +32,14 @@ public: bool isEmpty(); bool isValidState(); - size_t sizeAsUTF32(); - size_t sizeAsUTF8(); + template <typename CharType> size_t sizeAs(); int push(char8_t utf8_byte); int push(char32_t utf32); ErrorOr<char8_t> pop_utf8(); ErrorOr<char32_t> pop_utf32(); + template <typename CharType> ErrorOr<CharType> pop(); }; } // namespace internal diff --git a/libc/src/__support/wchar/mbsnrtowcs.h b/libc/src/__support/wchar/mbsnrtowcs.h index 54e3152..6abb836 100644 --- a/libc/src/__support/wchar/mbsnrtowcs.h +++ b/libc/src/__support/wchar/mbsnrtowcs.h @@ -36,7 +36,7 @@ LIBC_INLINE static ErrorOr<size_t> mbsnrtowcs(wchar_t *__restrict dst, StringConverter<char8_t> str_conv(reinterpret_cast<const char8_t *>(*src), ps, len, nmc); size_t dst_idx = 0; - ErrorOr<char32_t> converted = str_conv.popUTF32(); + ErrorOr<char32_t> converted = str_conv.pop<char32_t>(); while (converted.has_value()) { if (dst != nullptr) dst[dst_idx] = converted.value(); @@ -47,7 +47,7 @@ LIBC_INLINE static ErrorOr<size_t> mbsnrtowcs(wchar_t *__restrict dst, return dst_idx; } dst_idx++; - converted = str_conv.popUTF32(); + converted = str_conv.pop<char32_t>(); } if (converted.error() == -1) { // if we hit conversion limit diff --git a/libc/src/__support/wchar/string_converter.h b/libc/src/__support/wchar/string_converter.h index 869ebdf..ba628bd 100644 --- a/libc/src/__support/wchar/string_converter.h +++ b/libc/src/__support/wchar/string_converter.h @@ -12,6 +12,7 @@ #include "hdr/types/char32_t.h" #include "hdr/types/char8_t.h" #include "hdr/types/size_t.h" +#include "src/__support/CPP/type_traits.h" #include "src/__support/common.h" #include "src/__support/error_or.h" #include "src/__support/wchar/character_converter.h" @@ -53,9 +54,7 @@ public: size_t srclen = SIZE_MAX) : cr(ps), src(s), src_len(srclen), src_idx(0), num_to_write(dstlen) {} - // TODO: following functions are almost identical - // look into templating CharacterConverter pop functions - ErrorOr<char32_t> popUTF32() { + template <typename CharType> ErrorOr<CharType> pop() { if (num_to_write == 0) return Error(-1); @@ -64,7 +63,7 @@ public: if (!src_elements_read.has_value()) return Error(src_elements_read.error()); - if (cr.sizeAsUTF32() > num_to_write) { + if (cr.sizeAs<CharType>() > num_to_write) { cr.clear(); return Error(-1); } @@ -72,34 +71,9 @@ public: src_idx += src_elements_read.value(); } - auto out = cr.pop_utf32(); - if (out.has_value() && out.value() == L'\0') - src_len = src_idx; - - num_to_write--; - - return out; - } - - ErrorOr<char8_t> popUTF8() { - if (num_to_write == 0) - return Error(-1); - - if (cr.isEmpty() || src_idx == 0) { - auto src_elements_read = pushFullCharacter(); - if (!src_elements_read.has_value()) - return Error(src_elements_read.error()); - - if (cr.sizeAsUTF8() > num_to_write) { - cr.clear(); - return Error(-1); - } - - src_idx += src_elements_read.value(); - } - - auto out = cr.pop_utf8(); - if (out.has_value() && out.value() == '\0') + ErrorOr<CharType> out = cr.pop<CharType>(); + // if out isn't null terminator or an error + if (out.has_value() && out.value() == 0) src_len = src_idx; num_to_write--; diff --git a/libc/src/__support/wchar/wcsnrtombs.h b/libc/src/__support/wchar/wcsnrtombs.h index 433097c..f593a0e 100644 --- a/libc/src/__support/wchar/wcsnrtombs.h +++ b/libc/src/__support/wchar/wcsnrtombs.h @@ -39,7 +39,7 @@ wcsnrtombs(char *__restrict dest, const wchar_t **__restrict ptr_to_src, reinterpret_cast<const char32_t *>(*ptr_to_src), ps, dest_len, num_src_widechars); size_t dst_idx = 0; - ErrorOr<char8_t> converted = str_conv.popUTF8(); + ErrorOr<char8_t> converted = str_conv.pop<char8_t>(); while (converted.has_value()) { if (dest != nullptr) dest[dst_idx] = converted.value(); @@ -51,7 +51,7 @@ wcsnrtombs(char *__restrict dest, const wchar_t **__restrict ptr_to_src, } dst_idx++; - converted = str_conv.popUTF8(); + converted = str_conv.pop<char8_t>(); } if (dest != nullptr) diff --git a/libc/src/dlfcn/CMakeLists.txt b/libc/src/dlfcn/CMakeLists.txt index e3a51ba..8ef0540 100644 --- a/libc/src/dlfcn/CMakeLists.txt +++ b/libc/src/dlfcn/CMakeLists.txt @@ -38,3 +38,25 @@ add_entrypoint_object( libc.include.dlfcn libc.src.errno.errno ) + +add_entrypoint_object( + dlinfo + SRCS + dlinfo.cpp + HDRS + dlinfo.h + DEPENDS + libc.include.dlfcn + libc.src.errno.errno +) + +add_entrypoint_object( + dladdr + SRCS + dladdr.cpp + HDRS + dladdr.h + DEPENDS + libc.include.dlfcn + libc.src.errno.errno +) diff --git a/libc/src/dlfcn/dladdr.cpp b/libc/src/dlfcn/dladdr.cpp new file mode 100644 index 0000000..61490fd --- /dev/null +++ b/libc/src/dlfcn/dladdr.cpp @@ -0,0 +1,21 @@ +//===-- Implementation of dladdr ------------------------------------------===// +// +// 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 "dladdr.h" + +#include "src/__support/common.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +// TODO: https:// github.com/llvm/llvm-project/issues/97929 +LLVM_LIBC_FUNCTION(int, dladdr, (const void *addr, Dl_info *info)) { + return -1; +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/dlfcn/dladdr.h b/libc/src/dlfcn/dladdr.h new file mode 100644 index 0000000..346fc8d --- /dev/null +++ b/libc/src/dlfcn/dladdr.h @@ -0,0 +1,20 @@ +//===-- Implementation header of dladdr -------------------------*- 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_DLFCN_DLADDR_H +#define LLVM_LIBC_SRC_DLFCN_DLADDR_H + +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +int dladdr(const void *, Dl_info *); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_DLFCN_DLADDR_H diff --git a/libc/src/dlfcn/dlinfo.cpp b/libc/src/dlfcn/dlinfo.cpp new file mode 100644 index 0000000..d78cade --- /dev/null +++ b/libc/src/dlfcn/dlinfo.cpp @@ -0,0 +1,23 @@ + +//===-- Implementation of dlinfo ------------------------------------------===// +// +// 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 "dlinfo.h" + +#include "src/__support/common.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +// TODO: https://github.com/llvm/llvm-project/issues/149911 +LLVM_LIBC_FUNCTION(int, dlinfo, + (void *restrict handle, int request, void *restrict info)) { + return -1; +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/dlfcn/dlinfo.h b/libc/src/dlfcn/dlinfo.h new file mode 100644 index 0000000..c2c34f0 --- /dev/null +++ b/libc/src/dlfcn/dlinfo.h @@ -0,0 +1,20 @@ +//===-- Implementation header of dlinfo -------------------------*- 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_DLFCN_DLINFO_H +#define LLVM_LIBC_SRC_DLFCN_DLINFO_H + +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +int dlinfo(void *restrict, int, void *restrict); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_DLFCN_DLINFO_H diff --git a/libc/src/wctype/iswalpha.cpp b/libc/src/wctype/iswalpha.cpp index e18f293..09f55d3 100644 --- a/libc/src/wctype/iswalpha.cpp +++ b/libc/src/wctype/iswalpha.cpp @@ -14,6 +14,6 @@ namespace LIBC_NAMESPACE_DECL { -LLVM_LIBC_FUNCTION(bool, iswalpha, (wint_t c)) { return internal::iswalpha(c); } +LLVM_LIBC_FUNCTION(int, iswalpha, (wint_t c)) { return internal::iswalpha(c); } } // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/wctype/iswalpha.h b/libc/src/wctype/iswalpha.h index 681fc6b..0353388 100644 --- a/libc/src/wctype/iswalpha.h +++ b/libc/src/wctype/iswalpha.h @@ -14,7 +14,7 @@ namespace LIBC_NAMESPACE_DECL { -bool iswalpha(wint_t c); +int iswalpha(wint_t c); } // namespace LIBC_NAMESPACE_DECL diff --git a/libc/test/IntegrationTest/CMakeLists.txt b/libc/test/IntegrationTest/CMakeLists.txt index 3afe354..235e9fe 100644 --- a/libc/test/IntegrationTest/CMakeLists.txt +++ b/libc/test/IntegrationTest/CMakeLists.txt @@ -13,5 +13,6 @@ add_object_library( DEPENDS libc.hdr.stdint_proxy libc.src.__support.OSUtil.osutil + libc.src.__support.CPP.atomic ${arch_specific_deps} ) diff --git a/libc/test/IntegrationTest/test.cpp b/libc/test/IntegrationTest/test.cpp index 8baf746..19eb255 100644 --- a/libc/test/IntegrationTest/test.cpp +++ b/libc/test/IntegrationTest/test.cpp @@ -5,8 +5,8 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// - #include "hdr/stdint_proxy.h" +#include "src/__support/CPP/atomic.h" #include "src/__support/common.h" #include "src/__support/macros/config.h" #include <stddef.h> @@ -63,16 +63,21 @@ int atexit(void (*func)(void)) { return LIBC_NAMESPACE::atexit(func); } // which just hands out continuous blocks from a statically allocated chunk of // memory. -static constexpr uint64_t MEMORY_SIZE = 16384; -static uint8_t memory[MEMORY_SIZE]; -static uint8_t *ptr = memory; +static constexpr uint64_t ALIGNMENT = alignof(double); +static constexpr uint64_t MEMORY_SIZE = 256 * 1024 /* 256 KiB */; +alignas(ALIGNMENT) static uint8_t memory[MEMORY_SIZE]; +static size_t ptr = 0; extern "C" { -void *malloc(size_t s) { - void *mem = ptr; - ptr += s; - return static_cast<uint64_t>(ptr - memory) >= MEMORY_SIZE ? nullptr : mem; +void *malloc(size_t size) { + LIBC_NAMESPACE::cpp::AtomicRef<size_t> ref(ptr); + size = (size + ALIGNMENT - 1) & ~(ALIGNMENT - 1); + size_t old_ptr = + ref.fetch_add(size, LIBC_NAMESPACE::cpp::MemoryOrder::RELAXED); + if (static_cast<size_t>(old_ptr + size) >= MEMORY_SIZE) + return nullptr; + return &memory[old_ptr]; } void free(void *) {} diff --git a/libc/test/integration/src/__support/GPU/match.cpp b/libc/test/integration/src/__support/GPU/match.cpp index 2d314c2..70c22b7 100644 --- a/libc/test/integration/src/__support/GPU/match.cpp +++ b/libc/test/integration/src/__support/GPU/match.cpp @@ -21,7 +21,9 @@ static void test_match() { gpu::match_any(mask, gpu::get_lane_id())); EXPECT_EQ(mask, gpu::match_any(mask, 1)); - uint64_t expected = gpu::get_lane_id() < 16 ? 0xffff : 0xffff0000; + uint64_t full_mask = + gpu::get_lane_size() > 32 ? 0xffffffffffffffff : 0xffffffff; + uint64_t expected = gpu::get_lane_id() < 16 ? 0xffff : full_mask & ~0xffff; EXPECT_EQ(expected, gpu::match_any(mask, gpu::get_lane_id() < 16)); EXPECT_EQ(mask, gpu::match_all(mask, 1)); EXPECT_EQ(0ull, gpu::match_all(mask, gpu::get_lane_id())); diff --git a/libc/test/src/__support/OSUtil/linux/vdso_test.cpp b/libc/test/src/__support/OSUtil/linux/vdso_test.cpp index 2f68470..71892a0 100644 --- a/libc/test/src/__support/OSUtil/linux/vdso_test.cpp +++ b/libc/test/src/__support/OSUtil/linux/vdso_test.cpp @@ -110,8 +110,8 @@ TEST(LlvmLibcOSUtilVDSOTest, RtSigReturn) { using namespace testing::ErrnoSetterMatcher; // must use struct since there is a function of the same name in the same // scope. - struct sigaction sa {}; - struct sigaction old_sa {}; + struct sigaction sa{}; + struct sigaction old_sa{}; sa.sa_handler = sigprof_handler; sa.sa_flags = SA_RESTORER; vdso::TypedSymbol<vdso::VDSOSym::RTSigReturn> symbol; @@ -158,4 +158,30 @@ TEST(LlvmLibcOSUtilVDSOTest, RiscvHwProbe) { } } +TEST(LlvmLibcOSUtilVDSOTest, GetRandom) { + using namespace testing::ErrnoSetterMatcher; + vdso::TypedSymbol<vdso::VDSOSym::GetRandom> symbol; + if (!symbol) + return; + // This structure exists in kernel UAPI header; but we define it on our own to + // make sure we can test it even on platform without support. + struct VGetrandomOpaqueParams { + uint32_t size_of_opaque_states; + uint32_t mmap_prot; + uint32_t mmap_flags; + uint32_t reserved[13]; + }; + VGetrandomOpaqueParams param{0, 0, 0, {}}; + // When getrandom vDSO symbol is called with special parameters (~0 for state + // size), it populates the desired configuration into VGetrandomOpaqueParams. + int res = symbol( + /*buf=*/nullptr, /*count=*/0, /*flags=*/0, + /*opaque_states=*/¶m, + /*size_of_opaque_states=*/~0); + // Test that the size of the states are correctly populated after a successful + // call. + EXPECT_EQ(res, 0); + EXPECT_GT(param.size_of_opaque_states, 0u); +} + } // namespace LIBC_NAMESPACE_DECL diff --git a/libc/test/src/__support/wchar/string_converter_test.cpp b/libc/test/src/__support/wchar/string_converter_test.cpp index d514df9..e45358d 100644 --- a/libc/test/src/__support/wchar/string_converter_test.cpp +++ b/libc/test/src/__support/wchar/string_converter_test.cpp @@ -34,32 +34,32 @@ TEST(LlvmLibcStringConverterTest, UTF8To32) { LIBC_NAMESPACE::internal::StringConverter<char8_t> sc( reinterpret_cast<const char8_t *>(src), &state, SIZE_MAX); - auto res = sc.popUTF32(); + auto res = sc.pop<char32_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(res.value()), 0x1f921); ASSERT_EQ(static_cast<int>(sc.getSourceIndex()), 4); - res = sc.popUTF32(); + res = sc.pop<char32_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(res.value()), 0x2211); ASSERT_EQ(static_cast<int>(sc.getSourceIndex()), 7); - res = sc.popUTF32(); + res = sc.pop<char32_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(res.value()), 0xff); ASSERT_EQ(static_cast<int>(sc.getSourceIndex()), 9); - res = sc.popUTF32(); + res = sc.pop<char32_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(res.value()), 0x41); ASSERT_EQ(static_cast<int>(sc.getSourceIndex()), 10); - res = sc.popUTF32(); + res = sc.pop<char32_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(res.value()), 0); ASSERT_EQ(static_cast<int>(sc.getSourceIndex()), 11); - res = sc.popUTF32(); + res = sc.pop<char32_t>(); ASSERT_FALSE(res.has_value()); ASSERT_EQ(res.error(), -1); ASSERT_EQ(static_cast<int>(sc.getSourceIndex()), 11); @@ -75,66 +75,66 @@ TEST(LlvmLibcStringConverterTest, UTF32To8) { LIBC_NAMESPACE::internal::StringConverter<char32_t> sc( reinterpret_cast<const char32_t *>(src), &state, SIZE_MAX); - auto res = sc.popUTF8(); + auto res = sc.pop<char8_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(res.value()), 0xF0); ASSERT_EQ(static_cast<int>(sc.getSourceIndex()), 1); - res = sc.popUTF8(); + res = sc.pop<char8_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(res.value()), 0x9F); ASSERT_EQ(static_cast<int>(sc.getSourceIndex()), 1); - res = sc.popUTF8(); + res = sc.pop<char8_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(res.value()), 0xA4); ASSERT_EQ(static_cast<int>(sc.getSourceIndex()), 1); - res = sc.popUTF8(); + res = sc.pop<char8_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(res.value()), 0xA1); ASSERT_EQ(static_cast<int>(sc.getSourceIndex()), 1); // end of clown emoji, sigma symbol begins - res = sc.popUTF8(); + res = sc.pop<char8_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(res.value()), 0xE2); ASSERT_EQ(static_cast<int>(sc.getSourceIndex()), 2); - res = sc.popUTF8(); + res = sc.pop<char8_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(res.value()), 0x88); ASSERT_EQ(static_cast<int>(sc.getSourceIndex()), 2); - res = sc.popUTF8(); + res = sc.pop<char8_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(res.value()), 0x91); ASSERT_EQ(static_cast<int>(sc.getSourceIndex()), 2); // end of sigma symbol, y with diaeresis begins - res = sc.popUTF8(); + res = sc.pop<char8_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(res.value()), 0xC3); ASSERT_EQ(static_cast<int>(sc.getSourceIndex()), 3); - res = sc.popUTF8(); + res = sc.pop<char8_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(res.value()), 0xBF); ASSERT_EQ(static_cast<int>(sc.getSourceIndex()), 3); // end of y with diaeresis, letter A begins - res = sc.popUTF8(); + res = sc.pop<char8_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(res.value()), 0x41); ASSERT_EQ(static_cast<int>(sc.getSourceIndex()), 4); // null byte - res = sc.popUTF8(); + res = sc.pop<char8_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(res.value()), 0); ASSERT_EQ(static_cast<int>(sc.getSourceIndex()), 5); - res = sc.popUTF8(); + res = sc.pop<char8_t>(); ASSERT_FALSE(res.has_value()); ASSERT_EQ(res.error(), -1); ASSERT_EQ(static_cast<int>(sc.getSourceIndex()), 5); @@ -148,28 +148,28 @@ TEST(LlvmLibcStringConverterTest, UTF32To8PartialRead) { LIBC_NAMESPACE::internal::StringConverter<char32_t> sc( reinterpret_cast<const char32_t *>(src), &state, SIZE_MAX, 1); - auto res = sc.popUTF8(); + auto res = sc.pop<char8_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(res.value()), 0xF0); ASSERT_EQ(static_cast<int>(sc.getSourceIndex()), 1); - res = sc.popUTF8(); + res = sc.pop<char8_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(res.value()), 0x9F); ASSERT_EQ(static_cast<int>(sc.getSourceIndex()), 1); - res = sc.popUTF8(); + res = sc.pop<char8_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(res.value()), 0xA4); ASSERT_EQ(static_cast<int>(sc.getSourceIndex()), 1); - res = sc.popUTF8(); + res = sc.pop<char8_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(res.value()), 0xA1); ASSERT_EQ(static_cast<int>(sc.getSourceIndex()), 1); // can only read 1 character from source string, so error on next pop - res = sc.popUTF8(); + res = sc.pop<char8_t>(); ASSERT_FALSE(res.has_value()); ASSERT_EQ(res.error(), -1); } @@ -181,12 +181,12 @@ TEST(LlvmLibcStringConverterTest, UTF8To32PartialRead) { LIBC_NAMESPACE::internal::StringConverter<char8_t> sc( reinterpret_cast<const char8_t *>(src), &state, SIZE_MAX, 5); - auto res = sc.popUTF32(); + auto res = sc.pop<char32_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(res.value()), 0x1f921); ASSERT_EQ(static_cast<int>(sc.getSourceIndex()), 4); - res = sc.popUTF32(); + res = sc.pop<char32_t>(); ASSERT_FALSE(res.has_value()); ASSERT_EQ(static_cast<int>(res.error()), -1); ASSERT_EQ(static_cast<int>(sc.getSourceIndex()), 5); @@ -200,27 +200,27 @@ TEST(LlvmLibcStringConverterTest, UTF32To8ErrorHandling) { LIBC_NAMESPACE::internal::StringConverter<char32_t> sc( reinterpret_cast<const char32_t *>(src), &state, SIZE_MAX); - auto res = sc.popUTF8(); + auto res = sc.pop<char8_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(res.value()), 0xF0); ASSERT_EQ(static_cast<int>(sc.getSourceIndex()), 1); - res = sc.popUTF8(); + res = sc.pop<char8_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(res.value()), 0x9F); ASSERT_EQ(static_cast<int>(sc.getSourceIndex()), 1); - res = sc.popUTF8(); + res = sc.pop<char8_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(res.value()), 0xA4); ASSERT_EQ(static_cast<int>(sc.getSourceIndex()), 1); - res = sc.popUTF8(); + res = sc.pop<char8_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(res.value()), 0xA1); ASSERT_EQ(static_cast<int>(sc.getSourceIndex()), 1); - res = sc.popUTF8(); + res = sc.pop<char8_t>(); ASSERT_FALSE(res.has_value()); ASSERT_EQ(static_cast<int>(res.error()), EILSEQ); ASSERT_EQ(static_cast<int>(sc.getSourceIndex()), 1); @@ -234,12 +234,12 @@ TEST(LlvmLibcStringConverterTest, UTF8To32ErrorHandling) { LIBC_NAMESPACE::internal::StringConverter<char8_t> sc( reinterpret_cast<const char8_t *>(src), &state, SIZE_MAX); - auto res = sc.popUTF32(); + auto res = sc.pop<char32_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(res.value()), 0x1f921); ASSERT_EQ(static_cast<int>(sc.getSourceIndex()), 4); - res = sc.popUTF32(); + res = sc.pop<char32_t>(); ASSERT_FALSE(res.has_value()); ASSERT_EQ(static_cast<int>(res.error()), EILSEQ); ASSERT_EQ(static_cast<int>(sc.getSourceIndex()), 4); @@ -257,12 +257,12 @@ TEST(LlvmLibcStringConverterTest, InvalidCharacterOutsideBounds) { LIBC_NAMESPACE::internal::StringConverter<char8_t> sc1( reinterpret_cast<const char8_t *>(src1), &ps1, 1); - auto res1 = sc1.popUTF32(); + auto res1 = sc1.pop<char32_t>(); ASSERT_TRUE(res1.has_value()); ASSERT_EQ(static_cast<int>(res1.value()), 0x1f921); ASSERT_EQ(static_cast<int>(sc1.getSourceIndex()), 4); - res1 = sc1.popUTF32(); + res1 = sc1.pop<char32_t>(); ASSERT_FALSE(res1.has_value()); // no space to write error NOT invalid character error (EILSEQ) ASSERT_EQ(static_cast<int>(res1.error()), -1); @@ -275,27 +275,27 @@ TEST(LlvmLibcStringConverterTest, InvalidCharacterOutsideBounds) { LIBC_NAMESPACE::internal::StringConverter<char32_t> sc2( reinterpret_cast<const char32_t *>(src2), &ps2, 4); - auto res2 = sc2.popUTF8(); + auto res2 = sc2.pop<char8_t>(); ASSERT_TRUE(res2.has_value()); ASSERT_EQ(static_cast<int>(res2.value()), 0xF0); ASSERT_EQ(static_cast<int>(sc2.getSourceIndex()), 1); - res2 = sc2.popUTF8(); + res2 = sc2.pop<char8_t>(); ASSERT_TRUE(res2.has_value()); ASSERT_EQ(static_cast<int>(res2.value()), 0x9F); ASSERT_EQ(static_cast<int>(sc2.getSourceIndex()), 1); - res2 = sc2.popUTF8(); + res2 = sc2.pop<char8_t>(); ASSERT_TRUE(res2.has_value()); ASSERT_EQ(static_cast<int>(res2.value()), 0xA4); ASSERT_EQ(static_cast<int>(sc2.getSourceIndex()), 1); - res2 = sc2.popUTF8(); + res2 = sc2.pop<char8_t>(); ASSERT_TRUE(res2.has_value()); ASSERT_EQ(static_cast<int>(res2.value()), 0xA1); ASSERT_EQ(static_cast<int>(sc2.getSourceIndex()), 1); - res2 = sc2.popUTF8(); + res2 = sc2.pop<char8_t>(); ASSERT_FALSE(res2.has_value()); // no space to write error NOT invalid character error (EILSEQ) ASSERT_EQ(static_cast<int>(res2.error()), -1); @@ -315,22 +315,22 @@ TEST(LlvmLibcStringConverterTest, MultipleStringConverters32To8) { LIBC_NAMESPACE::internal::StringConverter<char32_t> sc1( reinterpret_cast<const char32_t *>(src), &state, SIZE_MAX, 1); - auto res = sc1.popUTF8(); + auto res = sc1.pop<char8_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(res.value()), 0xF0); ASSERT_EQ(static_cast<int>(sc1.getSourceIndex()), 1); - res = sc1.popUTF8(); + res = sc1.pop<char8_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(res.value()), 0x9F); ASSERT_EQ(static_cast<int>(sc1.getSourceIndex()), 1); - res = sc1.popUTF8(); + res = sc1.pop<char8_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(res.value()), 0xA4); ASSERT_EQ(static_cast<int>(sc1.getSourceIndex()), 1); - res = sc1.popUTF8(); + res = sc1.pop<char8_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(res.value()), 0xA1); ASSERT_EQ(static_cast<int>(sc1.getSourceIndex()), 1); @@ -340,12 +340,12 @@ TEST(LlvmLibcStringConverterTest, MultipleStringConverters32To8) { reinterpret_cast<const char32_t *>(src) + sc1.getSourceIndex(), &state, SIZE_MAX, 1); - res = sc2.popUTF8(); + res = sc2.pop<char8_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(res.value()), 0xC3); ASSERT_EQ(static_cast<int>(sc2.getSourceIndex()), 1); - res = sc2.popUTF8(); + res = sc2.pop<char8_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(res.value()), 0xBF); ASSERT_EQ(static_cast<int>(sc2.getSourceIndex()), 1); @@ -357,7 +357,7 @@ TEST(LlvmLibcStringConverterTest, MultipleStringConverters8To32) { LIBC_NAMESPACE::internal::StringConverter<char8_t> sc1( reinterpret_cast<const char8_t *>(src), &state, SIZE_MAX, 2); - auto res = sc1.popUTF32(); + auto res = sc1.pop<char32_t>(); ASSERT_FALSE(res.has_value()); ASSERT_EQ(static_cast<int>(res.error()), -1); ASSERT_EQ(static_cast<int>(sc1.getSourceIndex()), 2); @@ -367,12 +367,12 @@ TEST(LlvmLibcStringConverterTest, MultipleStringConverters8To32) { reinterpret_cast<const char8_t *>(src) + sc1.getSourceIndex(), &state, SIZE_MAX, 3); - res = sc2.popUTF32(); + res = sc2.pop<char32_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(res.value()), 0x1f921); ASSERT_EQ(static_cast<int>(sc2.getSourceIndex()), 2); - res = sc2.popUTF32(); + res = sc2.pop<char32_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(res.value()), 0); ASSERT_EQ(static_cast<int>(sc2.getSourceIndex()), 3); @@ -384,11 +384,11 @@ TEST(LlvmLibcStringConverterTest, DestLimitUTF8To32) { LIBC_NAMESPACE::internal::StringConverter<char8_t> sc( reinterpret_cast<const char8_t *>(src), &state, 1); - auto res = sc.popUTF32(); + auto res = sc.pop<char32_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(sc.getSourceIndex()), 4); - res = sc.popUTF32(); // no space to pop this into + res = sc.pop<char32_t>(); // no space to pop this into ASSERT_FALSE(res.has_value()); } @@ -399,23 +399,23 @@ TEST(LlvmLibcStringConverterTest, DestLimitUTF32To8) { LIBC_NAMESPACE::internal::StringConverter<char32_t> sc( reinterpret_cast<const char32_t *>(src), &state, 5); - auto res = sc.popUTF8(); + auto res = sc.pop<char8_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(sc.getSourceIndex()), 1); - res = sc.popUTF8(); + res = sc.pop<char8_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(sc.getSourceIndex()), 1); - res = sc.popUTF8(); + res = sc.pop<char8_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(sc.getSourceIndex()), 1); - res = sc.popUTF8(); + res = sc.pop<char8_t>(); ASSERT_TRUE(res.has_value()); ASSERT_EQ(static_cast<int>(sc.getSourceIndex()), 1); - res = sc.popUTF8(); + res = sc.pop<char8_t>(); ASSERT_FALSE(res.has_value()); ASSERT_EQ(static_cast<int>(sc.getSourceIndex()), 1); } diff --git a/libc/test/src/math/exhaustive/CMakeLists.txt b/libc/test/src/math/exhaustive/CMakeLists.txt index 5246337..e40972e 100644 --- a/libc/test/src/math/exhaustive/CMakeLists.txt +++ b/libc/test/src/math/exhaustive/CMakeLists.txt @@ -567,3 +567,75 @@ add_fp_unittest( LINK_LIBRARIES -lpthread ) + +add_fp_unittest( + bfloat16_add_test + NO_RUN_POSTBUILD + NEED_MPFR + SUITE + libc_math_exhaustive_tests + SRCS + bfloat16_add_test.cpp + COMPILE_OPTIONS + ${libc_opt_high_flag} + DEPENDS + .exhaustive_test + libc.src.__support.FPUtil.bfloat16 + libc.src.__support.FPUtil.fp_bits + LINK_LIBRARIES + -lpthread +) + +add_fp_unittest( + bfloat16_div_test + NO_RUN_POSTBUILD + NEED_MPFR + SUITE + libc_math_exhaustive_tests + SRCS + bfloat16_div_test.cpp + COMPILE_OPTIONS + ${libc_opt_high_flag} + DEPENDS + .exhaustive_test + libc.src.__support.FPUtil.bfloat16 + libc.src.__support.FPUtil.fp_bits + LINK_LIBRARIES + -lpthread +) + +add_fp_unittest( + bfloat16_mul_test + NO_RUN_POSTBUILD + NEED_MPFR + SUITE + libc_math_exhaustive_tests + SRCS + bfloat16_mul_test.cpp + COMPILE_OPTIONS + ${libc_opt_high_flag} + DEPENDS + .exhaustive_test + libc.src.__support.FPUtil.bfloat16 + libc.src.__support.FPUtil.fp_bits + LINK_LIBRARIES + -lpthread +) + +add_fp_unittest( + bfloat16_sub_test + NO_RUN_POSTBUILD + NEED_MPFR + SUITE + libc_math_exhaustive_tests + SRCS + bfloat16_sub_test.cpp + COMPILE_OPTIONS + ${libc_opt_high_flag} + DEPENDS + .exhaustive_test + libc.src.__support.FPUtil.bfloat16 + libc.src.__support.FPUtil.fp_bits + LINK_LIBRARIES + -lpthread +) diff --git a/libc/test/src/math/exhaustive/bfloat16_add_test.cpp b/libc/test/src/math/exhaustive/bfloat16_add_test.cpp new file mode 100644 index 0000000..3f4c779 --- /dev/null +++ b/libc/test/src/math/exhaustive/bfloat16_add_test.cpp @@ -0,0 +1,65 @@ +//===-- Exhaustive tests for bfloat16 addition ----------------------------===// +// +// 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 "exhaustive_test.h" +#include "src/__support/FPUtil/FPBits.h" +#include "src/__support/FPUtil/bfloat16.h" +#include "test/UnitTest/FPMatcher.h" +#include "utils/MPFRWrapper/MPCommon.h" +#include "utils/MPFRWrapper/MPFRUtils.h" + +namespace mpfr = LIBC_NAMESPACE::testing::mpfr; +using LIBC_NAMESPACE::fputil::BFloat16; + +static BFloat16 add_func(BFloat16 x, BFloat16 y) { return x + y; } + +struct Bfloat16AddChecker : public virtual LIBC_NAMESPACE::testing::Test { + using FloatType = BFloat16; + using FPBits = LIBC_NAMESPACE::fputil::FPBits<bfloat16>; + using StorageType = typename FPBits::StorageType; + + uint64_t check(uint16_t x_start, uint16_t x_stop, uint16_t y_start, + uint16_t y_stop, mpfr::RoundingMode rounding) { + mpfr::ForceRoundingMode r(rounding); + if (!r.success) + return true; + uint16_t xbits = x_start; + uint64_t failed = 0; + do { + BFloat16 x = FPBits(xbits).get_val(); + uint16_t ybits = xbits; + do { + BFloat16 y = FPBits(ybits).get_val(); + mpfr::BinaryInput<BFloat16> input{x, y}; + bool correct = TEST_MPFR_MATCH_ROUNDING_SILENTLY( + mpfr::Operation::Add, input, add_func(x, y), 0.5, rounding); + failed += (!correct); + } while (ybits++ < y_stop); + } while (xbits++ < x_stop); + return failed; + } +}; + +using LlvmLibcBfloat16ExhaustiveAddTest = + LlvmLibcExhaustiveMathTest<Bfloat16AddChecker, 1 << 2>; + +// range: [0, inf] +static constexpr uint16_t POS_START = 0x0000U; +static constexpr uint16_t POS_STOP = 0x7f80U; + +// range: [-0, -inf] +static constexpr uint16_t NEG_START = 0x8000U; +static constexpr uint16_t NEG_STOP = 0xff80U; + +TEST_F(LlvmLibcBfloat16ExhaustiveAddTest, PositiveRange) { + test_full_range_all_roundings(POS_START, POS_STOP, POS_START, POS_STOP); +} + +TEST_F(LlvmLibcBfloat16ExhaustiveAddTest, NegativeRange) { + test_full_range_all_roundings(NEG_START, NEG_STOP, NEG_START, NEG_STOP); +} diff --git a/libc/test/src/math/exhaustive/bfloat16_div_test.cpp b/libc/test/src/math/exhaustive/bfloat16_div_test.cpp new file mode 100644 index 0000000..2648d5f --- /dev/null +++ b/libc/test/src/math/exhaustive/bfloat16_div_test.cpp @@ -0,0 +1,65 @@ +//===-- Exhaustive tests for bfloat16 division ----------------------------===// +// +// 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 "exhaustive_test.h" +#include "src/__support/FPUtil/FPBits.h" +#include "src/__support/FPUtil/bfloat16.h" +#include "test/UnitTest/FPMatcher.h" +#include "utils/MPFRWrapper/MPCommon.h" +#include "utils/MPFRWrapper/MPFRUtils.h" + +namespace mpfr = LIBC_NAMESPACE::testing::mpfr; +using LIBC_NAMESPACE::fputil::BFloat16; + +static BFloat16 div_func(BFloat16 x, BFloat16 y) { return x / y; } + +struct Bfloat16DivChecker : public virtual LIBC_NAMESPACE::testing::Test { + using FloatType = BFloat16; + using FPBits = LIBC_NAMESPACE::fputil::FPBits<bfloat16>; + using StorageType = typename FPBits::StorageType; + + uint64_t check(uint16_t x_start, uint16_t x_stop, uint16_t y_start, + uint16_t y_stop, mpfr::RoundingMode rounding) { + mpfr::ForceRoundingMode r(rounding); + if (!r.success) + return true; + uint16_t xbits = x_start; + uint64_t failed = 0; + do { + BFloat16 x = FPBits(xbits).get_val(); + uint16_t ybits = xbits; + do { + BFloat16 y = FPBits(ybits).get_val(); + mpfr::BinaryInput<BFloat16> input{x, y}; + bool correct = TEST_MPFR_MATCH_ROUNDING_SILENTLY( + mpfr::Operation::Div, input, div_func(x, y), 0.5, rounding); + failed += (!correct); + } while (ybits++ < y_stop); + } while (xbits++ < x_stop); + return failed; + } +}; + +using LlvmLibcBfloat16ExhaustiveDivTest = + LlvmLibcExhaustiveMathTest<Bfloat16DivChecker, 1 << 2>; + +// range: [0, inf] +static constexpr uint16_t POS_START = 0x0000U; +static constexpr uint16_t POS_STOP = 0x7f80U; + +// range: [-0, -inf] +static constexpr uint16_t NEG_START = 0x8000U; +static constexpr uint16_t NEG_STOP = 0xff80U; + +TEST_F(LlvmLibcBfloat16ExhaustiveDivTest, PositiveRange) { + test_full_range_all_roundings(POS_START, POS_STOP, POS_START, POS_STOP); +} + +TEST_F(LlvmLibcBfloat16ExhaustiveDivTest, NegativeRange) { + test_full_range_all_roundings(NEG_START, NEG_STOP, NEG_START, NEG_STOP); +} diff --git a/libc/test/src/math/exhaustive/bfloat16_mul_test.cpp b/libc/test/src/math/exhaustive/bfloat16_mul_test.cpp new file mode 100644 index 0000000..3cbbcb5 --- /dev/null +++ b/libc/test/src/math/exhaustive/bfloat16_mul_test.cpp @@ -0,0 +1,65 @@ +//===-- Exhaustive tests for bfloat16 multiplication ----------------------===// +// +// 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 "exhaustive_test.h" +#include "src/__support/FPUtil/FPBits.h" +#include "src/__support/FPUtil/bfloat16.h" +#include "test/UnitTest/FPMatcher.h" +#include "utils/MPFRWrapper/MPCommon.h" +#include "utils/MPFRWrapper/MPFRUtils.h" + +namespace mpfr = LIBC_NAMESPACE::testing::mpfr; +using LIBC_NAMESPACE::fputil::BFloat16; + +static BFloat16 mul_func(BFloat16 x, BFloat16 y) { return x * y; } + +struct Bfloat16MulChecker : public virtual LIBC_NAMESPACE::testing::Test { + using FloatType = BFloat16; + using FPBits = LIBC_NAMESPACE::fputil::FPBits<bfloat16>; + using StorageType = typename FPBits::StorageType; + + uint64_t check(uint16_t x_start, uint16_t x_stop, uint16_t y_start, + uint16_t y_stop, mpfr::RoundingMode rounding) { + mpfr::ForceRoundingMode r(rounding); + if (!r.success) + return true; + uint16_t xbits = x_start; + uint64_t failed = 0; + do { + BFloat16 x = FPBits(xbits).get_val(); + uint16_t ybits = xbits; + do { + BFloat16 y = FPBits(ybits).get_val(); + mpfr::BinaryInput<BFloat16> input{x, y}; + bool correct = TEST_MPFR_MATCH_ROUNDING_SILENTLY( + mpfr::Operation::Mul, input, mul_func(x, y), 0.5, rounding); + failed += (!correct); + } while (ybits++ < y_stop); + } while (xbits++ < x_stop); + return failed; + } +}; + +using LlvmLibcBfloat16ExhaustiveMulTest = + LlvmLibcExhaustiveMathTest<Bfloat16MulChecker, 1 << 2>; + +// range: [0, inf] +static constexpr uint16_t POS_START = 0x0000U; +static constexpr uint16_t POS_STOP = 0x7f80U; + +// range: [-0, -inf] +static constexpr uint16_t NEG_START = 0x8000U; +static constexpr uint16_t NEG_STOP = 0xff80U; + +TEST_F(LlvmLibcBfloat16ExhaustiveMulTest, PositiveRange) { + test_full_range_all_roundings(POS_START, POS_STOP, POS_START, POS_STOP); +} + +TEST_F(LlvmLibcBfloat16ExhaustiveMulTest, NegativeRange) { + test_full_range_all_roundings(NEG_START, NEG_STOP, NEG_START, NEG_STOP); +} diff --git a/libc/test/src/math/exhaustive/bfloat16_sub_test.cpp b/libc/test/src/math/exhaustive/bfloat16_sub_test.cpp new file mode 100644 index 0000000..11bc6f5 --- /dev/null +++ b/libc/test/src/math/exhaustive/bfloat16_sub_test.cpp @@ -0,0 +1,65 @@ +//===-- Exhaustive tests for bfloat16 subtraction -------------------------===// +// +// 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 "exhaustive_test.h" +#include "src/__support/FPUtil/FPBits.h" +#include "src/__support/FPUtil/bfloat16.h" +#include "test/UnitTest/FPMatcher.h" +#include "utils/MPFRWrapper/MPCommon.h" +#include "utils/MPFRWrapper/MPFRUtils.h" + +namespace mpfr = LIBC_NAMESPACE::testing::mpfr; +using LIBC_NAMESPACE::fputil::BFloat16; + +static BFloat16 sub_func(BFloat16 x, BFloat16 y) { return x - y; } + +struct Bfloat16SubChecker : public virtual LIBC_NAMESPACE::testing::Test { + using FloatType = BFloat16; + using FPBits = LIBC_NAMESPACE::fputil::FPBits<bfloat16>; + using StorageType = typename FPBits::StorageType; + + uint64_t check(uint16_t x_start, uint16_t x_stop, uint16_t y_start, + uint16_t y_stop, mpfr::RoundingMode rounding) { + mpfr::ForceRoundingMode r(rounding); + if (!r.success) + return true; + uint16_t xbits = x_start; + uint64_t failed = 0; + do { + BFloat16 x = FPBits(xbits).get_val(); + uint16_t ybits = xbits; + do { + BFloat16 y = FPBits(ybits).get_val(); + mpfr::BinaryInput<BFloat16> input{x, y}; + bool correct = TEST_MPFR_MATCH_ROUNDING_SILENTLY( + mpfr::Operation::Sub, input, sub_func(x, y), 0.5, rounding); + failed += (!correct); + } while (ybits++ < y_stop); + } while (xbits++ < x_stop); + return failed; + } +}; + +using LlvmLibcBfloat16ExhaustiveSubTest = + LlvmLibcExhaustiveMathTest<Bfloat16SubChecker, 1 << 2>; + +// range: [0, inf] +static constexpr uint16_t POS_START = 0x0000U; +static constexpr uint16_t POS_STOP = 0x7f80U; + +// range: [-0, -inf] +static constexpr uint16_t NEG_START = 0x8000U; +static constexpr uint16_t NEG_STOP = 0xff80U; + +TEST_F(LlvmLibcBfloat16ExhaustiveSubTest, PositiveRange) { + test_full_range_all_roundings(POS_START, POS_STOP, POS_START, POS_STOP); +} + +TEST_F(LlvmLibcBfloat16ExhaustiveSubTest, NegativeRange) { + test_full_range_all_roundings(NEG_START, NEG_STOP, NEG_START, NEG_STOP); +} diff --git a/libc/test/src/math/exhaustive/exhaustive_test.h b/libc/test/src/math/exhaustive/exhaustive_test.h index cdf459c..8be65ba 100644 --- a/libc/test/src/math/exhaustive/exhaustive_test.h +++ b/libc/test/src/math/exhaustive/exhaustive_test.h @@ -164,7 +164,7 @@ struct LlvmLibcExhaustiveMathTest range_begin = current_value; if (stop >= Increment && stop - Increment >= current_value) { - range_end = current_value + Increment; + range_end = static_cast<StorageType>(current_value + Increment); } else { range_end = stop; } diff --git a/libc/test/src/math/smoke/CMakeLists.txt b/libc/test/src/math/smoke/CMakeLists.txt index 40b7a342..a722f61 100644 --- a/libc/test/src/math/smoke/CMakeLists.txt +++ b/libc/test/src/math/smoke/CMakeLists.txt @@ -5307,6 +5307,69 @@ add_fp_unittest( ) add_fp_unittest( + bfloat16_add_test + SUITE + libc-math-smoke-tests + SRCS + bfloat16_add_test.cpp + HDRS + AddTest.h + DEPENDS + libc.src.__support.FPUtil.bfloat16 + libc.src.__support.FPUtil.generic.add_sub + libc.src.__support.macros.properties.os + libc.src.__support.macros.properties.types + libc.hdr.errno_macros + libc.hdr.fenv_macros +) + +add_fp_unittest( + bfloat16_div_test + SUITE + libc-math-smoke-tests + SRCS + bfloat16_div_test.cpp + HDRS + DivTest.h + DEPENDS + libc.src.__support.FPUtil.bfloat16 + libc.hdr.errno_macros + libc.hdr.fenv_macros +) + +add_fp_unittest( + bfloat16_mul_test + SUITE + libc-math-smoke-tests + SRCS + bfloat16_mul_test.cpp + HDRS + MulTest.h + DEPENDS + libc.src.__support.FPUtil.basic_operations + libc.src.__support.FPUtil.bfloat16 + libc.hdr.errno_macros + libc.hdr.fenv_macros +) + +add_fp_unittest( + bfloat16_sub_test + SUITE + libc-math-smoke-tests + SRCS + bfloat16_sub_test.cpp + HDRS + SubTest.h + DEPENDS + libc.src.__support.FPUtil.bfloat16 + libc.src.__support.FPUtil.generic.add_sub + libc.src.__support.macros.properties.os + libc.src.__support.macros.properties.types + libc.hdr.errno_macros + libc.hdr.fenv_macros +) + +add_fp_unittest( add_same_type_test SUITE libc-math-smoke-tests diff --git a/libc/test/src/math/smoke/MulTest.h b/libc/test/src/math/smoke/MulTest.h index cf7f41a..a45f4224 100644 --- a/libc/test/src/math/smoke/MulTest.h +++ b/libc/test/src/math/smoke/MulTest.h @@ -53,10 +53,12 @@ public: EXPECT_FP_EQ_ALL_ROUNDING(neg_zero, func(in.zero, in.neg_zero)); EXPECT_FP_EQ_ALL_ROUNDING(neg_zero, func(in.neg_zero, in.zero)); - EXPECT_FP_EQ_ALL_ROUNDING(OutType(1.0), func(1.0, 1.0)); - EXPECT_FP_EQ_ALL_ROUNDING(OutType(15.0), func(3.0, 5.0)); - EXPECT_FP_EQ_ALL_ROUNDING(OutType(0x1.0p-13), func(0x1.0p+1, 0x1.0p-14)); - EXPECT_FP_EQ_ALL_ROUNDING(OutType(0x1.0p-10), func(0x1.0p+2, 0x1.0p-12)); + EXPECT_FP_EQ_ALL_ROUNDING(InType(1.0), func(InType(1.0), InType(1.0))); + EXPECT_FP_EQ_ALL_ROUNDING(InType(15.0), func(InType(3.0), InType(5.0))); + EXPECT_FP_EQ_ALL_ROUNDING(OutType(0x1.0p-13), + func(InType(0x1.0p+1), InType(0x1.0p-14))); + EXPECT_FP_EQ_ALL_ROUNDING(OutType(0x1.0p-10), + func(InType(0x1.0p+2), InType(0x1.0p-12))); } void test_invalid_operations(MulFunc func) { diff --git a/libc/test/src/math/smoke/bfloat16_add_test.cpp b/libc/test/src/math/smoke/bfloat16_add_test.cpp new file mode 100644 index 0000000..1db65a9 --- /dev/null +++ b/libc/test/src/math/smoke/bfloat16_add_test.cpp @@ -0,0 +1,15 @@ +//===-- Unittests for bfloat16 addition -----------------------------------===// +// +// 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 "AddTest.h" + +#include "src/__support/FPUtil/bfloat16.h" + +static bfloat16 add_func(bfloat16 x, bfloat16 y) { return x + y; } + +LIST_ADD_TESTS(bfloat16, bfloat16, add_func) diff --git a/libc/test/src/math/smoke/bfloat16_div_test.cpp b/libc/test/src/math/smoke/bfloat16_div_test.cpp new file mode 100644 index 0000000..c1ab598 --- /dev/null +++ b/libc/test/src/math/smoke/bfloat16_div_test.cpp @@ -0,0 +1,15 @@ +//===-- Unittests for bfloat16 division -----------------------------------===// +// +// 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 "DivTest.h" + +#include "src/__support/FPUtil/bfloat16.h" + +static bfloat16 div_func(bfloat16 x, bfloat16 y) { return x / y; } + +LIST_DIV_TESTS(bfloat16, bfloat16, div_func) diff --git a/libc/test/src/math/smoke/bfloat16_mul_test.cpp b/libc/test/src/math/smoke/bfloat16_mul_test.cpp new file mode 100644 index 0000000..03e38d8 --- /dev/null +++ b/libc/test/src/math/smoke/bfloat16_mul_test.cpp @@ -0,0 +1,15 @@ +//===-- Unittests for bfloat16 multiplication -----------------------------===// +// +// 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 "MulTest.h" + +#include "src/__support/FPUtil/bfloat16.h" + +static bfloat16 mul_func(bfloat16 x, bfloat16 y) { return x * y; } + +LIST_MUL_TESTS(bfloat16, bfloat16, mul_func) diff --git a/libc/test/src/math/smoke/bfloat16_sub_test.cpp b/libc/test/src/math/smoke/bfloat16_sub_test.cpp new file mode 100644 index 0000000..5eb4a9b --- /dev/null +++ b/libc/test/src/math/smoke/bfloat16_sub_test.cpp @@ -0,0 +1,15 @@ +//===-- Unittests for bfloat16 subtraction --------------------------------===// +// +// 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 "SubTest.h" + +#include "src/__support/FPUtil/bfloat16.h" + +static bfloat16 sub_func(bfloat16 x, bfloat16 y) { return x - y; } + +LIST_SUB_TESTS(bfloat16, bfloat16, sub_func) diff --git a/libc/test/src/stdfix/BitsFxTest.h b/libc/test/src/stdfix/BitsFxTest.h index eca6ab1..cdb363f 100644 --- a/libc/test/src/stdfix/BitsFxTest.h +++ b/libc/test/src/stdfix/BitsFxTest.h @@ -54,7 +54,7 @@ public: if (max >= 11 && FXRep::FRACTION_LEN >= kMinFbits) { // (10.71875)_10 = (1010.1011100)_2 - constexpr long long kExpected = 1372; + constexpr int64_t kExpected = 1372; EXPECT_EQ( static_cast<XType>(kExpected << (FXRep::FRACTION_LEN - kMinFbits)), func(special_num_t)); @@ -63,9 +63,11 @@ public: if constexpr (FXRep::SIGN_LEN > 0) { if (min <= -11 && FXRep::FRACTION_LEN >= kMinFbits) { // (-10.71875)_10 = (-1010.1011100)_2 - constexpr long long kExpected = -1372; - EXPECT_EQ(static_cast<XType>(kExpected - << (FXRep::FRACTION_LEN - kMinFbits)), + constexpr int64_t kExpected = + static_cast<int64_t>(static_cast<uint64_t>(-1372) + << (FXRep::FRACTION_LEN - kMinFbits)); + + EXPECT_EQ(static_cast<XType>(kExpected), func(negative_special_num_t)); } } diff --git a/libc/test/src/wctype/iswalpha_test.cpp b/libc/test/src/wctype/iswalpha_test.cpp index f3f75f4..a82c005 100644 --- a/libc/test/src/wctype/iswalpha_test.cpp +++ b/libc/test/src/wctype/iswalpha_test.cpp @@ -9,46 +9,48 @@ #include "src/__support/CPP/span.h" #include "src/wctype/iswalpha.h" -#include "test/UnitTest/LibcTest.h" #include "test/UnitTest/Test.h" -namespace { - -// TODO: Merge the wctype tests using this framework. -constexpr char WALPHA_ARRAY[] = { - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', -}; - -bool in_span(int ch, LIBC_NAMESPACE::cpp::span<const char> arr) { - for (size_t i = 0; i < arr.size(); ++i) - if (static_cast<int>(arr[i]) == ch) - return true; - return false; -} - -} // namespace - TEST(LlvmLibciswalpha, SimpleTest) { - EXPECT_TRUE(LIBC_NAMESPACE::iswalpha('a')); - EXPECT_TRUE(LIBC_NAMESPACE::iswalpha('B')); - - EXPECT_FALSE(LIBC_NAMESPACE::iswalpha('3')); - EXPECT_FALSE(LIBC_NAMESPACE::iswalpha(' ')); - EXPECT_FALSE(LIBC_NAMESPACE::iswalpha('?')); - EXPECT_FALSE(LIBC_NAMESPACE::iswalpha('\0')); - EXPECT_FALSE(LIBC_NAMESPACE::iswalpha(-1)); + EXPECT_NE(LIBC_NAMESPACE::iswalpha('a'), 0); + EXPECT_NE(LIBC_NAMESPACE::iswalpha('B'), 0); + + EXPECT_EQ(LIBC_NAMESPACE::iswalpha('3'), 0); + EXPECT_EQ(LIBC_NAMESPACE::iswalpha(' '), 0); + EXPECT_EQ(LIBC_NAMESPACE::iswalpha('?'), 0); + EXPECT_EQ(LIBC_NAMESPACE::iswalpha('\0'), 0); + EXPECT_EQ(LIBC_NAMESPACE::iswalpha(-1), 0); } -TEST(LlvmLibciswalpha, DefaultLocale) { - // Loops through all characters, verifying that letters return - // true and everything else returns false. - for (int ch = -255; ch < 255; ++ch) { - if (in_span(ch, WALPHA_ARRAY)) - EXPECT_TRUE(LIBC_NAMESPACE::iswalpha(ch)); - else - EXPECT_FALSE(LIBC_NAMESPACE::iswalpha(ch)); - } -} +// TODO: once iswalpha supports more than just ascii-range characters add a +// proper test. + +// namespace { + +// // TODO: Merge the wctype tests using this framework. +// constexpr char WALPHA_ARRAY[] = { +// 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', +// 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', +// 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', +// 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', +// }; + +// bool in_span(int ch, LIBC_NAMESPACE::cpp::span<const char> arr) { +// for (size_t i = 0; i < arr.size(); ++i) +// if (static_cast<int>(arr[i]) == ch) +// return true; +// return false; +// } + +// } // namespace + +// TEST(LlvmLibciswalpha, DefaultLocale) { +// // Loops through all characters, verifying that letters return +// // true and everything else returns false. +// for (int ch = -255; ch < 255; ++ch) { +// if (in_span(ch, WALPHA_ARRAY)) +// EXPECT_TRUE(LIBC_NAMESPACE::iswalpha(ch)); +// else +// EXPECT_FALSE(LIBC_NAMESPACE::iswalpha(ch)); +// } +// } diff --git a/libc/utils/MPFRWrapper/CMakeLists.txt b/libc/utils/MPFRWrapper/CMakeLists.txt index 2669b23..73151c6 100644 --- a/libc/utils/MPFRWrapper/CMakeLists.txt +++ b/libc/utils/MPFRWrapper/CMakeLists.txt @@ -43,6 +43,7 @@ if(LIBC_TESTS_CAN_USE_MPFR) libc.hdr.stdint_proxy libc.src.__support.CPP.array libc.src.__support.CPP.stringstream + libc.src.__support.FPUtil.bfloat16 libc.src.__support.FPUtil.fp_bits libc.src.__support.FPUtil.fpbits_str LibcTest.unit diff --git a/libc/utils/MPFRWrapper/MPFRUtils.cpp b/libc/utils/MPFRWrapper/MPFRUtils.cpp index 8853f96..ae12a83 100644 --- a/libc/utils/MPFRWrapper/MPFRUtils.cpp +++ b/libc/utils/MPFRWrapper/MPFRUtils.cpp @@ -11,6 +11,7 @@ #include "src/__support/CPP/array.h" #include "src/__support/CPP/stringstream.h" +#include "src/__support/FPUtil/bfloat16.h" #include "src/__support/FPUtil/fpbits_str.h" #include "src/__support/macros/config.h" #include "src/__support/macros/properties/types.h" @@ -408,6 +409,8 @@ template void explain_binary_operation_one_output_error( template void explain_binary_operation_one_output_error( Operation, const BinaryInput<float128> &, float128, double, RoundingMode); #endif +template void explain_binary_operation_one_output_error( + Operation, const BinaryInput<bfloat16> &, bfloat16, double, RoundingMode); template <typename InputType, typename OutputType> void explain_ternary_operation_one_output_error( @@ -641,7 +644,10 @@ template bool compare_binary_operation_one_output(Operation, float128, double, RoundingMode); #endif - +template bool compare_binary_operation_one_output(Operation, + const BinaryInput<bfloat16> &, + bfloat16, double, + RoundingMode); template <typename InputType, typename OutputType> bool compare_ternary_operation_one_output(Operation op, const TernaryInput<InputType> &input, |