Skip to content

Commit 612a87f

Browse files
committed
[libc][math][c23] Add f16divf C23 math function
1 parent 1107575 commit 612a87f

File tree

18 files changed

+528
-42
lines changed

18 files changed

+528
-42
lines changed

libc/config/linux/aarch64/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
504504
libc.src.math.canonicalizef16
505505
libc.src.math.ceilf16
506506
libc.src.math.copysignf16
507+
libc.src.math.f16divf
507508
libc.src.math.f16fmaf
508509
libc.src.math.f16sqrtf
509510
libc.src.math.fabsf16

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.f16divf
539540
libc.src.math.f16fmaf
540541
libc.src.math.f16sqrtf
541542
libc.src.math.fabsf16

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+
| f16div | |check| | | | N/A | | 7.12.14.4 | F.10.11 |
128+
+------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
127129
| f16fma | |check| | | | N/A | | 7.12.14.5 | F.10.11 |
128130
+------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
129131
| fabs | |check| | |check| | |check| | |check| | |check| | 7.12.7.3 | F.10.4.3 |

libc/spec/stdc.td

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

723723
GuardedFunctionSpec<"setpayloadsigf16", RetValSpec<IntType>, [ArgSpec<Float16Ptr>, ArgSpec<Float16Type>], "LIBC_TYPES_HAS_FLOAT16">,
724724

