Skip to content

[libc][math] Add initial support for C23 float128 math functions, starting with copysignf128. #71731

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions libc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
59 changes: 59 additions & 0 deletions libc/cmake/modules/CheckCompilerFeatures.cmake
Original file line number Diff line number Diff line change
@@ -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(
# <output variable>
# <list of cpu features>
# )
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}")
5 changes: 5 additions & 0 deletions libc/cmake/modules/compiler_features/check_float128.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#include "src/__support/macros/properties/compiler.h"

#ifndef LIBC_COMPILER_HAS_FLOAT128
#error unsupported
#endif
7 changes: 7 additions & 0 deletions libc/config/linux/x86_64/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 4 additions & 2 deletions libc/docs/math/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,9 @@ Implementation Status

* To check math functions enabled for embedded system:

- `barebone-aarch32 <https://github.com/llvm/llvm-project/tree/main/libc/config/baremetal/arm/entrypoints.txt>`_
- `baremetal-aarch32 <https://github.com/llvm/llvm-project/tree/main/libc/config/baremetal/arm/entrypoints.txt>`_

- barebone-riscv32 - to be added
- baremetal-riscv32 - to be added

Basic Operations
----------------
Expand All @@ -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| | | | |
Expand Down
3 changes: 3 additions & 0 deletions libc/spec/spec.td
Original file line number Diff line number Diff line change
Expand Up @@ -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<VoidType>;
def VoidPtrPtr : PtrType<VoidPtr>;
Expand Down
1 change: 1 addition & 0 deletions libc/spec/stdc.td
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,7 @@ def StdC : StandardSpec<"stdc"> {
FunctionSpec<"copysign", RetValSpec<DoubleType>, [ArgSpec<DoubleType>, ArgSpec<DoubleType>]>,
FunctionSpec<"copysignf", RetValSpec<FloatType>, [ArgSpec<FloatType>, ArgSpec<FloatType>]>,
FunctionSpec<"copysignl", RetValSpec<LongDoubleType>, [ArgSpec<LongDoubleType>, ArgSpec<LongDoubleType>]>,
FunctionSpec<"copysignf128", RetValSpec<Float128Type>, [ArgSpec<Float128Type>, ArgSpec<Float128Type>]>,

FunctionSpec<"ceil", RetValSpec<DoubleType>, [ArgSpec<DoubleType>]>,
FunctionSpec<"ceilf", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,
Expand Down
6 changes: 6 additions & 0 deletions libc/src/__support/CPP/type_traits/is_floating_point.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand All @@ -23,8 +24,13 @@ template <typename T> struct is_floating_point {
}

public:
#if defined(LIBC_COMPILER_HAS_FLOAT128)
LIBC_INLINE_VAR static constexpr bool value =
__is_unqualified_any_of<T, float, double, long double, float128>();
#else
LIBC_INLINE_VAR static constexpr bool value =
__is_unqualified_any_of<T, float, double, long double>();
#endif // LIBC_COMPILER_HAS_FLOAT128
};
template <typename T>
LIBC_INLINE_VAR constexpr bool is_floating_point_v =
Expand Down
32 changes: 32 additions & 0 deletions libc/src/__support/FPUtil/FloatProperties.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,38 @@ template <> struct FloatProperties<long double> {
};
#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<float128> {
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 <typename BitsType> struct FloatType;

Expand Down
21 changes: 21 additions & 0 deletions libc/src/__support/macros/properties/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
1 change: 1 addition & 0 deletions libc/src/math/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
20 changes: 20 additions & 0 deletions libc/src/math/copysignf128.h
Original file line number Diff line number Diff line change
@@ -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
18 changes: 15 additions & 3 deletions libc/src/math/generic/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -806,7 +806,7 @@ add_entrypoint_object(
DEPENDS
libc.src.__support.FPUtil.manipulation_functions
COMPILE_OPTIONS
-O2
-O3
)

add_entrypoint_object(
Expand All @@ -818,7 +818,7 @@ add_entrypoint_object(
DEPENDS
libc.src.__support.FPUtil.manipulation_functions
COMPILE_OPTIONS
-O2
-O3
)

add_entrypoint_object(
Expand All @@ -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(
Expand Down
20 changes: 20 additions & 0 deletions libc/src/math/generic/copysignf128.cpp
Original file line number Diff line number Diff line change
@@ -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
72 changes: 72 additions & 0 deletions libc/test/src/__support/FPUtil/fpbits_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -287,3 +287,75 @@ TEST(LlvmLibcFPBitsTest, LongDoubleType) {
#endif
}
#endif

#if defined(LIBC_COMPILER_HAS_FLOAT128)
TEST(LlvmLibcFPBitsTest, Float128Type) {
using Float128Bits = FPBits<float128>;

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<uint16_t>(0x0000));
EXPECT_EQ(zero.get_mantissa(), static_cast<UInt128>(0x0000000000000000)
<< 64);
EXPECT_EQ(zero.uintval(), static_cast<UInt128>(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<uint16_t>(0x0000));
EXPECT_EQ(negzero.get_mantissa(), static_cast<UInt128>(0x0000000000000000)
<< 64);
EXPECT_EQ(negzero.uintval(), static_cast<UInt128>(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<uint16_t>(0x3FFF));
EXPECT_EQ(one.get_mantissa(), static_cast<UInt128>(0x0000000000000000) << 64);
EXPECT_EQ(one.uintval(), static_cast<UInt128>(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<uint16_t>(0x3FFF));
EXPECT_EQ(negone.get_mantissa(), static_cast<UInt128>(0x0000000000000000)
<< 64);
EXPECT_EQ(negone.uintval(), static_cast<UInt128>(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<uint16_t>(0x3FFF));
EXPECT_EQ(num.get_mantissa(), static_cast<UInt128>(0x2) << 108);
EXPECT_EQ(num.uintval(), static_cast<UInt128>(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<uint16_t>(0x3FFF));
EXPECT_EQ(negnum.get_mantissa(), static_cast<UInt128>(0x2) << 108);
EXPECT_EQ(negnum.uintval(), static_cast<UInt128>(0xBFFF2) << 108);
EXPECT_STREQ(LIBC_NAMESPACE::str(negnum).c_str(),
"0xBFFF2000000000000000000000000000 = "
"(S: 1, E: 0x3FFF, M: 0x00002000000000000000000000000000)");
}
#endif // LIBC_COMPILER_HAS_FLOAT128
Loading