Skip to content

Commit

Permalink
[libc][math] Add initial support for C23 float128 math functions, sta…
Browse files Browse the repository at this point in the history
…rting with copysignf128. (llvm#71731)
  • Loading branch information
lntue committed Nov 10, 2023
1 parent 77d75dc commit 3f906f5
Show file tree
Hide file tree
Showing 19 changed files with 310 additions and 15 deletions.
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

0 comments on commit 3f906f5

Please sign in to comment.