Skip to content

Commit f5e03f4

Browse files
committed
[libc][math][c23] Add f16addf C23 math function
1 parent b6a94b6 commit f5e03f4

File tree

18 files changed

+546
-1
lines changed

18 files changed

+546
-1
lines changed

libc/config/linux/aarch64/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
506506
libc.src.math.canonicalizef16
507507
libc.src.math.ceilf16
508508
libc.src.math.copysignf16
509+
libc.src.math.f16addf
509510
libc.src.math.f16divf
510511
libc.src.math.f16fmaf
511512
libc.src.math.f16sqrtf

libc/config/linux/x86_64/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
536536
libc.src.math.canonicalizef16
537537
libc.src.math.ceilf16
538538
libc.src.math.copysignf16
539+
libc.src.math.f16addf
539540
libc.src.math.f16divf
540541
libc.src.math.f16fmaf
541542
libc.src.math.f16sqrtf

libc/docs/math/index.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ Basic Operations
124124
+------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
125125
| dsub | N/A | N/A | | N/A | | 7.12.14.2 | F.10.11 |
126126
+------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
127+
| f16add | |check| | | | N/A | | 7.12.14.1 | F.10.11 |
128+
+------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
127129
| f16div | |check| | | | N/A | | 7.12.14.4 | F.10.11 |
128130
+------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
129131
| f16fma | |check| | | | N/A | | 7.12.14.5 | F.10.11 |

