Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[libc][math][c23] Add f16divf C23 math function #96131

Merged
merged 8 commits into from
Jun 25, 2024
1 change: 1 addition & 0 deletions libc/config/linux/aarch64/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
libc.src.math.canonicalizef16
libc.src.math.ceilf16
libc.src.math.copysignf16
libc.src.math.f16divf
libc.src.math.f16fmaf
libc.src.math.f16sqrtf
libc.src.math.fabsf16
Expand Down
1 change: 1 addition & 0 deletions libc/config/linux/x86_64/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
libc.src.math.canonicalizef16
libc.src.math.ceilf16
libc.src.math.copysignf16
libc.src.math.f16divf
libc.src.math.f16fmaf
libc.src.math.f16sqrtf
libc.src.math.fabsf16
Expand Down
2 changes: 2 additions & 0 deletions libc/docs/math/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ Basic Operations
+------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
| dsub | N/A | N/A | | N/A | | 7.12.14.2 | F.10.11 |
+------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
| f16div | |check| | | | N/A | | 7.12.14.4 | F.10.11 |
+------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
| f16fma | |check| | | | N/A | | 7.12.14.5 | F.10.11 |
+------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
| fabs | |check| | |check| | |check| | |check| | |check| | 7.12.7.3 | F.10.4.3 |
Expand Down
2 changes: 2 additions & 0 deletions libc/spec/stdc.td
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,8 @@ def StdC : StandardSpec<"stdc"> {

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

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

GuardedFunctionSpec<"f16sqrtf", RetValSpec<Float16Type>, [ArgSpec<FloatType>], "LIBC_TYPES_HAS_FLOAT16">,
]
>;
Expand Down
1 change: 1 addition & 0 deletions libc/src/__support/FPUtil/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ add_header_library(
HDRS
dyadic_float.h
DEPENDS
.fenv_impl
.fp_bits
.multiply_add
libc.src.__support.CPP.type_traits
Expand Down
35 changes: 32 additions & 3 deletions libc/src/__support/FPUtil/dyadic_float.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#ifndef LLVM_LIBC_SRC___SUPPORT_FPUTIL_DYADIC_FLOAT_H
#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_DYADIC_FLOAT_H

#include "FEnvImpl.h"
#include "FPBits.h"
#include "multiply_add.h"
#include "src/__support/CPP/type_traits.h"
Expand Down Expand Up @@ -86,11 +87,11 @@ template <size_t Bits> struct DyadicFloat {

// Assume that it is already normalized.
// Output is rounded correctly with respect to the current rounding mode.
template <typename T,
template <typename T, bool ShouldSignalExceptions,
typename = cpp::enable_if_t<cpp::is_floating_point_v<T> &&
(FPBits<T>::FRACTION_LEN < Bits),
void>>
LIBC_INLINE explicit constexpr operator T() const {
LIBC_INLINE constexpr T as() const {
if (LIBC_UNLIKELY(mantissa.is_zero()))
return FPBits<T>::zero(sign).get_val();

Expand All @@ -107,7 +108,17 @@ template <size_t Bits> struct DyadicFloat {
T d_hi =
FPBits<T>::create_value(sign, 2 * FPBits<T>::EXP_BIAS, IMPLICIT_MASK)
.get_val();
return T(2) * d_hi;
// volatile prevents constant propagation that would result in infinity
// always being returned no matter the current rounding mode.
volatile T two(2.0);
T r = two * d_hi;

// TODO: Whether rounding down the absolute value to max_normal should
// also raise FE_OVERFLOW and set ERANGE is debatable.
if (ShouldSignalExceptions && FPBits<T>(r).is_inf())
set_errno_if_required(ERANGE);

return r;
}

bool denorm = false;
Expand Down Expand Up @@ -179,10 +190,20 @@ template <size_t Bits> struct DyadicFloat {
output_bits_t clear_exp = static_cast<output_bits_t>(
output_bits_t(exp_hi) << FPBits<T>::SIG_LEN);
output_bits_t r_bits = FPBits<T>(r).uintval() - clear_exp;

if (!(r_bits & FPBits<T>::EXP_MASK)) {
// Output is denormal after rounding, clear the implicit bit for 80-bit
// long double.
r_bits -= IMPLICIT_MASK;

// TODO: IEEE Std 754-2019 lets implementers choose whether to check for
// "tininess" before or after rounding for base-2 formats, as long as
// the same choice is made for all operations. Our choice to check after
// rounding might not be the same as the hardware's.
if (ShouldSignalExceptions && round_and_sticky) {
set_errno_if_required(ERANGE);
raise_except_if_required(FE_UNDERFLOW);
overmighty marked this conversation as resolved.
Show resolved Hide resolved
}
}

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

template <typename T,
typename = cpp::enable_if_t<cpp::is_floating_point_v<T> &&
(FPBits<T>::FRACTION_LEN < Bits),
void>>
LIBC_INLINE explicit constexpr operator T() const {
return as<T, /*ShouldSignalExceptions=*/false>();
}

LIBC_INLINE explicit constexpr operator MantissaType() const {
if (mantissa.is_zero())
return 0;
Expand Down
17 changes: 17 additions & 0 deletions libc/src/__support/FPUtil/generic/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,20 @@ add_header_library(
libc.src.__support.FPUtil.rounding_mode
libc.src.__support.macros.optimization
)

add_header_library(
div
HDRS
div.h
DEPENDS
libc.hdr.errno_macros
libc.hdr.fenv_macros
libc.src.__support.CPP.bit
libc.src.__support.CPP.type_traits
libc.src.__support.FPUtil.basic_operations
libc.src.__support.FPUtil.fenv_impl
libc.src.__support.FPUtil.fp_bits
libc.src.__support.FPUtil.dyadic_float
libc.src.__support.macros.attributes
libc.src.__support.macros.optimization
)
124 changes: 124 additions & 0 deletions libc/src/__support/FPUtil/generic/div.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
//===-- Division of IEEE 754 floating-point numbers -------------*- 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___SUPPORT_FPUTIL_GENERIC_DIV_H
#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_GENERIC_DIV_H

#include "hdr/errno_macros.h"
#include "hdr/fenv_macros.h"
#include "src/__support/CPP/bit.h"
#include "src/__support/CPP/type_traits.h"
#include "src/__support/FPUtil/BasicOperations.h"
#include "src/__support/FPUtil/FEnvImpl.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/FPUtil/dyadic_float.h"
#include "src/__support/macros/attributes.h"
#include "src/__support/macros/optimization.h"

namespace LIBC_NAMESPACE::fputil::generic {

template <typename OutType, typename InType>
LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<OutType> &&
cpp::is_floating_point_v<InType> &&
sizeof(OutType) <= sizeof(InType),
OutType>
div(InType x, InType y) {
using OutFPBits = FPBits<OutType>;
using OutStorageType = typename OutFPBits::StorageType;
using InFPBits = FPBits<InType>;
using InStorageType = typename InFPBits::StorageType;
using DyadicFloat =
DyadicFloat<cpp::bit_ceil(static_cast<size_t>(InFPBits::FRACTION_LEN))>;

InFPBits x_bits(x);
InFPBits y_bits(y);

Sign result_sign = x_bits.sign() == y_bits.sign() ? Sign::POS : Sign::NEG;

if (LIBC_UNLIKELY(x_bits.is_inf_or_nan() || y_bits.is_inf_or_nan() ||
x_bits.is_zero() || y_bits.is_zero())) {
if (x_bits.is_nan() || y_bits.is_nan()) {
if (x_bits.is_signaling_nan() || y_bits.is_signaling_nan())
raise_except_if_required(FE_INVALID);

if (x_bits.is_quiet_nan()) {
InStorageType x_payload = static_cast<InStorageType>(getpayload(x));
if ((x_payload & ~(OutFPBits::FRACTION_MASK >> 1)) == 0)
return OutFPBits::quiet_nan(x_bits.sign(),
static_cast<OutStorageType>(x_payload))
.get_val();
}

if (y_bits.is_quiet_nan()) {
InStorageType y_payload = static_cast<InStorageType>(getpayload(y));
if ((y_payload & ~(OutFPBits::FRACTION_MASK >> 1)) == 0)
return OutFPBits::quiet_nan(y_bits.sign(),
static_cast<OutStorageType>(y_payload))
.get_val();
}

return OutFPBits::quiet_nan().get_val();
}

if (x_bits.is_inf()) {
if (y_bits.is_inf()) {
set_errno_if_required(EDOM);
raise_except_if_required(FE_INVALID);
return OutFPBits::quiet_nan().get_val();
}

return OutFPBits::inf(result_sign).get_val();
}

if (y_bits.is_inf())
return OutFPBits::inf(result_sign).get_val();

if (y_bits.is_zero()) {
if (x_bits.is_zero()) {
raise_except_if_required(FE_INVALID);
return OutFPBits::quiet_nan().get_val();
}

raise_except_if_required(FE_DIVBYZERO);
return OutFPBits::inf(result_sign).get_val();
}

if (x_bits.is_zero())
return OutFPBits::zero(result_sign).get_val();
}

DyadicFloat xd(x);
DyadicFloat yd(y);

// Number of iterations = full output precision + 1 rounding bit + 1 potential
// leading 0.
constexpr size_t NUM_ITERS = OutFPBits::FRACTION_LEN + 3;
int result_exp = xd.exponent - yd.exponent - (NUM_ITERS - 1);

InStorageType q = 0;
InStorageType r = static_cast<InStorageType>(xd.mantissa >> 2);
InStorageType yd_mant_in = static_cast<InStorageType>(yd.mantissa >> 1);

for (size_t i = 0; i < NUM_ITERS; ++i) {
q <<= 1;
r <<= 1;
if (r >= yd_mant_in) {
q += 1;
r -= yd_mant_in;
}
}

DyadicFloat result(result_sign, result_exp, q);
result.mantissa += r != 0;

return result.template as<OutType, /*ShouldSignalExceptions=*/true>();
}

} // namespace LIBC_NAMESPACE::fputil::generic

#endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_GENERIC_DIV_H
2 changes: 2 additions & 0 deletions libc/src/math/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ add_math_entrypoint_object(exp10f)
add_math_entrypoint_object(expm1)
add_math_entrypoint_object(expm1f)

add_math_entrypoint_object(f16divf)

add_math_entrypoint_object(f16fmaf)

add_math_entrypoint_object(f16sqrtf)
Expand Down
20 changes: 20 additions & 0 deletions libc/src/math/f16divf.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//===-- Implementation header for f16divf -----------------------*- 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_F16DIVF_H
#define LLVM_LIBC_SRC_MATH_F16DIVF_H

#include "src/__support/macros/properties/types.h"

namespace LIBC_NAMESPACE {

float16 f16divf(float x, float y);

} // namespace LIBC_NAMESPACE

#endif // LLVM_LIBC_SRC_MATH_F16DIVF_H
13 changes: 13 additions & 0 deletions libc/src/math/generic/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3682,6 +3682,19 @@ add_entrypoint_object(
-O3
)

add_entrypoint_object(
f16divf
SRCS
f16divf.cpp
HDRS
../f16divf.h
DEPENDS
libc.src.__support.macros.properties.types
libc.src.__support.FPUtil.generic.div
COMPILE_OPTIONS
-O3
)

add_entrypoint_object(
f16fmaf
SRCS
Expand Down
19 changes: 19 additions & 0 deletions libc/src/math/generic/f16divf.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//===-- Implementation of f16divf 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/f16divf.h"
#include "src/__support/FPUtil/generic/div.h"
#include "src/__support/common.h"

namespace LIBC_NAMESPACE {

LLVM_LIBC_FUNCTION(float16, f16divf, (float x, float y)) {
return fputil::generic::div<float16>(x, y);
}

} // namespace LIBC_NAMESPACE
13 changes: 13 additions & 0 deletions libc/test/src/math/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1890,6 +1890,19 @@ add_fp_unittest(
libc.src.__support.FPUtil.fp_bits
)

add_fp_unittest(
f16divf_test
NEED_MPFR
SUITE
libc-math-unittests
SRCS
f16divf_test.cpp
HDRS
DivTest.h
DEPENDS
libc.src.math.f16divf
)

add_fp_unittest(
f16fmaf_test
NEED_MPFR
Expand Down
Loading
Loading