Skip to content

Commit 8be21bd

Browse files
overmightyAlexisPerry
authored andcommitted
[libc][math][c23] Add f16divf C23 math function (llvm#96131)
Part of llvm#93566.
1 parent a92d721 commit 8be21bd

File tree

20 files changed

+613
-45
lines changed

20 files changed

+613
-45
lines changed

libc/config/linux/aarch64/entrypoints.txt

+1
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
505505
libc.src.math.canonicalizef16
506506
libc.src.math.ceilf16
507507
libc.src.math.copysignf16
508+
libc.src.math.f16divf
508509
libc.src.math.f16fmaf
509510
libc.src.math.f16sqrtf
510511
libc.src.math.fabsf16

libc/config/linux/x86_64/entrypoints.txt

+1
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

+2
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

+2
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<"f16divf", RetValSpec<Float16Type>, [ArgSpec<FloatType>, ArgSpec<FloatType>], "LIBC_TYPES_HAS_FLOAT16">,
730+
729731
GuardedFunctionSpec<"f16sqrtf", RetValSpec<Float16Type>, [ArgSpec<FloatType>], "LIBC_TYPES_HAS_FLOAT16">,
730732
]
731733
>;

libc/src/__support/FPUtil/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ add_header_library(
199199
HDRS
200200
dyadic_float.h
201201
DEPENDS
202+
.fenv_impl
202203
.fp_bits
203204
.multiply_add
204205
libc.src.__support.CPP.type_traits

libc/src/__support/FPUtil/dyadic_float.h

+32-3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#ifndef LLVM_LIBC_SRC___SUPPORT_FPUTIL_DYADIC_FLOAT_H
1010
#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_DYADIC_FLOAT_H
1111

12+
#include "FEnvImpl.h"
1213
#include "FPBits.h"
1314
#include "multiply_add.h"
1415
#include "src/__support/CPP/type_traits.h"
@@ -86,11 +87,11 @@ template <size_t Bits> struct DyadicFloat {
8687

8788
// Assume that it is already normalized.
8889
// Output is rounded correctly with respect to the current rounding mode.
89-
template <typename T,
90+
template <typename T, bool ShouldSignalExceptions,
9091
typename = cpp::enable_if_t<cpp::is_floating_point_v<T> &&
9192
(FPBits<T>::FRACTION_LEN < Bits),
9293
void>>
93-
LIBC_INLINE explicit constexpr operator T() const {
94+
LIBC_INLINE constexpr T as() const {
9495
if (LIBC_UNLIKELY(mantissa.is_zero()))
9596
return FPBits<T>::zero(sign).get_val();
9697

@@ -107,7 +108,17 @@ template <size_t Bits> struct DyadicFloat {
107108
T d_hi =
108109
FPBits<T>::create_value(sign, 2 * FPBits<T>::EXP_BIAS, IMPLICIT_MASK)
109110
.get_val();
110-
return T(2) * d_hi;
111+
// volatile prevents constant propagation that would result in infinity
112+
// always being returned no matter the current rounding mode.
113+
volatile T two(2.0);
114+
T r = two * d_hi;
115+
116+
// TODO: Whether rounding down the absolute value to max_normal should
117+
// also raise FE_OVERFLOW and set ERANGE is debatable.
118+
if (ShouldSignalExceptions && FPBits<T>(r).is_inf())
119+
set_errno_if_required(ERANGE);
120+
121+
return r;
111122
}
112123

113124
bool denorm = false;
@@ -179,10 +190,20 @@ template <size_t Bits> struct DyadicFloat {
179190
output_bits_t clear_exp = static_cast<output_bits_t>(
180191
output_bits_t(exp_hi) << FPBits<T>::SIG_LEN);
181192
output_bits_t r_bits = FPBits<T>(r).uintval() - clear_exp;
193+
182194
if (!(r_bits & FPBits<T>::EXP_MASK)) {
183195
// Output is denormal after rounding, clear the implicit bit for 80-bit
184196
// long double.
185197
r_bits -= IMPLICIT_MASK;
198+
199+
// TODO: IEEE Std 754-2019 lets implementers choose whether to check for
200+
// "tininess" before or after rounding for base-2 formats, as long as
201+
// the same choice is made for all operations. Our choice to check after
202+
// rounding might not be the same as the hardware's.
203+
if (ShouldSignalExceptions && round_and_sticky) {
204+
set_errno_if_required(ERANGE);
205+
raise_except_if_required(FE_UNDERFLOW);
206+
}
186207
}
187208

188209
return FPBits<T>(r_bits).get_val();
@@ -191,6 +212,14 @@ template <size_t Bits> struct DyadicFloat {
191212
return r;
192213
}
193214

215+
template <typename T,
216+
typename = cpp::enable_if_t<cpp::is_floating_point_v<T> &&
217+
(FPBits<T>::FRACTION_LEN < Bits),
218+
void>>
219+
LIBC_INLINE explicit constexpr operator T() const {
220+
return as<T, /*ShouldSignalExceptions=*/false>();
221+
}
222+
194223
LIBC_INLINE explicit constexpr operator MantissaType() const {
195224
if (mantissa.is_zero())
196225
return 0;

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

+17
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,20 @@ 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.errno_macros
55+
libc.hdr.fenv_macros
56+
libc.src.__support.CPP.bit
57+
libc.src.__support.CPP.type_traits
58+
libc.src.__support.FPUtil.basic_operations
59+
libc.src.__support.FPUtil.fenv_impl
60+
libc.src.__support.FPUtil.fp_bits
61+
libc.src.__support.FPUtil.dyadic_float
62+
libc.src.__support.macros.attributes
63+
libc.src.__support.macros.optimization
64+
)
+124
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
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/errno_macros.h"
13+
#include "hdr/fenv_macros.h"
14+
#include "src/__support/CPP/bit.h"
15+
#include "src/__support/CPP/type_traits.h"
16+
#include "src/__support/FPUtil/BasicOperations.h"
17+
#include "src/__support/FPUtil/FEnvImpl.h"
18+
#include "src/__support/FPUtil/FPBits.h"
19+
#include "src/__support/FPUtil/dyadic_float.h"
20+
#include "src/__support/macros/attributes.h"
21+
#include "src/__support/macros/optimization.h"
22+
23+
namespace LIBC_NAMESPACE::fputil::generic {
24+
25+
template <typename OutType, typename InType>
26+
LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<OutType> &&
27+
cpp::is_floating_point_v<InType> &&
28+
sizeof(OutType) <= sizeof(InType),
29+
OutType>
30+
div(InType x, InType y) {
31+
using OutFPBits = FPBits<OutType>;
32+
using OutStorageType = typename OutFPBits::StorageType;
33+
using InFPBits = FPBits<InType>;
34+
using InStorageType = typename InFPBits::StorageType;
35+
using DyadicFloat =
36+
DyadicFloat<cpp::bit_ceil(static_cast<size_t>(InFPBits::FRACTION_LEN))>;
37+
38+
InFPBits x_bits(x);
39+
InFPBits y_bits(y);
40+
41+
Sign result_sign = x_bits.sign() == y_bits.sign() ? Sign::POS : Sign::NEG;
42+
43+
if (LIBC_UNLIKELY(x_bits.is_inf_or_nan() || y_bits.is_inf_or_nan() ||
44+
x_bits.is_zero() || y_bits.is_zero())) {
45+
if (x_bits.is_nan() || y_bits.is_nan()) {
46+
if (x_bits.is_signaling_nan() || y_bits.is_signaling_nan())
47+
raise_except_if_required(FE_INVALID);
48+
49+
if (x_bits.is_quiet_nan()) {
50+
InStorageType x_payload = static_cast<InStorageType>(getpayload(x));
51+
if ((x_payload & ~(OutFPBits::FRACTION_MASK >> 1)) == 0)
52+
return OutFPBits::quiet_nan(x_bits.sign(),
53+
static_cast<OutStorageType>(x_payload))
54+
.get_val();
55+
}
56+
57+
if (y_bits.is_quiet_nan()) {
58+
InStorageType y_payload = static_cast<InStorageType>(getpayload(y));
59+
if ((y_payload & ~(OutFPBits::FRACTION_MASK >> 1)) == 0)
60+
return OutFPBits::quiet_nan(y_bits.sign(),
61+
static_cast<OutStorageType>(y_payload))
62+
.get_val();
63+
}
64+
65+
return OutFPBits::quiet_nan().get_val();
66+
}
67+
68+
if (x_bits.is_inf()) {
69+
if (y_bits.is_inf()) {
70+
set_errno_if_required(EDOM);
71+
raise_except_if_required(FE_INVALID);
72+
return OutFPBits::quiet_nan().get_val();
73+
}
74+
75+
return OutFPBits::inf(result_sign).get_val();
76+
}
77+
78+
if (y_bits.is_inf())
79+
return OutFPBits::inf(result_sign).get_val();
80+
81+
if (y_bits.is_zero()) {
82+
if (x_bits.is_zero()) {
83+
raise_except_if_required(FE_INVALID);
84+
return OutFPBits::quiet_nan().get_val();
85+
}
86+
87+
raise_except_if_required(FE_DIVBYZERO);
88+
return OutFPBits::inf(result_sign).get_val();
89+
}
90+
91+
if (x_bits.is_zero())
92+
return OutFPBits::zero(result_sign).get_val();
93+
}
94+
95+
DyadicFloat xd(x);
96+
DyadicFloat yd(y);
97+
98+
// Number of iterations = full output precision + 1 rounding bit + 1 potential
99+
// leading 0.
100+
constexpr size_t NUM_ITERS = OutFPBits::FRACTION_LEN + 3;
101+
int result_exp = xd.exponent - yd.exponent - (NUM_ITERS - 1);
102+
103+
InStorageType q = 0;
104+
InStorageType r = static_cast<InStorageType>(xd.mantissa >> 2);
105+
InStorageType yd_mant_in = static_cast<InStorageType>(yd.mantissa >> 1);
106+
107+
for (size_t i = 0; i < NUM_ITERS; ++i) {
108+
q <<= 1;
109+
r <<= 1;
110+
if (r >= yd_mant_in) {
111+
q += 1;
112+
r -= yd_mant_in;
113+
}
114+
}
115+
116+
DyadicFloat result(result_sign, result_exp, q);
117+
result.mantissa += r != 0;
118+
119+
return result.template as<OutType, /*ShouldSignalExceptions=*/true>();
120+
}
121+
122+
} // namespace LIBC_NAMESPACE::fputil::generic
123+
124+
#endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_GENERIC_DIV_H

libc/src/math/CMakeLists.txt

+2
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

+20
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

+13
Original file line numberDiff line numberDiff line change
@@ -3731,6 +3731,19 @@ add_entrypoint_object(
37313731
-O3
37323732
)
37333733

3734+
add_entrypoint_object(
3735+
f16divf
3736+
SRCS
3737+
f16divf.cpp
3738+
HDRS
3739+
../f16divf.h
3740+
DEPENDS
3741+
libc.src.__support.macros.properties.types
3742+
libc.src.__support.FPUtil.generic.div
3743+
COMPILE_OPTIONS
3744+
-O3
3745+
)
3746+
37343747
add_entrypoint_object(
37353748
f16fmaf
37363749
SRCS

libc/src/math/generic/f16divf.cpp

+19
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

+13
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

0 commit comments

Comments
 (0)