725+
GuardedFunctionSpec<"f16divf", RetValSpec<Float16Type>, [ArgSpec<FloatType>, ArgSpec<FloatType>], "LIBC_TYPES_HAS_FLOAT16">,
726+
725727
GuardedFunctionSpec<"f16sqrtf", RetValSpec<Float16Type>, [ArgSpec<FloatType>], "LIBC_TYPES_HAS_FLOAT16">,
726728
]
727729
>;

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,17 @@ add_header_library(
4545
libc.src.__support.FPUtil.rounding_mode
4646
libc.src.__support.macros.optimization
4747
)
48+
49+
add_header_library(
50+
div
51+
HDRS
52+
div.h
53+
DEPENDS
54+
libc.hdr.fenv_macros
55+
libc.src.__support.CPP.bit
56+
libc.src.__support.CPP.type_traits
57+
libc.src.__support.FPUtil.fenv_impl
58+
libc.src.__support.FPUtil.fp_bits
59+
libc.src.__support.FPUtil.dyadic_float
60+
libc.src.__support.FPUtil.rounding_mode
61+
)
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
//===-- Division 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_DIV_H
10+
#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_GENERIC_DIV_H
11+
12+
#include "hdr/fenv_macros.h"
13+
#include "src/__support/CPP/bit.h"
14+
#include "src/__support/CPP/type_traits.h"
15+
#include "src/__support/FPUtil/FEnvImpl.h"
16+
#include "src/__support/FPUtil/FPBits.h"
17+
#include "src/__support/FPUtil/dyadic_float.h"
18+
#include "src/__support/FPUtil/rounding_mode.h"
19+
20+
namespace LIBC_NAMESPACE::fputil::generic {
21+
22+
template <typename OutType, typename InType>
23+
cpp::enable_if_t<cpp::is_floating_point_v<OutType> &&
24+
cpp::is_floating_point_v<InType> &&
25+
sizeof(OutType) <= sizeof(InType),
26+
OutType>
27+
div(InType x, InType y) {
28+
using OutFPBits = FPBits<OutType>;
29+
using OutStorageType = typename OutFPBits::StorageType;
30+
using InFPBits = FPBits<InType>;
31+
using InStorageType = typename InFPBits::StorageType;
32+
using DyadicFloat =
33+
DyadicFloat<cpp::bit_ceil(static_cast<size_t>(InFPBits::FRACTION_LEN))>;
34+
using DyadicMantissaType = typename DyadicFloat::MantissaType;
35+
36+
// +1 for the implicit bit.
37+
constexpr int DYADIC_EXTRA_MANTISSA_LEN =
38+
DyadicMantissaType::BITS - (InFPBits::FRACTION_LEN + 1);
39+
// +1 for the extra fractional bit in q.
40+
constexpr int Q_EXTRA_FRACTION_LEN =
41+
InFPBits::FRACTION_LEN + 1 - OutFPBits::FRACTION_LEN;
42+
43+
InFPBits x_bits(x);
44+
InFPBits y_bits(y);
45+
46+
if (x_bits.is_nan() || y_bits.is_nan()) {
47+
if (x_bits.is_signaling_nan() || y_bits.is_signaling_nan())
48+
raise_except_if_required(FE_INVALID);
49+
50+
// TODO: Handle NaN payloads.
51+
return OutFPBits::quiet_nan().get_val();
52+
}
53+
54+
Sign result_sign = x_bits.sign() == y_bits.sign() ? Sign::POS : Sign::NEG;
55+
56+
if (x_bits.is_inf()) {
57+
if (y_bits.is_inf()) {
58+
raise_except_if_required(FE_INVALID);
59+
return OutFPBits::quiet_nan().get_val();
60+
}
61+
62+
return OutFPBits::inf(result_sign).get_val();
63+
}
64+
65+
if (y_bits.is_inf())
66+
return OutFPBits::inf(result_sign).get_val();
67+
68+
if (y_bits.is_zero()) {
69+
if (x_bits.is_zero()) {
70+
raise_except_if_required(FE_INVALID);
71+
return OutFPBits::quiet_nan().get_val();
72+
}
73+
74+
raise_except_if_required(FE_DIVBYZERO);
75+
return OutFPBits::inf(result_sign).get_val();
76+
}
77+
78+
if (x_bits.is_zero())
79+
return OutFPBits::zero(result_sign).get_val();
80+
81+
DyadicFloat xd(x);
82+
DyadicFloat yd(y);
83+
84+
bool would_q_be_subnormal = xd.mantissa < yd.mantissa;
85+
int q_exponent = xd.get_unbiased_exponent() - yd.get_unbiased_exponent() -
86+
would_q_be_subnormal;
87+
88+
if (q_exponent + OutFPBits::EXP_BIAS >= OutFPBits::MAX_BIASED_EXPONENT) {
89+
switch (quick_get_round()) {
90+
case FE_TONEAREST:
91+
case FE_UPWARD:
92+
return OutFPBits::inf().get_val();
93+
default:
94+
return OutFPBits::max_normal().get_val();
95+
}
96+
}
97+
98+
if (q_exponent < -OutFPBits::EXP_BIAS - OutFPBits::FRACTION_LEN) {
99+
switch (quick_get_round()) {
100+
case FE_UPWARD:
101+
return OutFPBits::min_subnormal().get_val();
102+
default:
103+
return OutFPBits::zero().get_val();
104+
}
105+
}
106+
107+
InStorageType q = 1;
108+
InStorageType xd_mant_in = static_cast<InStorageType>(
109+
xd.mantissa >> (DYADIC_EXTRA_MANTISSA_LEN - would_q_be_subnormal));
110+
InStorageType yd_mant_in =
111+
static_cast<InStorageType>(yd.mantissa >> DYADIC_EXTRA_MANTISSA_LEN);
112+
InStorageType r = xd_mant_in - yd_mant_in;
113+
114+
for (size_t i = 0; i < InFPBits::FRACTION_LEN + 1; i++) {
115+
q <<= 1;
116+
InStorageType t = r << 1;
117+
if (t < yd_mant_in) {
118+
r = t;
119+
} else {
120+
q += 1;
121+
r = t - yd_mant_in;
122+
}
123+
}
124+
125+
bool round;
126+
bool sticky;
127+
OutStorageType result;
128+
129+
if (q_exponent > -OutFPBits::EXP_BIAS) {
130+
// Result is normal.
131+
132+
round = (q & (InStorageType(1) << (Q_EXTRA_FRACTION_LEN - 1))) != 0;
133+
sticky = (q & ((InStorageType(1) << (Q_EXTRA_FRACTION_LEN - 1)) - 1)) != 0;
134+
135+
result = OutFPBits::create_value(
136+
result_sign,
137+
static_cast<OutStorageType>(q_exponent + OutFPBits::EXP_BIAS),
138+
static_cast<OutStorageType>((q >> Q_EXTRA_FRACTION_LEN) &
139+
OutFPBits::SIG_MASK))
140+
.uintval();
141+
142+
} else {
143+
// Result is subnormal.
144+
145+
// +1 because the leading bit is now part of the fraction.
146+
int underflow_extra_fraction_len =
147+
Q_EXTRA_FRACTION_LEN + 1 - q_exponent - OutFPBits::EXP_BIAS;
148+
149+
InStorageType round_bit_mask = InStorageType(1)
150+
<< (underflow_extra_fraction_len - 1);
151+
round = (q & round_bit_mask) != 0;
152+
InStorageType sticky_bits_mask = round_bit_mask - 1;
153+
sticky = (q & sticky_bits_mask) != 0;
154+
155+
result = OutFPBits::create_value(
156+
result_sign, 0,
157+
static_cast<OutStorageType>(q >> underflow_extra_fraction_len))
158+
.uintval();
159+
}
160+
161+
bool lsb = (result & 1) != 0;
162+
163+
switch (quick_get_round()) {
164+
case FE_TONEAREST:
165+
if (round && (lsb || sticky))
166+
++result;
167+
break;
168+
case FE_UPWARD:
169+
++result;
170+
break;
171+
default:
172+
break;
173+
}
174+
175+
return cpp::bit_cast<OutType>(result);
176+
}
177+
178+
} // namespace LIBC_NAMESPACE::fputil::generic
179+
180+
#endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_GENERIC_DIV_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(f16divf)
103+
102104
add_math_entrypoint_object(f16fmaf)
103105

