Skip to content

Commit

Permalink
[libc][math][c23] Add f16divf C23 math function (llvm#96131)
Browse files Browse the repository at this point in the history
Part of llvm#93566.
  • Loading branch information
overmighty authored and AlexisPerry committed Jun 27, 2024
1 parent a92d721 commit 8be21bd
Show file tree
Hide file tree
Showing 20 changed files with 613 additions and 45 deletions.
1 change: 1 addition & 0 deletions libc/config/linux/aarch64/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,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);
}
}

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 @@ -3731,6 +3731,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

0 comments on commit 8be21bd

Please sign in to comment.