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
13 changes: 12 additions & 1 deletion libc/src/__support/FPUtil/dyadic_float.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,10 @@ 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);
return two * d_hi;
}

bool denorm = false;
Expand Down Expand Up @@ -179,10 +182,18 @@ 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 (round_and_sticky)
raise_except_if_required(FE_UNDERFLOW);
overmighty marked this conversation as resolved.
Show resolved Hide resolved
}

return FPBits<T>(r_bits).get_val();
Expand Down
16 changes: 16 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,19 @@ add_header_library(
libc.src.__support.FPUtil.rounding_mode
libc.src.__support.macros.optimization
)

add_header_library(
div
HDRS
div.h
DEPENDS
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
)
128 changes: 128 additions & 0 deletions libc/src/__support/FPUtil/generic/div.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
//===-- 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/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;

OutType output = static_cast<OutType>(result);

if (test_except(FE_OVERFLOW | FE_UNDERFLOW) != 0)
overmighty marked this conversation as resolved.
Show resolved Hide resolved
set_errno_if_required(ERANGE);

return output;
}

} // 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
74 changes: 74 additions & 0 deletions libc/test/src/math/DivTest.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//===-- Utility class to test different flavors of float div ----*- 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_TEST_SRC_MATH_DIVTEST_H
#define LLVM_LIBC_TEST_SRC_MATH_DIVTEST_H

#include "test/UnitTest/FEnvSafeTest.h"
#include "test/UnitTest/FPMatcher.h"
#include "test/UnitTest/Test.h"
#include "utils/MPFRWrapper/MPFRUtils.h"

namespace mpfr = LIBC_NAMESPACE::testing::mpfr;

template <typename OutType, typename InType>
class DivTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {

struct InConstants {
DECLARE_SPECIAL_CONSTANTS(InType)
};

using InFPBits = typename InConstants::FPBits;
using InStorageType = typename InConstants::StorageType;

static constexpr InStorageType IN_MAX_NORMAL_U =
InFPBits::max_normal().uintval();
static constexpr InStorageType IN_MIN_NORMAL_U =
InFPBits::min_normal().uintval();
static constexpr InStorageType IN_MAX_SUBNORMAL_U =
InFPBits::max_subnormal().uintval();
static constexpr InStorageType IN_MIN_SUBNORMAL_U =
InFPBits::min_subnormal().uintval();

public:
using DivFunc = OutType (*)(InType, InType);

void test_subnormal_range(DivFunc func) {
constexpr InStorageType COUNT = 100'001;
constexpr InStorageType STEP =
(IN_MAX_SUBNORMAL_U - IN_MIN_SUBNORMAL_U) / COUNT;
for (InStorageType i = 0, v = 0, w = IN_MAX_SUBNORMAL_U; i <= COUNT;
++i, v += STEP, w -= STEP) {
InType x = InFPBits(v).get_val();
InType y = InFPBits(w).get_val();
mpfr::BinaryInput<InType> input{x, y};
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Div, input, func(x, y),
0.5);
}
}

void test_normal_range(DivFunc func) {
constexpr InStorageType COUNT = 100'001;
constexpr InStorageType STEP = (IN_MAX_NORMAL_U - IN_MIN_NORMAL_U) / COUNT;
for (InStorageType i = 0, v = 0, w = IN_MAX_NORMAL_U; i <= COUNT;
++i, v += STEP, w -= STEP) {
InType x = InFPBits(v).get_val();
InType y = InFPBits(w).get_val();
mpfr::BinaryInput<InType> input{x, y};
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Div, input, func(x, y),
0.5);
}
}
};

#define LIST_DIV_TESTS(OutType, InType, func) \
using LlvmLibcDivTest = DivTest<OutType, InType>; \
TEST_F(LlvmLibcDivTest, SubnormalRange) { test_subnormal_range(&func); } \
TEST_F(LlvmLibcDivTest, NormalRange) { test_normal_range(&func); }

#endif // LLVM_LIBC_TEST_SRC_MATH_DIVTEST_H
13 changes: 13 additions & 0 deletions libc/test/src/math/f16divf_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//===-- Unittests for f16divf ---------------------------------------------===//
//
// 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 "DivTest.h"

#include "src/math/f16divf.h"

LIST_DIV_TESTS(float16, float, LIBC_NAMESPACE::f16divf)
Loading
Loading