diff --git a/libc/CMakeLists.txt b/libc/CMakeLists.txt index 414be906336bf..8eb3a465c20ba 100644 --- a/libc/CMakeLists.txt +++ b/libc/CMakeLists.txt @@ -245,6 +245,7 @@ option(LIBC_INCLUDE_DOCS "Build the libc documentation." ${LLVM_INCLUDE_DOCS}) include(CMakeParseArguments) include(LLVMLibCCheckCpuFeatures) +include(CheckCompilerFeatures) include(LLVMLibCRules) if(EXISTS "${LIBC_SOURCE_DIR}/config/${LIBC_TARGET_OS}/${LIBC_TARGET_ARCHITECTURE}/entrypoints.txt") diff --git a/libc/cmake/modules/CheckCompilerFeatures.cmake b/libc/cmake/modules/CheckCompilerFeatures.cmake new file mode 100644 index 0000000000000..1ad81bbb5f666 --- /dev/null +++ b/libc/cmake/modules/CheckCompilerFeatures.cmake @@ -0,0 +1,59 @@ +# ------------------------------------------------------------------------------ +# Compiler features definition and flags +# ------------------------------------------------------------------------------ + +# Initialize ALL_COMPILER_FEATURES as empty list. +set(ALL_COMPILER_FEATURES "float128") + +# Making sure ALL_COMPILER_FEATURES is sorted. +list(SORT ALL_COMPILER_FEATURES) + +# Function to check whether the compiler supports the provided set of features. +# Usage: +# compiler_supports( +# +# +# ) +function(compiler_supports output_var features) + _intersection(var "${LIBC_CPU_FEATURES}" "${features}") + if("${var}" STREQUAL "${features}") + set(${output_var} TRUE PARENT_SCOPE) + else() + unset(${output_var} PARENT_SCOPE) + endif() +endfunction() + +# ------------------------------------------------------------------------------ +# Internal helpers and utilities. +# ------------------------------------------------------------------------------ + +# Computes the intersection between two lists. +function(_intersection output_var list1 list2) + foreach(element IN LISTS list1) + if("${list2}" MATCHES "(^|;)${element}(;|$)") + list(APPEND tmp "${element}") + endif() + endforeach() + set(${output_var} ${tmp} PARENT_SCOPE) +endfunction() + +set(AVAILABLE_COMPILER_FEATURES "") + +# Try compile a C file to check if flag is supported. +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) +foreach(feature IN LISTS ALL_COMPILER_FEATURES) + try_compile( + has_feature + ${CMAKE_CURRENT_BINARY_DIR}/compiler_features + SOURCES ${LIBC_SOURCE_DIR}/cmake/modules/compiler_features/check_${feature}.cpp + COMPILE_DEFINITIONS -I${LIBC_SOURCE_DIR} ${LIBC_COMPILE_OPTIONS_NATIVE} + ) + if(has_feature) + list(APPEND AVAILABLE_COMPILER_FEATURES ${feature}) + if(${feature} STREQUAL "float128") + set(LIBC_COMPILER_HAS_FLOAT128 TRUE) + endif() + endif() +endforeach() + +message(STATUS "Compiler features available: ${AVAILABLE_COMPILER_FEATURES}") diff --git a/libc/cmake/modules/compiler_features/check_float128.cpp b/libc/cmake/modules/compiler_features/check_float128.cpp new file mode 100644 index 0000000000000..1dcfe80da0a0e --- /dev/null +++ b/libc/cmake/modules/compiler_features/check_float128.cpp @@ -0,0 +1,5 @@ +#include "src/__support/macros/properties/compiler.h" + +#ifndef LIBC_COMPILER_HAS_FLOAT128 +#error unsupported +#endif diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index 31ea282edb591..83bc8461999ed 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -360,6 +360,13 @@ set(TARGET_LIBM_ENTRYPOINTS libc.src.math.truncl ) +if(LIBC_COMPILER_HAS_FLOAT128) + list(APPEND TARGET_LIBM_ENTRYPOINTS + # math.h C23 _Float128 entrypoints + libc.src.math.copysignf128 + ) +endif() + if(LLVM_LIBC_FULL_BUILD) list(APPEND TARGET_LIBC_ENTRYPOINTS # assert.h entrypoints diff --git a/libc/docs/math/index.rst b/libc/docs/math/index.rst index 7c83691c0d9c8..4af95aec60c0c 100644 --- a/libc/docs/math/index.rst +++ b/libc/docs/math/index.rst @@ -96,9 +96,9 @@ Implementation Status * To check math functions enabled for embedded system: - - `barebone-aarch32 `_ + - `baremetal-aarch32 `_ - - barebone-riscv32 - to be added + - baremetal-riscv32 - to be added Basic Operations ---------------- @@ -120,6 +120,8 @@ Basic Operations +--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+ | copysignl | |check| | |check| | | |check| | |check| | | | |check| | | | | | +--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+ +| copysignf128 | |check| | |check| | | | | | | | | | | | ++--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+ | fabs | |check| | |check| | |check| | |check| | |check| | | | |check| | |check| | | | | +--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+ | fabsf | |check| | |check| | |check| | |check| | |check| | | | |check| | |check| | | | | diff --git a/libc/spec/spec.td b/libc/spec/spec.td index ac0fba3bd0af7..981507a05412a 100644 --- a/libc/spec/spec.td +++ b/libc/spec/spec.td @@ -50,6 +50,9 @@ def DoubleType : NamedType<"double">; def LongDoubleType : NamedType<"long double">; def CharType : NamedType<"char">; +// TODO: Add compatibility layer to use C23 type _Float128 if possible. +def Float128Type : NamedType<"__float128"> + // Common types def VoidPtr : PtrType; def VoidPtrPtr : PtrType; diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td index 239d8f9843cfc..4e38cb742d284 100644 --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -358,6 +358,7 @@ def StdC : StandardSpec<"stdc"> { FunctionSpec<"copysign", RetValSpec, [ArgSpec, ArgSpec]>, FunctionSpec<"copysignf", RetValSpec, [ArgSpec, ArgSpec]>, FunctionSpec<"copysignl", RetValSpec, [ArgSpec, ArgSpec]>, + FunctionSpec<"copysignf128", RetValSpec, [ArgSpec, ArgSpec]>, FunctionSpec<"ceil", RetValSpec, [ArgSpec]>, FunctionSpec<"ceilf", RetValSpec, [ArgSpec]>, diff --git a/libc/src/__support/CPP/type_traits/is_floating_point.h b/libc/src/__support/CPP/type_traits/is_floating_point.h index 2df75a072c360..bcd2041029975 100644 --- a/libc/src/__support/CPP/type_traits/is_floating_point.h +++ b/libc/src/__support/CPP/type_traits/is_floating_point.h @@ -11,6 +11,7 @@ #include "src/__support/CPP/type_traits/is_same.h" #include "src/__support/CPP/type_traits/remove_cv.h" #include "src/__support/macros/attributes.h" +#include "src/__support/macros/properties/compiler.h" namespace LIBC_NAMESPACE::cpp { @@ -23,8 +24,13 @@ template struct is_floating_point { } public: +#if defined(LIBC_COMPILER_HAS_FLOAT128) + LIBC_INLINE_VAR static constexpr bool value = + __is_unqualified_any_of(); +#else LIBC_INLINE_VAR static constexpr bool value = __is_unqualified_any_of(); +#endif // LIBC_COMPILER_HAS_FLOAT128 }; template LIBC_INLINE_VAR constexpr bool is_floating_point_v = diff --git a/libc/src/__support/FPUtil/FloatProperties.h b/libc/src/__support/FPUtil/FloatProperties.h index c1d58a87101af..9a5f1cced9de9 100644 --- a/libc/src/__support/FPUtil/FloatProperties.h +++ b/libc/src/__support/FPUtil/FloatProperties.h @@ -175,6 +175,38 @@ template <> struct FloatProperties { }; #endif +#if (defined(LIBC_COMPILER_HAS_FLOAT128) && \ + !defined(LIBC_FLOAT128_IS_LONG_DOUBLE)) +// Properties for numbers represented in 128 bits long double on non x86 +// platform. +template <> struct FloatProperties { + typedef UInt128 BitsType; + static_assert(sizeof(BitsType) == sizeof(float128), + "Unexpected size of 'float128' type."); + + static constexpr uint32_t BIT_WIDTH = sizeof(BitsType) << 3; + + static constexpr uint32_t MANTISSA_WIDTH = 112; + static constexpr uint32_t MANTISSA_PRECISION = MANTISSA_WIDTH + 1; + static constexpr uint32_t EXPONENT_WIDTH = 15; + static constexpr BitsType MANTISSA_MASK = (BitsType(1) << MANTISSA_WIDTH) - 1; + static constexpr BitsType SIGN_MASK = BitsType(1) + << (EXPONENT_WIDTH + MANTISSA_WIDTH); + static constexpr BitsType EXPONENT_MASK = ~(SIGN_MASK | MANTISSA_MASK); + static constexpr uint32_t EXPONENT_BIAS = 16383; + + static constexpr BitsType EXP_MANT_MASK = MANTISSA_MASK | EXPONENT_MASK; + static_assert(EXP_MANT_MASK == ~SIGN_MASK, + "Exponent and mantissa masks are not as expected."); + + // If a number x is a NAN, then it is a quiet NAN if: + // QuietNaNMask & bits(x) != 0 + // Else, it is a signalling NAN. + static constexpr BitsType QUIET_NAN_MASK = BitsType(1) + << (MANTISSA_WIDTH - 1); +}; +#endif // LIBC_COMPILER_HAS_FLOAT128 + // Define the float type corresponding to the BitsType. template struct FloatType; diff --git a/libc/src/__support/macros/properties/compiler.h b/libc/src/__support/macros/properties/compiler.h index a7a2822bf6a14..3805d1f9a7a50 100644 --- a/libc/src/__support/macros/properties/compiler.h +++ b/libc/src/__support/macros/properties/compiler.h @@ -21,4 +21,25 @@ #define LIBC_COMPILER_IS_MSC #endif +// Check compiler features +#if defined(FLT128_MANT_DIG) +// C23 _Float128 type is available. +#define LIBC_COMPILER_HAS_FLOAT128 +#define LIBC_FLOAT128_IS_C23 +using float128 = _Float128; + +#elif defined(__SIZEOF_FLOAT128__) +// Builtin __float128 is available. +#define LIBC_COMPILER_HAS_FLOAT128 +#define LIBC_FLOAT128_IS_BUILTIN +using float128 = __float128; + +#elif (defined(__linux__) && defined(__aarch64__)) +// long double on Linux aarch64 is 128-bit floating point. +#define LIBC_COMPILER_HAS_FLOAT128 +#define LIBC_FLOAT128_IS_LONG_DOUBLE +using float128 = long double; + +#endif + #endif // LLVM_LIBC_SRC___SUPPORT_MACROS_PROPERTIES_COMPILER_H diff --git a/libc/src/math/CMakeLists.txt b/libc/src/math/CMakeLists.txt index f1f72714981a9..4aea54c7a4575 100644 --- a/libc/src/math/CMakeLists.txt +++ b/libc/src/math/CMakeLists.txt @@ -80,6 +80,7 @@ add_math_entrypoint_object(ceill) add_math_entrypoint_object(copysign) add_math_entrypoint_object(copysignf) add_math_entrypoint_object(copysignl) +add_math_entrypoint_object(copysignf128) add_math_entrypoint_object(cos) add_math_entrypoint_object(cosf) diff --git a/libc/src/math/copysignf128.h b/libc/src/math/copysignf128.h new file mode 100644 index 0000000000000..9448d8205dd75 --- /dev/null +++ b/libc/src/math/copysignf128.h @@ -0,0 +1,20 @@ +//===-- Implementation header for copysignf128 ------------------*- 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_MATH_COPYSIGNF128_H +#define LLVM_LIBC_SRC_MATH_COPYSIGNF128_H + +#include "src/__support/macros/properties/compiler.h" + +namespace LIBC_NAMESPACE { + +float128 copysignf128(float128 x, float128 y); + +} // namespace LIBC_NAMESPACE + +#endif // LLVM_LIBC_SRC_MATH_COPYSIGN_H diff --git a/libc/src/math/generic/CMakeLists.txt b/libc/src/math/generic/CMakeLists.txt index 693887c165ce9..7ac365f55bb6f 100644 --- a/libc/src/math/generic/CMakeLists.txt +++ b/libc/src/math/generic/CMakeLists.txt @@ -806,7 +806,7 @@ add_entrypoint_object( DEPENDS libc.src.__support.FPUtil.manipulation_functions COMPILE_OPTIONS - -O2 + -O3 ) add_entrypoint_object( @@ -818,7 +818,7 @@ add_entrypoint_object( DEPENDS libc.src.__support.FPUtil.manipulation_functions COMPILE_OPTIONS - -O2 + -O3 ) add_entrypoint_object( @@ -830,7 +830,19 @@ add_entrypoint_object( DEPENDS libc.src.__support.FPUtil.manipulation_functions COMPILE_OPTIONS - -O2 + -O3 +) + +add_entrypoint_object( + copysignf128 + SRCS + copysignf128.cpp + HDRS + ../copysignf128.h + DEPENDS + libc.src.__support.FPUtil.manipulation_functions + COMPILE_OPTIONS + -O3 ) add_entrypoint_object( diff --git a/libc/src/math/generic/copysignf128.cpp b/libc/src/math/generic/copysignf128.cpp new file mode 100644 index 0000000000000..07e5caa223a53 --- /dev/null +++ b/libc/src/math/generic/copysignf128.cpp @@ -0,0 +1,20 @@ +//===-- Implementation of copysignf128 function ---------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/math/copysignf128.h" +#include "src/__support/FPUtil/ManipulationFunctions.h" +#include "src/__support/common.h" +#include "src/__support/macros/properties/compiler.h" + +namespace LIBC_NAMESPACE { + +LLVM_LIBC_FUNCTION(float128, copysignf128, (float128 x, float128 y)) { + return fputil::copysign(x, y); +} + +} // namespace LIBC_NAMESPACE diff --git a/libc/test/src/__support/FPUtil/fpbits_test.cpp b/libc/test/src/__support/FPUtil/fpbits_test.cpp index 5c97ba3a70ca7..027db8807ab22 100644 --- a/libc/test/src/__support/FPUtil/fpbits_test.cpp +++ b/libc/test/src/__support/FPUtil/fpbits_test.cpp @@ -287,3 +287,75 @@ TEST(LlvmLibcFPBitsTest, LongDoubleType) { #endif } #endif + +#if defined(LIBC_COMPILER_HAS_FLOAT128) +TEST(LlvmLibcFPBitsTest, Float128Type) { + using Float128Bits = FPBits; + + EXPECT_STREQ(LIBC_NAMESPACE::str(Float128Bits(Float128Bits::inf())).c_str(), + "(+Infinity)"); + EXPECT_STREQ( + LIBC_NAMESPACE::str(Float128Bits(Float128Bits::neg_inf())).c_str(), + "(-Infinity)"); + EXPECT_STREQ( + LIBC_NAMESPACE::str(Float128Bits(Float128Bits::build_nan(1))).c_str(), + "(NaN)"); + + Float128Bits zero(Float128Bits::zero()); + EXPECT_EQ(zero.get_sign(), false); + EXPECT_EQ(zero.get_unbiased_exponent(), static_cast(0x0000)); + EXPECT_EQ(zero.get_mantissa(), static_cast(0x0000000000000000) + << 64); + EXPECT_EQ(zero.uintval(), static_cast(0x0000000000000000) << 64); + EXPECT_STREQ(LIBC_NAMESPACE::str(zero).c_str(), + "0x00000000000000000000000000000000 = " + "(S: 0, E: 0x0000, M: 0x00000000000000000000000000000000)"); + + Float128Bits negzero(Float128Bits::neg_zero()); + EXPECT_EQ(negzero.get_sign(), true); + EXPECT_EQ(negzero.get_unbiased_exponent(), static_cast(0x0000)); + EXPECT_EQ(negzero.get_mantissa(), static_cast(0x0000000000000000) + << 64); + EXPECT_EQ(negzero.uintval(), static_cast(0x1) << 127); + EXPECT_STREQ(LIBC_NAMESPACE::str(negzero).c_str(), + "0x80000000000000000000000000000000 = " + "(S: 1, E: 0x0000, M: 0x00000000000000000000000000000000)"); + + Float128Bits one(float128(1.0)); + EXPECT_EQ(one.get_sign(), false); + EXPECT_EQ(one.get_unbiased_exponent(), static_cast(0x3FFF)); + EXPECT_EQ(one.get_mantissa(), static_cast(0x0000000000000000) << 64); + EXPECT_EQ(one.uintval(), static_cast(0x3FFF) << 112); + EXPECT_STREQ(LIBC_NAMESPACE::str(one).c_str(), + "0x3FFF0000000000000000000000000000 = " + "(S: 0, E: 0x3FFF, M: 0x00000000000000000000000000000000)"); + + Float128Bits negone(float128(-1.0)); + EXPECT_EQ(negone.get_sign(), true); + EXPECT_EQ(negone.get_unbiased_exponent(), static_cast(0x3FFF)); + EXPECT_EQ(negone.get_mantissa(), static_cast(0x0000000000000000) + << 64); + EXPECT_EQ(negone.uintval(), static_cast(0xBFFF) << 112); + EXPECT_STREQ(LIBC_NAMESPACE::str(negone).c_str(), + "0xBFFF0000000000000000000000000000 = " + "(S: 1, E: 0x3FFF, M: 0x00000000000000000000000000000000)"); + + Float128Bits num(float128(1.125)); + EXPECT_EQ(num.get_sign(), false); + EXPECT_EQ(num.get_unbiased_exponent(), static_cast(0x3FFF)); + EXPECT_EQ(num.get_mantissa(), static_cast(0x2) << 108); + EXPECT_EQ(num.uintval(), static_cast(0x3FFF2) << 108); + EXPECT_STREQ(LIBC_NAMESPACE::str(num).c_str(), + "0x3FFF2000000000000000000000000000 = " + "(S: 0, E: 0x3FFF, M: 0x00002000000000000000000000000000)"); + + Float128Bits negnum(float128(-1.125)); + EXPECT_EQ(negnum.get_sign(), true); + EXPECT_EQ(negnum.get_unbiased_exponent(), static_cast(0x3FFF)); + EXPECT_EQ(negnum.get_mantissa(), static_cast(0x2) << 108); + EXPECT_EQ(negnum.uintval(), static_cast(0xBFFF2) << 108); + EXPECT_STREQ(LIBC_NAMESPACE::str(negnum).c_str(), + "0xBFFF2000000000000000000000000000 = " + "(S: 1, E: 0x3FFF, M: 0x00002000000000000000000000000000)"); +} +#endif // LIBC_COMPILER_HAS_FLOAT128 diff --git a/libc/test/src/math/smoke/CMakeLists.txt b/libc/test/src/math/smoke/CMakeLists.txt index 995dee7b0b548..101bdac995355 100644 --- a/libc/test/src/math/smoke/CMakeLists.txt +++ b/libc/test/src/math/smoke/CMakeLists.txt @@ -674,6 +674,22 @@ add_fp_unittest( UNIT_TEST_ONLY ) +add_fp_unittest( + copysignf128_test + SUITE + libc-math-smoke-tests + SRCS + copysignf128_test.cpp + HDRS + CopySignTest.h + DEPENDS + libc.include.math + libc.src.math.copysignf128 + libc.src.__support.FPUtil.fp_bits + # FIXME: Currently fails on the GPU build. + UNIT_TEST_ONLY +) + add_fp_unittest( frexp_test SUITE diff --git a/libc/test/src/math/smoke/CopySignTest.h b/libc/test/src/math/smoke/CopySignTest.h index c33f78038a3cf..5e748bb8fd8ed 100644 --- a/libc/test/src/math/smoke/CopySignTest.h +++ b/libc/test/src/math/smoke/CopySignTest.h @@ -20,28 +20,29 @@ class CopySignTest : public LIBC_NAMESPACE::testing::Test { typedef T (*CopySignFunc)(T, T); void testSpecialNumbers(CopySignFunc func) { - EXPECT_FP_EQ(aNaN, func(aNaN, -1.0)); - EXPECT_FP_EQ(aNaN, func(aNaN, 1.0)); + EXPECT_FP_EQ(aNaN, func(aNaN, T(-1.0))); + EXPECT_FP_EQ(aNaN, func(aNaN, T(1.0))); - EXPECT_FP_EQ(neg_inf, func(inf, -1.0)); - EXPECT_FP_EQ(inf, func(neg_inf, 1.0)); + EXPECT_FP_EQ(neg_inf, func(inf, T(-1.0))); + EXPECT_FP_EQ(inf, func(neg_inf, T(1.0))); - EXPECT_FP_EQ(neg_zero, func(zero, -1.0)); - EXPECT_FP_EQ(zero, func(neg_zero, 1.0)); + EXPECT_FP_EQ(neg_zero, func(zero, T(-1.0))); + EXPECT_FP_EQ(zero, func(neg_zero, T(1.0))); } void testRange(CopySignFunc func) { constexpr UIntType COUNT = 100'000; constexpr UIntType STEP = UIntType(-1) / COUNT; for (UIntType i = 0, v = 0; i <= COUNT; ++i, v += STEP) { - T x = T(FPBits(v)); - if (isnan(x) || isinf(x)) + FPBits x_bits = FPBits(v); + T x = T(v); + if (x_bits.is_nan() || x_bits.is_inf()) continue; - double res1 = func(x, -x); + T res1 = func(x, -x); ASSERT_FP_EQ(res1, -x); - double res2 = func(x, x); + T res2 = func(x, x); ASSERT_FP_EQ(res2, x); } } diff --git a/libc/test/src/math/smoke/copysignf128_test.cpp b/libc/test/src/math/smoke/copysignf128_test.cpp new file mode 100644 index 0000000000000..48b41deb71d53 --- /dev/null +++ b/libc/test/src/math/smoke/copysignf128_test.cpp @@ -0,0 +1,13 @@ +//===-- Unittests for copysignf128 ----------------------------------------===// +// +// 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 "CopySignTest.h" + +#include "src/math/copysignf128.h" + +LIST_COPYSIGN_TESTS(float128, LIBC_NAMESPACE::copysignf128) diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel index d9252208e9b8d..486146187d3a6 100644 --- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel @@ -308,6 +308,7 @@ libc_support_library( deps = [ ":__support_macros_attributes", ":__support_macros_config", + ":__support_macros_properties_compiler", ], ) @@ -1821,6 +1822,8 @@ libc_math_function(name = "copysignf") libc_math_function(name = "copysignl") +libc_math_function(name = "copysignf128") + libc_math_function(name = "ilogb") libc_math_function(name = "ilogbf")