104106
add_math_entrypoint_object(f16sqrtf)

libc/src/math/f16divf.h

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

libc/src/math/generic/CMakeLists.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3641,6 +3641,19 @@ add_entrypoint_object(
36413641
-O3
36423642
)
36433643

3644+
add_entrypoint_object(
3645+
f16divf
3646+
SRCS
3647+
f16divf.cpp
3648+
HDRS
3649+
../f16divf.h
3650+
DEPENDS
3651+
libc.src.__support.macros.properties.types
3652+
libc.src.__support.FPUtil.generic.div
3653+
COMPILE_OPTIONS
3654+
-O3
3655+
)
3656+
36443657
add_entrypoint_object(
36453658
f16fmaf
36463659
SRCS

libc/src/math/generic/f16divf.cpp

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

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+
f16divf_test
1895+
NEED_MPFR
1896+
SUITE
1897+
libc-math-unittests
1898+
SRCS
1899+
f16divf_test.cpp
1900+
HDRS
1901+
DivTest.h
1902+
DEPENDS
1903+
libc.src.math.f16divf
1904+
)
1905+
18931906
add_fp_unittest(
18941907
f16fmaf_test
18951908
NEED_MPFR

libc/test/src/math/DivTest.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 div ----*- 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_DIVTEST_H
10+
#define LLVM_LIBC_TEST_SRC_MATH_DIVTEST_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 DivTest : 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 (*DivFunc)(InType, InType);
40+
41+
void test_subnormal_range(DivFunc 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::Div, input, func(x, y),
51+
0.5);
52+
}
53+
}
54+
55+
void test_normal_range(DivFunc 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::Div, input, func(x, y),
64+
0.5);
65+
}
66+
}
67+
};
68+
69+
#define LIST_DIV_TESTS(OutType, InType, func) \
70+
using LlvmLibcDivTest = DivTest<OutType, InType>; \
71+
TEST_F(LlvmLibcDivTest, SubnormalRange) { test_subnormal_range(&func); } \
72+
TEST_F(LlvmLibcDivTest, NormalRange) { test_normal_range(&func); }
73+
74+
#endif // LLVM_LIBC_TEST_SRC_MATH_DIVTEST_H

0 commit comments

Comments
 (0)