libc/spec/stdc.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,8 @@ def StdC : StandardSpec<"stdc"> {
726726

727727
GuardedFunctionSpec<"setpayloadsigf16", RetValSpec<IntType>, [ArgSpec<Float16Ptr>, ArgSpec<Float16Type>], "LIBC_TYPES_HAS_FLOAT16">,
728728

729+
GuardedFunctionSpec<"f16addf", RetValSpec<Float16Type>, [ArgSpec<FloatType>, ArgSpec<FloatType>], "LIBC_TYPES_HAS_FLOAT16">,
730+
729731
GuardedFunctionSpec<"f16divf", RetValSpec<Float16Type>, [ArgSpec<FloatType>, ArgSpec<FloatType>], "LIBC_TYPES_HAS_FLOAT16">,
730732

731733
GuardedFunctionSpec<"f16sqrtf", RetValSpec<Float16Type>, [ArgSpec<FloatType>], "LIBC_TYPES_HAS_FLOAT16">,

libc/src/__support/FPUtil/generic/CMakeLists.txt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,25 @@ add_header_library(
4646
libc.src.__support.macros.optimization
4747
)
4848

49+
add_header_library(
50+
add
51+
HDRS
52+
add.h
53+
DEPENDS
54+
libc.hdr.errno_macros
55+
libc.hdr.fenv_macros
56+
libc.src.__support.CPP.algorithm
57+
libc.src.__support.CPP.bit
58+
libc.src.__support.CPP.type_traits
59+
libc.src.__support.FPUtil.basic_operations
60+
libc.src.__support.FPUtil.fenv_impl
61+
libc.src.__support.FPUtil.fp_bits
62+
libc.src.__support.FPUtil.dyadic_float
63+
libc.src.__support.FPUtil.rounding_mode
64+
libc.src.__support.macros.attributes
65+
libc.src.__support.macros.optimization
66+
)
67+
4968
add_header_library(
5069
div
5170
HDRS
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
//===-- Addition of IEEE 754 floating-point numbers -------------*- 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___SUPPORT_FPUTIL_GENERIC_ADD_H
10+
#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_GENERIC_ADD_H
11+
12+
#include "hdr/errno_macros.h"
13+
#include "hdr/fenv_macros.h"
14+
#include "src/__support/CPP/algorithm.h"
15+
#include "src/__support/CPP/bit.h"
16+
#include "src/__support/CPP/type_traits.h"
17+
#include "src/__support/FPUtil/BasicOperations.h"
18+
#include "src/__support/FPUtil/FEnvImpl.h"
19+
#include "src/__support/FPUtil/FPBits.h"
20+
#include "src/__support/FPUtil/dyadic_float.h"
21+
#include "src/__support/FPUtil/rounding_mode.h"
22+
#include "src/__support/macros/attributes.h"
23+
#include "src/__support/macros/optimization.h"
24+
25+
namespace LIBC_NAMESPACE::fputil::generic {
26+
27+
template <typename OutType, typename InType>
28+
LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<OutType> &&
29+
cpp::is_floating_point_v<InType> &&
30+
sizeof(OutType) <= sizeof(InType),
31+
OutType>
32+
add(InType x, InType y) {
33+
using OutFPBits = FPBits<OutType>;
34+
using OutStorageType = typename OutFPBits::StorageType;
35+
using InFPBits = FPBits<InType>;
36+
using InStorageType = typename InFPBits::StorageType;
37+
38+
constexpr int GUARD_BITS_LEN = 3;
39+
constexpr int RESULT_FRACTION_LEN = InFPBits::FRACTION_LEN + GUARD_BITS_LEN;
40+
constexpr int RESULT_MANTISSA_LEN = RESULT_FRACTION_LEN + 1;
41+
42+
using DyadicFloat =
43+
DyadicFloat<cpp::bit_ceil(static_cast<size_t>(RESULT_MANTISSA_LEN))>;
44+
45+
InFPBits x_bits(x);
46+
InFPBits y_bits(y);
47+
48+
if (LIBC_UNLIKELY(x_bits.is_inf_or_nan() || y_bits.is_inf_or_nan() ||
49+
x_bits.is_zero() || y_bits.is_zero())) {
50+
if (x_bits.is_nan() || y_bits.is_nan()) {
51+
if (x_bits.is_signaling_nan() || y_bits.is_signaling_nan())
52+
raise_except_if_required(FE_INVALID);
53+
54+
if (x_bits.is_quiet_nan()) {
55+
InStorageType x_payload = static_cast<InStorageType>(getpayload(x));
56+
if ((x_payload & ~(OutFPBits::FRACTION_MASK >> 1)) == 0)
57+
return OutFPBits::quiet_nan(x_bits.sign(),
58+
static_cast<OutStorageType>(x_payload))
59+
.get_val();
60+
}
61+
62+
if (y_bits.is_quiet_nan()) {
63+
InStorageType y_payload = static_cast<InStorageType>(getpayload(y));
64+
if ((y_payload & ~(OutFPBits::FRACTION_MASK >> 1)) == 0)
65+
return OutFPBits::quiet_nan(y_bits.sign(),
66+
static_cast<OutStorageType>(y_payload))
67+
.get_val();
68+
}
69+
70+
return OutFPBits::quiet_nan().get_val();
71+
}
72+
73+
if (x_bits.is_inf()) {
74+
if (y_bits.is_inf()) {
75+
if (x_bits.sign() != y_bits.sign()) {
76+
raise_except_if_required(FE_INVALID);
77+
return OutFPBits::quiet_nan().get_val();
78+
}
79+
80+
return OutFPBits::inf(x_bits.sign()).get_val();
81+
}
82+
83+
return OutFPBits::inf(x_bits.sign()).get_val();
84+
}
85+
86+
if (y_bits.is_inf())
87+
return OutFPBits::inf(y_bits.sign()).get_val();
88+
89+
if (x_bits.is_zero()) {
90+
if (y_bits.is_zero()) {
91+
switch (quick_get_round()) {
92+
case FE_DOWNWARD:
93+
return OutFPBits::zero(Sign::NEG).get_val();
94+
default:
95+
return OutFPBits::zero(Sign::POS).get_val();
96+
}
97+
}
98+
99+
return static_cast<OutType>(y);
100+
}
101+
102+
if (y_bits.is_zero())
103+
return static_cast<OutType>(x);
104+
}
105+
106+
InType x_abs = x_bits.abs().get_val();
107+
InType y_abs = y_bits.abs().get_val();
108+
109+
if (x_abs == y_abs && x_bits.sign() != y_bits.sign()) {
110+
switch (quick_get_round()) {
111+
case FE_DOWNWARD:
112+
return OutFPBits::zero(Sign::NEG).get_val();
113+
default:
114+
return OutFPBits::zero(Sign::POS).get_val();
115+
}
116+
}
117+
118+
Sign result_sign = Sign::POS;
119+
120+
if (x_abs > y_abs)
121+
result_sign = x_bits.sign();
122+
else if (x_abs < y_abs)
123+
result_sign = y_bits.sign();
124+
else if (x_bits.sign() == y_bits.sign())
125+
result_sign = x_bits.sign();
126+
127+
InFPBits max_bits(cpp::max(x_abs, y_abs));
128+
InFPBits min_bits(cpp::min(x_abs, y_abs));
129+
130+
InStorageType result_mant;
131+
132+
if (max_bits.is_subnormal()) {
133+
// min_bits must be subnormal too.
134+
if (max_bits.sign() == min_bits.sign())
135+
result_mant = max_bits.get_mantissa() + min_bits.get_mantissa();
136+
else
137+
result_mant = max_bits.get_mantissa() - min_bits.get_mantissa();
138+
139+
result_mant <<= GUARD_BITS_LEN;
140+
} else {
141+
InStorageType max_mant = max_bits.get_explicit_mantissa() << GUARD_BITS_LEN;
142+
InStorageType min_mant = min_bits.get_explicit_mantissa() << GUARD_BITS_LEN;
143+
int alignment =
144+
max_bits.get_biased_exponent() - min_bits.get_biased_exponent();
145+
146+
InStorageType aligned_min_mant =
147+
min_mant >> cpp::min(alignment, RESULT_MANTISSA_LEN);
148+
bool aligned_min_mant_sticky;
149+
150+
if (alignment <= 3)
151+
aligned_min_mant_sticky = false;
152+
else if (alignment <= InFPBits::FRACTION_LEN + 3)
153+
aligned_min_mant_sticky =
154+
(min_mant << (InFPBits::STORAGE_LEN - alignment)) != 0;
155+
else
156+
aligned_min_mant_sticky = true;
157+
158+
if (max_bits.sign() == min_bits.sign())
159+
result_mant = max_mant + (aligned_min_mant | aligned_min_mant_sticky);
160+
else
161+
result_mant = max_mant - (aligned_min_mant | aligned_min_mant_sticky);
162+
}
163+
164+
int result_exp = max_bits.get_exponent() - RESULT_FRACTION_LEN;
165+
DyadicFloat result(result_sign, result_exp, result_mant);
166+
return result.template as<OutType, /*ShouldSignalExceptions=*/true>();
167+
}
168+
169+
} // namespace LIBC_NAMESPACE::fputil::generic
170+
171+
#endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_GENERIC_ADD_H

libc/src/math/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ add_math_entrypoint_object(exp10f)
9999
add_math_entrypoint_object(expm1)
100100
add_math_entrypoint_object(expm1f)
101101

102+
add_math_entrypoint_object(f16addf)
103+
102104
add_math_entrypoint_object(f16divf)
103105

104106
add_math_entrypoint_object(f16fmaf)

libc/src/math/f16addf.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//===-- Implementation header for f16addf -----------------------*- 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_F16ADDF_H
10+
#define LLVM_LIBC_SRC_MATH_F16ADDF_H
11+
12+
#include "src/__support/macros/properties/types.h"
13+
14+
namespace LIBC_NAMESPACE {
15+
16+
float16 f16addf(float x, float y);
17+
18+
} // namespace LIBC_NAMESPACE
19+
20+
#endif // LLVM_LIBC_SRC_MATH_F16ADDF_H

libc/src/math/generic/CMakeLists.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3754,6 +3754,19 @@ add_entrypoint_object(
37543754
-O3
37553755
)
37563756

3757+
add_entrypoint_object(
3758+
f16addf
3759+
SRCS
3760+
f16addf.cpp
3761+
HDRS
3762+
../f16addf.h
3763+
DEPENDS
3764+
libc.src.__support.macros.properties.types
3765+
libc.src.__support.FPUtil.generic.add
3766+
COMPILE_OPTIONS
3767+
-O3
3768+
)
3769+
37573770
add_entrypoint_object(
37583771
f16divf
37593772
SRCS

libc/src/math/generic/f16addf.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//===-- Implementation of f16addf 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/f16addf.h"
10+
#include "src/__support/FPUtil/generic/add.h"
11+
#include "src/__support/common.h"
12+
13+
namespace LIBC_NAMESPACE {
14+
15+
LLVM_LIBC_FUNCTION(float16, f16addf, (float x, float y)) {
16+
return fputil::generic::add<float16>(x, y);
17+
}
18+
19+
} // namespace LIBC_NAMESPACE

libc/test/src/math/AddTest.h

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//===-- Utility class to test different flavors of float add ----*- 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_TEST_SRC_MATH_ADDTEST_H
10+
#define LLVM_LIBC_TEST_SRC_MATH_ADDTEST_H
11+
12+
#include "test/UnitTest/FEnvSafeTest.h"
13+
#include "test/UnitTest/FPMatcher.h"
14+
#include "test/UnitTest/Test.h"
15+
#include "utils/MPFRWrapper/MPFRUtils.h"
16+
17+
namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
18+
19+
template <typename OutType, typename InType>
20+
class AddTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
21+
22+
struct InConstants {
23+
DECLARE_SPECIAL_CONSTANTS(InType)
24+
};
25+
26+
using InFPBits = typename InConstants::FPBits;
27+
using InStorageType = typename InConstants::StorageType;
28+
29+
static constexpr InStorageType IN_MAX_NORMAL_U =
30+
InFPBits::max_normal().uintval();
31+
static constexpr InStorageType IN_MIN_NORMAL_U =
32+
InFPBits::min_normal().uintval();
33+
static constexpr InStorageType IN_MAX_SUBNORMAL_U =
34+
InFPBits::max_subnormal().uintval();
35+
static constexpr InStorageType IN_MIN_SUBNORMAL_U =
36+
InFPBits::min_subnormal().uintval();
37+
38+
public:
39+
typedef OutType (*AddFunc)(InType, InType);
40+
41+
void test_subnormal_range(AddFunc func) {
42+
constexpr InStorageType COUNT = 100'001;
43+
constexpr InStorageType STEP =
44+
(IN_MAX_SUBNORMAL_U - IN_MIN_SUBNORMAL_U) / COUNT;
45+
for (InStorageType i = 0, v = 0, w = IN_MAX_SUBNORMAL_U; i <= COUNT;
46+
++i, v += STEP, w -= STEP) {
47+
InType x = InFPBits(v).get_val();
48+
InType y = InFPBits(w).get_val();
49+
mpfr::BinaryInput<InType> input{x, y};
50+
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Add, input, func(x, y),
51+
0.5);
52+
}
53+
}
54+
55+
void test_normal_range(AddFunc func) {
56+
constexpr InStorageType COUNT = 100'001;
57+
constexpr InStorageType STEP = (IN_MAX_NORMAL_U - IN_MIN_NORMAL_U) / COUNT;
58+
for (InStorageType i = 0, v = 0, w = IN_MAX_NORMAL_U; i <= COUNT;
59+
++i, v += STEP, w -= STEP) {
60+
InType x = InFPBits(v).get_val();
61+
InType y = InFPBits(w).get_val();
62+
mpfr::BinaryInput<InType> input{x, y};
63+
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Add, input, func(x, y),
64+
0.5);
65+
}
66+
}
67+
};
68+
69+
#define LIST_ADD_TESTS(OutType, InType, func) \
70+
using LlvmLibcAddTest = AddTest<OutType, InType>; \
71+
TEST_F(LlvmLibcAddTest, SubnormalRange) { test_subnormal_range(&func); } \
72+
TEST_F(LlvmLibcAddTest, NormalRange) { test_normal_range(&func); }
73+
74+
#endif // LLVM_LIBC_TEST_SRC_MATH_ADDTEST_H

libc/test/src/math/CMakeLists.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1890,6 +1890,19 @@ add_fp_unittest(
18901890
libc.src.__support.FPUtil.fp_bits
18911891
)
18921892

1893+
add_fp_unittest(
1894+
f16addf_test
1895+
NEED_MPFR
1896+
SUITE
1897+
libc-math-unittests
1898+
SRCS
1899+
f16addf_test.cpp
1900+
HDRS
1901+
AddTest.h
1902+
DEPENDS
1903+
libc.src.math.f16addf
1904+
)
1905+
18931906
add_fp_unittest(
18941907
f16divf_test
18951908
NEED_MPFR

0 commit comments

Comments
 (0)