diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -52,6 +52,9 @@ libc.src.math.fabs libc.src.math.fabsf libc.src.math.fabsl + libc.src.math.fdim + libc.src.math.fdimf + libc.src.math.fdiml libc.src.math.floor libc.src.math.floorf libc.src.math.floorl diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -85,6 +85,9 @@ libc.src.math.fabs libc.src.math.fabsf libc.src.math.fabsl + libc.src.math.fdim + libc.src.math.fdimf + libc.src.math.fdiml libc.src.math.floor libc.src.math.floorf libc.src.math.floorl diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -258,6 +258,10 @@ FunctionSpec<"fabsf", RetValSpec, [ArgSpec]>, FunctionSpec<"fabsl", RetValSpec, [ArgSpec]>, + FunctionSpec<"fdim", RetValSpec, [ArgSpec]>, + FunctionSpec<"fdimf", RetValSpec, [ArgSpec]>, + FunctionSpec<"fdiml", RetValSpec, [ArgSpec]>, + FunctionSpec<"floor", RetValSpec, [ArgSpec]>, FunctionSpec<"floorf", RetValSpec, [ArgSpec]>, FunctionSpec<"floorl", RetValSpec, [ArgSpec]>, diff --git a/libc/src/math/CMakeLists.txt b/libc/src/math/CMakeLists.txt --- a/libc/src/math/CMakeLists.txt +++ b/libc/src/math/CMakeLists.txt @@ -641,3 +641,39 @@ COMPILE_OPTIONS -O2 ) + +add_entrypoint_object( + fdim + SRCS + fdim.cpp + HDRS + fdim.h + DEPENDS + libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 +) + +add_entrypoint_object( + fdimf + SRCS + fdimf.cpp + HDRS + fdimf.h + DEPENDS + libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 +) + +add_entrypoint_object( + fdiml + SRCS + fdiml.cpp + HDRS + fdiml.h + DEPENDS + libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 +) diff --git a/libc/src/math/fdim.h b/libc/src/math/fdim.h new file mode 100644 --- /dev/null +++ b/libc/src/math/fdim.h @@ -0,0 +1,18 @@ +//===-- Implementation header for fdim --------------------------*- 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_FDIM_H +#define LLVM_LIBC_SRC_MATH_FDIM_H + +namespace __llvm_libc { + +double fdim(double x, double y); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_MATH_FDIM_H diff --git a/libc/src/math/fdim.cpp b/libc/src/math/fdim.cpp new file mode 100644 --- /dev/null +++ b/libc/src/math/fdim.cpp @@ -0,0 +1,18 @@ +//===-- Implementation of fdim 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/__support/common.h" +#include "utils/FPUtil/BasicOperations.h" + +namespace __llvm_libc { + +double LLVM_LIBC_ENTRYPOINT(fdim)(double x, double y) { + return fputil::fdim(x, y); +} + +} // namespace __llvm_libc diff --git a/libc/src/math/fdimf.h b/libc/src/math/fdimf.h new file mode 100644 --- /dev/null +++ b/libc/src/math/fdimf.h @@ -0,0 +1,18 @@ +//===-- Implementation header for fdimf -------------------------*- 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_FDIMF_H +#define LLVM_LIBC_SRC_MATH_FDIMF_H + +namespace __llvm_libc { + +float fdimf(float x, float y); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_MATH_FDIMF_H diff --git a/libc/src/math/fdimf.cpp b/libc/src/math/fdimf.cpp new file mode 100644 --- /dev/null +++ b/libc/src/math/fdimf.cpp @@ -0,0 +1,18 @@ +//===-- Implementation of fdimf 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/__support/common.h" +#include "utils/FPUtil/BasicOperations.h" + +namespace __llvm_libc { + +float LLVM_LIBC_ENTRYPOINT(fdimf)(float x, float y) { + return fputil::fdim(x, y); +} + +} // namespace __llvm_libc diff --git a/libc/src/math/fdiml.h b/libc/src/math/fdiml.h new file mode 100644 --- /dev/null +++ b/libc/src/math/fdiml.h @@ -0,0 +1,18 @@ +//===-- Implementation header for fdiml -------------------------*- 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_FDIML_H +#define LLVM_LIBC_SRC_MATH_FDIML_H + +namespace __llvm_libc { + +long double fdiml(long double x, long double y); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_MATH_FDIML_H diff --git a/libc/src/math/fdiml.cpp b/libc/src/math/fdiml.cpp new file mode 100644 --- /dev/null +++ b/libc/src/math/fdiml.cpp @@ -0,0 +1,18 @@ +//===-- Implementation of fdiml 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/__support/common.h" +#include "utils/FPUtil/BasicOperations.h" + +namespace __llvm_libc { + +long double LLVM_LIBC_ENTRYPOINT(fdiml)(long double x, long double y) { + return fputil::fdim(x, y); +} + +} // namespace __llvm_libc diff --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt --- a/libc/test/src/math/CMakeLists.txt +++ b/libc/test/src/math/CMakeLists.txt @@ -484,6 +484,48 @@ libc.utils.FPUtil.fputil ) +add_fp_unittest( + fdimf_test + SUITE + libc_math_unittests + SRCS + fdimf_test.cpp + HDRS + FDimTest.h + DEPENDS + libc.include.math + libc.src.math.fdimf + libc.utils.FPUtil.fputil +) + +add_fp_unittest( + fdim_test + SUITE + libc_math_unittests + SRCS + fdim_test.cpp + HDRS + FDimTest.h + DEPENDS + libc.include.math + libc.src.math.fdim + libc.utils.FPUtil.fputil +) + +add_fp_unittest( + fdiml_test + SUITE + libc_math_unittests + SRCS + fdiml_test.cpp + HDRS + FDimTest.h + DEPENDS + libc.include.math + libc.src.math.fdiml + libc.utils.FPUtil.fputil +) + add_fp_unittest( fminf_test SUITE diff --git a/libc/test/src/math/FDimTest.h b/libc/test/src/math/FDimTest.h new file mode 100644 --- /dev/null +++ b/libc/test/src/math/FDimTest.h @@ -0,0 +1,82 @@ +//===-- Utility class to test different flavors of fdim ---------*- 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 +// +//===---------------------------------------------------------------------===// + +#include "include/math.h" +#include "utils/FPUtil/BasicOperations.h" +#include "utils/FPUtil/FPBits.h" +#include "utils/FPUtil/TestHelpers.h" +#include "utils/UnitTest/Test.h" + +template +class FDimTestTemplate : public __llvm_libc::testing::Test { +public: + using FuncPtr = T (*)(T, T); + using FPBits = __llvm_libc::fputil::FPBits; + using UIntType = typename FPBits::UIntType; + + void testNaNArg(FuncPtr func) { + EXPECT_FP_EQ(nan, func(nan, inf)); + EXPECT_FP_EQ(nan, func(negInf, nan)); + EXPECT_FP_EQ(nan, func(nan, zero)); + EXPECT_FP_EQ(nan, func(negZero, nan)); + EXPECT_FP_EQ(nan, func(nan, T(-1.2345))); + EXPECT_FP_EQ(nan, func(T(1.2345), nan)); + EXPECT_NE(isnan(func(nan, nan)), 0); + } + + void testInfArg(FuncPtr func) { + EXPECT_FP_EQ(zero, func(negInf, inf)); + EXPECT_FP_EQ(inf, func(inf, zero)); + EXPECT_FP_EQ(zero, func(negZero, inf)); + EXPECT_FP_EQ(inf, func(inf, T(1.2345))); + EXPECT_FP_EQ(zero, func(T(-1.2345), inf)); + } + + void testNegInfArg(FuncPtr func) { + EXPECT_FP_EQ(inf, func(inf, negInf)); + EXPECT_FP_EQ(zero, func(negInf, zero)); + EXPECT_FP_EQ(inf, func(negZero, negInf)); + EXPECT_FP_EQ(zero, func(negInf, T(-1.2345))); + EXPECT_FP_EQ(inf, func(T(1.2345), negInf)); + } + + void testBothZero(FuncPtr func) { + EXPECT_FP_EQ(zero, func(zero, zero)); + EXPECT_FP_EQ(zero, func(zero, negZero)); + EXPECT_FP_EQ(zero, func(negZero, zero)); + EXPECT_FP_EQ(zero, func(negZero, negZero)); + } + + void testInRange(FuncPtr func) { + constexpr UIntType count = 10000001; + constexpr UIntType step = UIntType(-1) / count; + for (UIntType i = 0, v = 0, w = UIntType(-1); i <= count; + ++i, v += step, w -= step) { + T x = FPBits(v), y = FPBits(w); + if (isnan(x) || isinf(x)) + continue; + if (isnan(y) || isinf(y)) + continue; + + if (x > y) { + EXPECT_FP_EQ(x - y, func(x, y)); + } else { + EXPECT_FP_EQ(zero, func(x, y)); + } + } + } + +private: + // constexpr does not work on FPBits yet, so we cannot have these constants as + // static. + const T nan = __llvm_libc::fputil::FPBits::buildNaN(1); + const T inf = __llvm_libc::fputil::FPBits::inf(); + const T negInf = __llvm_libc::fputil::FPBits::negInf(); + const T zero = __llvm_libc::fputil::FPBits::zero(); + const T negZero = __llvm_libc::fputil::FPBits::negZero(); +}; diff --git a/libc/test/src/math/fdim_test.cpp b/libc/test/src/math/fdim_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/fdim_test.cpp @@ -0,0 +1,27 @@ +//===-- Unittests for fdim ------------------------------------------------===// +// +// 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 "FDimTest.h" + +#include "include/math.h" +#include "src/math/fdim.h" +#include "utils/FPUtil/FPBits.h" +#include "utils/FPUtil/TestHelpers.h" +#include "utils/UnitTest/Test.h" + +using FDimTest = FDimTestTemplate; + +TEST_F(FDimTest, NaNArg_fdim) { testNaNArg(&__llvm_libc::fdim); } + +TEST_F(FDimTest, InfArg_fdim) { testInfArg(&__llvm_libc::fdim); } + +TEST_F(FDimTest, NegInfArg_fdim) { testNegInfArg(&__llvm_libc::fdim); } + +TEST_F(FDimTest, BothZero_fdim) { testBothZero(&__llvm_libc::fdim); } + +TEST_F(FDimTest, InDoubleRange_fdim) { testInRange(&__llvm_libc::fdim); } diff --git a/libc/test/src/math/fdimf_test.cpp b/libc/test/src/math/fdimf_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/fdimf_test.cpp @@ -0,0 +1,27 @@ +//===-- Unittests for fdimf -----------------------------------------------===// +// +// 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 "FDimTest.h" + +#include "include/math.h" +#include "src/math/fdimf.h" +#include "utils/FPUtil/FPBits.h" +#include "utils/FPUtil/TestHelpers.h" +#include "utils/UnitTest/Test.h" + +using FDimTest = FDimTestTemplate; + +TEST_F(FDimTest, NaNArg_fdimf) { testNaNArg(&__llvm_libc::fdimf); } + +TEST_F(FDimTest, InfArg_fdimf) { testInfArg(&__llvm_libc::fdimf); } + +TEST_F(FDimTest, NegInfArg_fdimf) { testNegInfArg(&__llvm_libc::fdimf); } + +TEST_F(FDimTest, BothZero_fdimf) { testBothZero(&__llvm_libc::fdimf); } + +TEST_F(FDimTest, InFloatRange_fdimf) { testInRange(&__llvm_libc::fdimf); } diff --git a/libc/test/src/math/fdiml_test.cpp b/libc/test/src/math/fdiml_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/fdiml_test.cpp @@ -0,0 +1,27 @@ +//===-- Unittests for fdiml -----------------------------------------------===// +// +// 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 "FDimTest.h" + +#include "include/math.h" +#include "src/math/fdiml.h" +#include "utils/FPUtil/FPBits.h" +#include "utils/FPUtil/TestHelpers.h" +#include "utils/UnitTest/Test.h" + +using FDimTest = FDimTestTemplate; + +TEST_F(FDimTest, NaNArg_fdiml) { testNaNArg(&__llvm_libc::fdiml); } + +TEST_F(FDimTest, InfArg_fdiml) { testInfArg(&__llvm_libc::fdiml); } + +TEST_F(FDimTest, NegInfArg_fdiml) { testNegInfArg(&__llvm_libc::fdiml); } + +TEST_F(FDimTest, BothZero_fdiml) { testBothZero(&__llvm_libc::fdiml); } + +TEST_F(FDimTest, InLongDoubleRange_fdiml) { testInRange(&__llvm_libc::fdiml); } diff --git a/libc/utils/FPUtil/BasicOperations.h b/libc/utils/FPUtil/BasicOperations.h --- a/libc/utils/FPUtil/BasicOperations.h +++ b/libc/utils/FPUtil/BasicOperations.h @@ -62,6 +62,22 @@ } } +template ::Value, int> = 0> +static inline T fdim(T x, T y) { + FPBits bitx(x), bity(y); + + if (bitx.isNaN()) { + return x; + } + + if (bity.isNaN()) { + return y; + } + + return (x > y ? x - y : 0); +} + } // namespace fputil } // namespace __llvm_libc