Skip to content

Commit 3f906f5

Browse files
authored
[libc][math] Add initial support for C23 float128 math functions, starting with copysignf128. (llvm#71731)
1 parent 77d75dc commit 3f906f5

File tree

19 files changed

+310
-15
lines changed

19 files changed

+310
-15
lines changed

libc/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ option(LIBC_INCLUDE_DOCS "Build the libc documentation." ${LLVM_INCLUDE_DOCS})
245245

246246
include(CMakeParseArguments)
247247
include(LLVMLibCCheckCpuFeatures)
248+
include(CheckCompilerFeatures)
248249
include(LLVMLibCRules)
249250

250251
if(EXISTS "${LIBC_SOURCE_DIR}/config/${LIBC_TARGET_OS}/${LIBC_TARGET_ARCHITECTURE}/entrypoints.txt")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# ------------------------------------------------------------------------------
2+
# Compiler features definition and flags
3+
# ------------------------------------------------------------------------------
4+
5+
# Initialize ALL_COMPILER_FEATURES as empty list.
6+
set(ALL_COMPILER_FEATURES "float128")
7+
8+
# Making sure ALL_COMPILER_FEATURES is sorted.
9+
list(SORT ALL_COMPILER_FEATURES)
10+
11+
# Function to check whether the compiler supports the provided set of features.
12+
# Usage:
13+
# compiler_supports(
14+
# <output variable>
15+
# <list of cpu features>
16+
# )
17+
function(compiler_supports output_var features)
18+
_intersection(var "${LIBC_CPU_FEATURES}" "${features}")
19+
if("${var}" STREQUAL "${features}")
20+
set(${output_var} TRUE PARENT_SCOPE)
21+
else()
22+
unset(${output_var} PARENT_SCOPE)
23+
endif()
24+
endfunction()
25+
26+
# ------------------------------------------------------------------------------
27+
# Internal helpers and utilities.
28+
# ------------------------------------------------------------------------------
29+
30+
# Computes the intersection between two lists.
31+
function(_intersection output_var list1 list2)
32+
foreach(element IN LISTS list1)
33+
if("${list2}" MATCHES "(^|;)${element}(;|$)")
34+
list(APPEND tmp "${element}")
35+
endif()
36+
endforeach()
37+
set(${output_var} ${tmp} PARENT_SCOPE)
38+
endfunction()
39+
40+
set(AVAILABLE_COMPILER_FEATURES "")
41+
42+
# Try compile a C file to check if flag is supported.
43+
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
44+
foreach(feature IN LISTS ALL_COMPILER_FEATURES)
45+
try_compile(
46+
has_feature
47+
${CMAKE_CURRENT_BINARY_DIR}/compiler_features
48+
SOURCES ${LIBC_SOURCE_DIR}/cmake/modules/compiler_features/check_${feature}.cpp
49+
COMPILE_DEFINITIONS -I${LIBC_SOURCE_DIR} ${LIBC_COMPILE_OPTIONS_NATIVE}
50+
)
51+
if(has_feature)
52+
list(APPEND AVAILABLE_COMPILER_FEATURES ${feature})
53+
if(${feature} STREQUAL "float128")
54+
set(LIBC_COMPILER_HAS_FLOAT128 TRUE)
55+
endif()
56+
endif()
57+
endforeach()
58+
59+
message(STATUS "Compiler features available: ${AVAILABLE_COMPILER_FEATURES}")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#include "src/__support/macros/properties/compiler.h"
2+
3+
#ifndef LIBC_COMPILER_HAS_FLOAT128
4+
#error unsupported
5+
#endif

libc/config/linux/x86_64/entrypoints.txt

+7
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,13 @@ set(TARGET_LIBM_ENTRYPOINTS
360360
libc.src.math.truncl
361361
)
362362

363+
if(LIBC_COMPILER_HAS_FLOAT128)
364+
list(APPEND TARGET_LIBM_ENTRYPOINTS
365+
# math.h C23 _Float128 entrypoints
366+
libc.src.math.copysignf128
367+
)
368+
endif()
369+
363370
if(LLVM_LIBC_FULL_BUILD)
364371
list(APPEND TARGET_LIBC_ENTRYPOINTS
365372
# assert.h entrypoints

libc/docs/math/index.rst

+4-2
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,9 @@ Implementation Status
9696

9797
* To check math functions enabled for embedded system:
9898

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

101-
- barebone-riscv32 - to be added
101+
- baremetal-riscv32 - to be added
102102

103103
Basic Operations
104104
----------------
@@ -120,6 +120,8 @@ Basic Operations
120120
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
121121
| copysignl | |check| | |check| | | |check| | |check| | | | |check| | | | | |
122122
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
123+
| copysignf128 | |check| | |check| | | | | | | | | | | |
124+
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
123125
| fabs | |check| | |check| | |check| | |check| | |check| | | | |check| | |check| | | | |
124126
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
125127
| fabsf | |check| | |check| | |check| | |check| | |check| | | | |check| | |check| | | | |

libc/spec/spec.td

+3
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ def DoubleType : NamedType<"double">;
5050
def LongDoubleType : NamedType<"long double">;
5151
def CharType : NamedType<"char">;
5252

53+
// TODO: Add compatibility layer to use C23 type _Float128 if possible.
54+
def Float128Type : NamedType<"__float128">
55+
5356
// Common types
5457
def VoidPtr : PtrType<VoidType>;
5558
def VoidPtrPtr : PtrType<VoidPtr>;

libc/spec/stdc.td

+1
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,7 @@ def StdC : StandardSpec<"stdc"> {
358358
FunctionSpec<"copysign", RetValSpec<DoubleType>, [ArgSpec<DoubleType>, ArgSpec<DoubleType>]>,
359359
FunctionSpec<"copysignf", RetValSpec<FloatType>, [ArgSpec<FloatType>, ArgSpec<FloatType>]>,
360360
FunctionSpec<"copysignl", RetValSpec<LongDoubleType>, [ArgSpec<LongDoubleType>, ArgSpec<LongDoubleType>]>,
361+
FunctionSpec<"copysignf128", RetValSpec<Float128Type>, [ArgSpec<Float128Type>, ArgSpec<Float128Type>]>,
361362

362363
FunctionSpec<"ceil", RetValSpec<DoubleType>, [ArgSpec<DoubleType>]>,
363364
FunctionSpec<"ceilf", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,

libc/src/__support/CPP/type_traits/is_floating_point.h

+6
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "src/__support/CPP/type_traits/is_same.h"
1212
#include "src/__support/CPP/type_traits/remove_cv.h"
1313
#include "src/__support/macros/attributes.h"
14+
#include "src/__support/macros/properties/compiler.h"
1415

1516
namespace LIBC_NAMESPACE::cpp {
1617

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

2526
public:
27+
#if defined(LIBC_COMPILER_HAS_FLOAT128)
28+
LIBC_INLINE_VAR static constexpr bool value =
29+
__is_unqualified_any_of<T, float, double, long double, float128>();
30+
#else
2631
LIBC_INLINE_VAR static constexpr bool value =
2732
__is_unqualified_any_of<T, float, double, long double>();
33+
#endif // LIBC_COMPILER_HAS_FLOAT128
2834
};
2935
template <typename T>
3036
LIBC_INLINE_VAR constexpr bool is_floating_point_v =

libc/src/__support/FPUtil/FloatProperties.h

+32
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,38 @@ template <> struct FloatProperties<long double> {
175175
};
176176
#endif
177177

178+
#if (defined(LIBC_COMPILER_HAS_FLOAT128) && \
179+
!defined(LIBC_FLOAT128_IS_LONG_DOUBLE))
180+
// Properties for numbers represented in 128 bits long double on non x86
181+
// platform.
182+
template <> struct FloatProperties<float128> {
183+
typedef UInt128 BitsType;
184+
static_assert(sizeof(BitsType) == sizeof(float128),
185+
"Unexpected size of 'float128' type.");
186+
187+
static constexpr uint32_t BIT_WIDTH = sizeof(BitsType) << 3;
188+
189+
static constexpr uint32_t MANTISSA_WIDTH = 112;
190+
static constexpr uint32_t MANTISSA_PRECISION = MANTISSA_WIDTH + 1;
191+
static constexpr uint32_t EXPONENT_WIDTH = 15;
192+
static constexpr BitsType MANTISSA_MASK = (BitsType(1) << MANTISSA_WIDTH) - 1;
193+
static constexpr BitsType SIGN_MASK = BitsType(1)
194+
<< (EXPONENT_WIDTH + MANTISSA_WIDTH);
195+
static constexpr BitsType EXPONENT_MASK = ~(SIGN_MASK | MANTISSA_MASK);
196+
static constexpr uint32_t EXPONENT_BIAS = 16383;
197+
198+
static constexpr BitsType EXP_MANT_MASK = MANTISSA_MASK | EXPONENT_MASK;
199+
static_assert(EXP_MANT_MASK == ~SIGN_MASK,
200+
"Exponent and mantissa masks are not as expected.");
201+
202+
// If a number x is a NAN, then it is a quiet NAN if:
203+
// QuietNaNMask & bits(x) != 0
204+
// Else, it is a signalling NAN.
205+
static constexpr BitsType QUIET_NAN_MASK = BitsType(1)
206+
<< (MANTISSA_WIDTH - 1);
207+
};
208+
#endif // LIBC_COMPILER_HAS_FLOAT128
209+
178210
// Define the float type corresponding to the BitsType.
179211
template <typename BitsType> struct FloatType;
180212

libc/src/__support/macros/properties/compiler.h

+21
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,25 @@
2121
#define LIBC_COMPILER_IS_MSC
2222
#endif
2323

24+
// Check compiler features
25+
#if defined(FLT128_MANT_DIG)
26+
// C23 _Float128 type is available.
27+
#define LIBC_COMPILER_HAS_FLOAT128
28+
#define LIBC_FLOAT128_IS_C23
29+
using float128 = _Float128;
30+
31+
#elif defined(__SIZEOF_FLOAT128__)
32+
// Builtin __float128 is available.
33+
#define LIBC_COMPILER_HAS_FLOAT128
34+
#define LIBC_FLOAT128_IS_BUILTIN
35+
using float128 = __float128;
36+
37+
#elif (defined(__linux__) && defined(__aarch64__))
38+
// long double on Linux aarch64 is 128-bit floating point.
39+
#define LIBC_COMPILER_HAS_FLOAT128
40+
#define LIBC_FLOAT128_IS_LONG_DOUBLE
41+
using float128 = long double;
42+
43+
#endif
44+
2445
#endif // LLVM_LIBC_SRC___SUPPORT_MACROS_PROPERTIES_COMPILER_H

libc/src/math/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ add_math_entrypoint_object(ceill)
8080
add_math_entrypoint_object(copysign)
8181
add_math_entrypoint_object(copysignf)
8282
add_math_entrypoint_object(copysignl)
83+
add_math_entrypoint_object(copysignf128)
8384

8485
add_math_entrypoint_object(cos)
8586
add_math_entrypoint_object(cosf)

libc/src/math/copysignf128.h

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//===-- Implementation header for copysignf128 ------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC_MATH_COPYSIGNF128_H
10+
#define LLVM_LIBC_SRC_MATH_COPYSIGNF128_H
11+
12+
#include "src/__support/macros/properties/compiler.h"
13+
14+
namespace LIBC_NAMESPACE {
15+
16+
float128 copysignf128(float128 x, float128 y);
17+
18+
} // namespace LIBC_NAMESPACE
19+
20+
#endif // LLVM_LIBC_SRC_MATH_COPYSIGN_H

libc/src/math/generic/CMakeLists.txt

+15-3
Original file line numberDiff line numberDiff line change
@@ -806,7 +806,7 @@ add_entrypoint_object(
806806
DEPENDS
807807
libc.src.__support.FPUtil.manipulation_functions
808808
COMPILE_OPTIONS
809-
-O2
809+
-O3
810810
)
811811

812812
add_entrypoint_object(
@@ -818,7 +818,7 @@ add_entrypoint_object(
818818
DEPENDS
819819
libc.src.__support.FPUtil.manipulation_functions
820820
COMPILE_OPTIONS
821-
-O2
821+
-O3
822822
)
823823

824824
add_entrypoint_object(
@@ -830,7 +830,19 @@ add_entrypoint_object(
830830
DEPENDS
831831
libc.src.__support.FPUtil.manipulation_functions
832832
COMPILE_OPTIONS
833-
-O2
833+
-O3
834+
)
835+
836+
add_entrypoint_object(
837+
copysignf128
838+
SRCS
839+
copysignf128.cpp
840+
HDRS
841+
../copysignf128.h
842+
DEPENDS
843+
libc.src.__support.FPUtil.manipulation_functions
844+
COMPILE_OPTIONS
845+
-O3
834846
)
835847

836848
add_entrypoint_object(
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//===-- Implementation of copysignf128 function ---------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "src/math/copysignf128.h"
10+
#include "src/__support/FPUtil/ManipulationFunctions.h"
11+
#include "src/__support/common.h"
12+
#include "src/__support/macros/properties/compiler.h"
13+
14+
namespace LIBC_NAMESPACE {
15+
16+
LLVM_LIBC_FUNCTION(float128, copysignf128, (float128 x, float128 y)) {
17+
return fputil::copysign(x, y);
18+
}
19+
20+
} // namespace LIBC_NAMESPACE

libc/test/src/__support/FPUtil/fpbits_test.cpp

+72
Original file line numberDiff line numberDiff line change
@@ -287,3 +287,75 @@ TEST(LlvmLibcFPBitsTest, LongDoubleType) {
287287
#endif
288288
}
289289
#endif
290+
291+
#if defined(LIBC_COMPILER_HAS_FLOAT128)
292+
TEST(LlvmLibcFPBitsTest, Float128Type) {
293+
using Float128Bits = FPBits<float128>;
294+
295+
EXPECT_STREQ(LIBC_NAMESPACE::str(Float128Bits(Float128Bits::inf())).c_str(),
296+
"(+Infinity)");
297+
EXPECT_STREQ(
298+
LIBC_NAMESPACE::str(Float128Bits(Float128Bits::neg_inf())).c_str(),
299+
"(-Infinity)");
300+
EXPECT_STREQ(
301+
LIBC_NAMESPACE::str(Float128Bits(Float128Bits::build_nan(1))).c_str(),
302+
"(NaN)");
303+
304+
Float128Bits zero(Float128Bits::zero());
305+
EXPECT_EQ(zero.get_sign(), false);
306+
EXPECT_EQ(zero.get_unbiased_exponent(), static_cast<uint16_t>(0x0000));
307+
EXPECT_EQ(zero.get_mantissa(), static_cast<UInt128>(0x0000000000000000)
308+
<< 64);
309+
EXPECT_EQ(zero.uintval(), static_cast<UInt128>(0x0000000000000000) << 64);
310+
EXPECT_STREQ(LIBC_NAMESPACE::str(zero).c_str(),
311+
"0x00000000000000000000000000000000 = "
312+
"(S: 0, E: 0x0000, M: 0x00000000000000000000000000000000)");
313+
314+
Float128Bits negzero(Float128Bits::neg_zero());
315+
EXPECT_EQ(negzero.get_sign(), true);
316+
EXPECT_EQ(negzero.get_unbiased_exponent(), static_cast<uint16_t>(0x0000));
317+
EXPECT_EQ(negzero.get_mantissa(), static_cast<UInt128>(0x0000000000000000)
318+
<< 64);
319+
EXPECT_EQ(negzero.uintval(), static_cast<UInt128>(0x1) << 127);
320+
EXPECT_STREQ(LIBC_NAMESPACE::str(negzero).c_str(),
321+
"0x80000000000000000000000000000000 = "
322+
"(S: 1, E: 0x0000, M: 0x00000000000000000000000000000000)");
323+
324+
Float128Bits one(float128(1.0));
325+
EXPECT_EQ(one.get_sign(), false);
326+
EXPECT_EQ(one.get_unbiased_exponent(), static_cast<uint16_t>(0x3FFF));
327+
EXPECT_EQ(one.get_mantissa(), static_cast<UInt128>(0x0000000000000000) << 64);
328+
EXPECT_EQ(one.uintval(), static_cast<UInt128>(0x3FFF) << 112);
329+
EXPECT_STREQ(LIBC_NAMESPACE::str(one).c_str(),
330+
"0x3FFF0000000000000000000000000000 = "
331+
"(S: 0, E: 0x3FFF, M: 0x00000000000000000000000000000000)");
332+
333+
Float128Bits negone(float128(-1.0));
334+
EXPECT_EQ(negone.get_sign(), true);
335+
EXPECT_EQ(negone.get_unbiased_exponent(), static_cast<uint16_t>(0x3FFF));
336+
EXPECT_EQ(negone.get_mantissa(), static_cast<UInt128>(0x0000000000000000)
337+
<< 64);
338+
EXPECT_EQ(negone.uintval(), static_cast<UInt128>(0xBFFF) << 112);
339+
EXPECT_STREQ(LIBC_NAMESPACE::str(negone).c_str(),
340+
"0xBFFF0000000000000000000000000000 = "
341+
"(S: 1, E: 0x3FFF, M: 0x00000000000000000000000000000000)");
342+
343+
Float128Bits num(float128(1.125));
344+
EXPECT_EQ(num.get_sign(), false);
345+
EXPECT_EQ(num.get_unbiased_exponent(), static_cast<uint16_t>(0x3FFF));
346+
EXPECT_EQ(num.get_mantissa(), static_cast<UInt128>(0x2) << 108);
347+
EXPECT_EQ(num.uintval(), static_cast<UInt128>(0x3FFF2) << 108);
348+
EXPECT_STREQ(LIBC_NAMESPACE::str(num).c_str(),
349+
"0x3FFF2000000000000000000000000000 = "
350+
"(S: 0, E: 0x3FFF, M: 0x00002000000000000000000000000000)");
351+
352+
Float128Bits negnum(float128(-1.125));
353+
EXPECT_EQ(negnum.get_sign(), true);
354+
EXPECT_EQ(negnum.get_unbiased_exponent(), static_cast<uint16_t>(0x3FFF));
355+
EXPECT_EQ(negnum.get_mantissa(), static_cast<UInt128>(0x2) << 108);
356+
EXPECT_EQ(negnum.uintval(), static_cast<UInt128>(0xBFFF2) << 108);
357+
EXPECT_STREQ(LIBC_NAMESPACE::str(negnum).c_str(),
358+
"0xBFFF2000000000000000000000000000 = "
359+
"(S: 1, E: 0x3FFF, M: 0x00002000000000000000000000000000)");
360+
}
361+
#endif // LIBC_COMPILER_HAS_FLOAT128

0 commit comments

Comments
 (0)