[libc] [llvm] [libc][math] Implement nexttoward functions (PR #72763)

Nishant Mittal via llvm-commits llvm-commits at lists.llvm.org
Tue Nov 21 02:02:45 PST 2023


https://github.com/nishantwrp updated https://github.com/llvm/llvm-project/pull/72763

>From 6a9f4c4d716b27d65d07323796f3bcbc4333ebfe Mon Sep 17 00:00:00 2001
From: Nishant Mittal <nishantwrp at google.com>
Date: Sat, 18 Nov 2023 21:41:34 +0000
Subject: [PATCH 1/2] [libc][math] Implement nexttoward functions

---
 libc/config/darwin/arm/entrypoints.txt        |   3 +
 libc/config/darwin/x86_64/entrypoints.txt     |   3 +
 libc/config/gpu/entrypoints.txt               |   2 +
 libc/config/linux/aarch64/entrypoints.txt     |   3 +
 libc/config/linux/riscv/entrypoints.txt       |   3 +
 libc/config/linux/x86_64/entrypoints.txt      |   3 +
 libc/config/windows/entrypoints.txt           |   3 +
 libc/spec/stdc.td                             |   4 +
 .../__support/FPUtil/ManipulationFunctions.h  |  45 +++-
 .../FPUtil/x86_64/NextAfterLongDouble.h       |  10 +-
 libc/src/math/CMakeLists.txt                  |   4 +
 libc/src/math/generic/CMakeLists.txt          |  36 +++
 libc/src/math/generic/nexttoward.cpp          |  19 ++
 libc/src/math/generic/nexttowardf.cpp         |  19 ++
 libc/src/math/generic/nexttowardl.cpp         |  22 ++
 libc/src/math/nexttoward.h                    |  18 ++
 libc/src/math/nexttowardf.h                   |  18 ++
 libc/src/math/nexttowardl.h                   |  18 ++
 libc/test/src/math/smoke/CMakeLists.txt       |  48 ++++
 libc/test/src/math/smoke/NextTowardTest.h     | 223 ++++++++++++++++++
 libc/test/src/math/smoke/nexttoward_test.cpp  |  13 +
 libc/test/src/math/smoke/nexttowardf_test.cpp |  13 +
 libc/test/src/math/smoke/nexttowardl_test.cpp |  13 +
 23 files changed, 541 insertions(+), 2 deletions(-)
 create mode 100644 libc/src/math/generic/nexttoward.cpp
 create mode 100644 libc/src/math/generic/nexttowardf.cpp
 create mode 100644 libc/src/math/generic/nexttowardl.cpp
 create mode 100644 libc/src/math/nexttoward.h
 create mode 100644 libc/src/math/nexttowardf.h
 create mode 100644 libc/src/math/nexttowardl.h
 create mode 100644 libc/test/src/math/smoke/NextTowardTest.h
 create mode 100644 libc/test/src/math/smoke/nexttoward_test.cpp
 create mode 100644 libc/test/src/math/smoke/nexttowardf_test.cpp
 create mode 100644 libc/test/src/math/smoke/nexttowardl_test.cpp

diff --git a/libc/config/darwin/arm/entrypoints.txt b/libc/config/darwin/arm/entrypoints.txt
index 362138c92a6869f..da4a2345f389c49 100644
--- a/libc/config/darwin/arm/entrypoints.txt
+++ b/libc/config/darwin/arm/entrypoints.txt
@@ -199,6 +199,9 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.nextafter
     libc.src.math.nextafterf
     libc.src.math.nextafterl
+    libc.src.math.nexttoward
+    libc.src.math.nexttowardf
+    libc.src.math.nexttowardl
     libc.src.math.powf
     libc.src.math.remainderf
     libc.src.math.remainder
diff --git a/libc/config/darwin/x86_64/entrypoints.txt b/libc/config/darwin/x86_64/entrypoints.txt
index 57d21a2ce273574..e29e75ec92dadcd 100644
--- a/libc/config/darwin/x86_64/entrypoints.txt
+++ b/libc/config/darwin/x86_64/entrypoints.txt
@@ -178,6 +178,9 @@ set(TARGET_LIBM_ENTRYPOINTS
     #libc.src.math.nextafter
     #libc.src.math.nextafterf
     #libc.src.math.nextafterl
+    #libc.src.math.nexttoward
+    #libc.src.math.nexttowardf
+    #libc.src.math.nexttowardl
     #libc.src.math.remainderf
     #libc.src.math.remainder
     #libc.src.math.remainderl
diff --git a/libc/config/gpu/entrypoints.txt b/libc/config/gpu/entrypoints.txt
index 5dda6aa71d36f81..ba86e31ee0adcd1 100644
--- a/libc/config/gpu/entrypoints.txt
+++ b/libc/config/gpu/entrypoints.txt
@@ -219,6 +219,8 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.nearbyintf
     libc.src.math.nextafter
     libc.src.math.nextafterf
+    libc.src.math.nexttoward
+    libc.src.math.nexttowardf
     libc.src.math.pow
     libc.src.math.powf
     libc.src.math.remainder
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index 096e2afb06e5a9d..284feb7b99096ec 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -316,6 +316,9 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.nextafter
     libc.src.math.nextafterf
     libc.src.math.nextafterl
+    libc.src.math.nexttoward
+    libc.src.math.nexttowardf
+    libc.src.math.nexttowardl
     libc.src.math.powf
     libc.src.math.remainderf
     libc.src.math.remainder
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 7dadbc7f8285477..a5f0c91e32d0810 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -325,6 +325,9 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.nextafter
     libc.src.math.nextafterf
     libc.src.math.nextafterl
+    libc.src.math.nexttoward
+    libc.src.math.nexttowardf
+    libc.src.math.nexttowardl
     libc.src.math.powf
     libc.src.math.remainderf
     libc.src.math.remainder
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 83bc8461999ed66..63aa7473115a08e 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -329,6 +329,9 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.nextafter
     libc.src.math.nextafterf
     libc.src.math.nextafterl
+    libc.src.math.nexttoward
+    libc.src.math.nexttowardf
+    libc.src.math.nexttowardl
     libc.src.math.powf
     libc.src.math.remainderf
     libc.src.math.remainder
diff --git a/libc/config/windows/entrypoints.txt b/libc/config/windows/entrypoints.txt
index 5611b399e8bca8b..4c0a6ec37fe4cc2 100644
--- a/libc/config/windows/entrypoints.txt
+++ b/libc/config/windows/entrypoints.txt
@@ -198,6 +198,9 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.nextafter
     libc.src.math.nextafterf
     libc.src.math.nextafterl
+    libc.src.math.nexttoward
+    libc.src.math.nexttowardf
+    libc.src.math.nexttowardl
     libc.src.math.powf
     libc.src.math.remainderf
     libc.src.math.remainder
diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td
index 4e38cb742d2844e..58c95b856535a12 100644
--- a/libc/spec/stdc.td
+++ b/libc/spec/stdc.td
@@ -492,6 +492,10 @@ def StdC : StandardSpec<"stdc"> {
           FunctionSpec<"nextafter", RetValSpec<DoubleType>, [ArgSpec<DoubleType>, ArgSpec<DoubleType>]>,
           FunctionSpec<"nextafterl", RetValSpec<LongDoubleType>, [ArgSpec<LongDoubleType>, ArgSpec<LongDoubleType>]>,
 
+          FunctionSpec<"nexttowardf", RetValSpec<FloatType>, [ArgSpec<FloatType>, ArgSpec<LongDoubleType>]>,
+          FunctionSpec<"nexttoward", RetValSpec<DoubleType>, [ArgSpec<DoubleType>, ArgSpec<LongDoubleType>]>,
+          FunctionSpec<"nexttowardl", RetValSpec<LongDoubleType>, [ArgSpec<LongDoubleType>, ArgSpec<LongDoubleType>]>,
+
           FunctionSpec<"powf", RetValSpec<FloatType>, [ArgSpec<FloatType>, ArgSpec<FloatType>]>,
           FunctionSpec<"pow", RetValSpec<DoubleType>, [ArgSpec<DoubleType>, ArgSpec<DoubleType>]>,
 
diff --git a/libc/src/__support/FPUtil/ManipulationFunctions.h b/libc/src/__support/FPUtil/ManipulationFunctions.h
index 6d62a2d0fcdf2be..ff131195528ddb0 100644
--- a/libc/src/__support/FPUtil/ManipulationFunctions.h
+++ b/libc/src/__support/FPUtil/ManipulationFunctions.h
@@ -10,6 +10,7 @@
 #define LLVM_LIBC_SRC___SUPPORT_FPUTIL_MANIPULATIONFUNCTIONS_H
 
 #include "FPBits.h"
+#include "FloatProperties.h"
 #include "NearestIntegerOperations.h"
 #include "NormalFloat.h"
 #include "PlatformDefs.h"
@@ -19,6 +20,7 @@
 #include "src/__support/macros/attributes.h"
 #include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
 
+#include <fenv.h>
 #include <limits.h>
 #include <math.h>
 
@@ -169,8 +171,49 @@ LIBC_INLINE T nextafter(T from, T to) {
     int_val = (to_bits.uintval() & sign_mask) + UIntType(1);
   }
 
+  UIntType exponent_bits = int_val & FloatProperties<T>::EXPONENT_MASK;
+  if (exponent_bits == UIntType(0))
+    raise_except_if_required(FE_UNDERFLOW | FE_INEXACT);
+  else if (exponent_bits == FloatProperties<T>::EXPONENT_MASK)
+    raise_except_if_required(FE_OVERFLOW | FE_INEXACT);
+
+  return cpp::bit_cast<T>(int_val);
+}
+
+template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
+LIBC_INLINE T nexttoward(T from, long double to) {
+  FPBits<T> from_bits(from);
+  if (from_bits.is_nan())
+    return from;
+
+  FPBits<long double> to_bits(to);
+  if (to_bits.is_nan())
+    return to;
+
+  if ((long double)from == to)
+    return to;
+
+  using UIntType = typename FPBits<T>::UIntType;
+  UIntType int_val = from_bits.uintval();
+  if (from != T(0.0)) {
+    if ((from < to) == (from > T(0.0))) {
+      ++int_val;
+    } else {
+      --int_val;
+    }
+  } else {
+    int_val = FPBits<T>::MIN_SUBNORMAL;
+    if (to_bits.get_sign())
+      int_val |= FloatProperties<T>::SIGN_MASK;
+  }
+
+  UIntType exponent_bits = int_val & FloatProperties<T>::EXPONENT_MASK;
+  if (exponent_bits == UIntType(0))
+    raise_except_if_required(FE_UNDERFLOW | FE_INEXACT);
+  else if (exponent_bits == FloatProperties<T>::EXPONENT_MASK)
+    raise_except_if_required(FE_OVERFLOW | FE_INEXACT);
+
   return cpp::bit_cast<T>(int_val);
-  // TODO: Raise floating point exceptions as required by the standard.
 }
 
 } // namespace fputil
diff --git a/libc/src/__support/FPUtil/x86_64/NextAfterLongDouble.h b/libc/src/__support/FPUtil/x86_64/NextAfterLongDouble.h
index 52ef2568c1dce06..354739bab1b1998 100644
--- a/libc/src/__support/FPUtil/x86_64/NextAfterLongDouble.h
+++ b/libc/src/__support/FPUtil/x86_64/NextAfterLongDouble.h
@@ -59,6 +59,8 @@ LIBC_INLINE long double nextafter(long double from, long double to) {
         // which is what is expected. Since NaNs are handling separately,
         // it will never overflow "beyond" infinity.
         from_bits.set_unbiased_exponent(from_bits.get_unbiased_exponent() + 1);
+        if (from_bits.is_inf())
+          raise_except_if_required(FE_OVERFLOW | FE_INEXACT);
         return from_bits;
       } else {
         ++int_val;
@@ -105,6 +107,8 @@ LIBC_INLINE long double nextafter(long double from, long double to) {
         // which is what is expected. Since NaNs are handling separately,
         // it will never overflow "beyond" infinity.
         from_bits.set_unbiased_exponent(from_bits.get_unbiased_exponent() + 1);
+        if (from_bits.is_inf())
+          raise_except_if_required(FE_OVERFLOW | FE_INEXACT);
         return from_bits;
       } else {
         ++int_val;
@@ -112,8 +116,12 @@ LIBC_INLINE long double nextafter(long double from, long double to) {
     }
   }
 
+  UIntType implicit_bit =
+      int_val & (UIntType(1) << MantissaWidth<long double>::VALUE);
+  if (implicit_bit == UIntType(0))
+    raise_except_if_required(FE_UNDERFLOW | FE_INEXACT);
+
   return cpp::bit_cast<long double>(int_val);
-  // TODO: Raise floating point exceptions as required by the standard.
 }
 
 } // namespace fputil
diff --git a/libc/src/math/CMakeLists.txt b/libc/src/math/CMakeLists.txt
index 4aea54c7a457547..ffabc27bc00abc7 100644
--- a/libc/src/math/CMakeLists.txt
+++ b/libc/src/math/CMakeLists.txt
@@ -187,6 +187,10 @@ add_math_entrypoint_object(nextafter)
 add_math_entrypoint_object(nextafterf)
 add_math_entrypoint_object(nextafterl)
 
+add_math_entrypoint_object(nexttoward)
+add_math_entrypoint_object(nexttowardf)
+add_math_entrypoint_object(nexttowardl)
+
 add_math_entrypoint_object(pow)
 add_math_entrypoint_object(powf)
 
diff --git a/libc/src/math/generic/CMakeLists.txt b/libc/src/math/generic/CMakeLists.txt
index 7ac365f55bb6f83..da59e74a8e822a8 100644
--- a/libc/src/math/generic/CMakeLists.txt
+++ b/libc/src/math/generic/CMakeLists.txt
@@ -1474,6 +1474,42 @@ add_entrypoint_object(
     -O2
 )
 
+add_entrypoint_object(
+  nexttoward
+  SRCS
+    nexttoward.cpp
+  HDRS
+    ../nexttoward.h
+  DEPENDS
+    libc.src.__support.FPUtil.manipulation_functions
+  COMPILE_OPTIONS
+    -O2
+)
+
+add_entrypoint_object(
+  nexttowardf
+  SRCS
+    nexttowardf.cpp
+  HDRS
+    ../nexttowardf.h
+  DEPENDS
+    libc.src.__support.FPUtil.manipulation_functions
+  COMPILE_OPTIONS
+    -O2
+)
+
+add_entrypoint_object(
+  nexttowardl
+  SRCS
+    nexttowardl.cpp
+  HDRS
+    ../nexttowardl.h
+  DEPENDS
+    libc.src.__support.FPUtil.manipulation_functions
+  COMPILE_OPTIONS
+    -O2
+)
+
 add_entrypoint_object(
   fmod
   SRCS
diff --git a/libc/src/math/generic/nexttoward.cpp b/libc/src/math/generic/nexttoward.cpp
new file mode 100644
index 000000000000000..38b45d6d2f65d9c
--- /dev/null
+++ b/libc/src/math/generic/nexttoward.cpp
@@ -0,0 +1,19 @@
+//===-- Implementation of nexttoward 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/nexttoward.h"
+#include "src/__support/FPUtil/ManipulationFunctions.h"
+#include "src/__support/common.h"
+
+namespace LIBC_NAMESPACE {
+
+LLVM_LIBC_FUNCTION(double, nexttoward, (double x, long double y)) {
+  return fputil::nexttoward(x, y);
+}
+
+} // namespace LIBC_NAMESPACE
diff --git a/libc/src/math/generic/nexttowardf.cpp b/libc/src/math/generic/nexttowardf.cpp
new file mode 100644
index 000000000000000..59a9f805a6946a1
--- /dev/null
+++ b/libc/src/math/generic/nexttowardf.cpp
@@ -0,0 +1,19 @@
+//===-- Implementation of nexttowardf 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/nexttowardf.h"
+#include "src/__support/FPUtil/ManipulationFunctions.h"
+#include "src/__support/common.h"
+
+namespace LIBC_NAMESPACE {
+
+LLVM_LIBC_FUNCTION(float, nexttowardf, (float x, long double y)) {
+  return fputil::nexttoward(x, y);
+}
+
+} // namespace LIBC_NAMESPACE
diff --git a/libc/src/math/generic/nexttowardl.cpp b/libc/src/math/generic/nexttowardl.cpp
new file mode 100644
index 000000000000000..0c887ae0671bc98
--- /dev/null
+++ b/libc/src/math/generic/nexttowardl.cpp
@@ -0,0 +1,22 @@
+//===-- Implementation of nexttowardl 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/nexttowardl.h"
+#include "src/__support/FPUtil/ManipulationFunctions.h"
+#include "src/__support/common.h"
+
+namespace LIBC_NAMESPACE {
+
+LLVM_LIBC_FUNCTION(long double, nexttowardl, (long double x, long double y)) {
+  // We can reuse the nextafter implementation because nexttoward behaves
+  // exactly same as nextafter in case of long doubles. Also, we have explcitly
+  // handled the special 80-bit long doubles in nextafter implementation.
+  return fputil::nextafter(x, y);
+}
+
+} // namespace LIBC_NAMESPACE
diff --git a/libc/src/math/nexttoward.h b/libc/src/math/nexttoward.h
new file mode 100644
index 000000000000000..6a5bece9957689c
--- /dev/null
+++ b/libc/src/math/nexttoward.h
@@ -0,0 +1,18 @@
+//===-- Implementation header for nexttoward --------------------*- 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_NEXTTOWARD_H
+#define LLVM_LIBC_SRC_MATH_NEXTTOWARD_H
+
+namespace LIBC_NAMESPACE {
+
+double nexttoward(double x, long double y);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_MATH_NEXTTOWARD_H
diff --git a/libc/src/math/nexttowardf.h b/libc/src/math/nexttowardf.h
new file mode 100644
index 000000000000000..7a0eb2a61b4d4b2
--- /dev/null
+++ b/libc/src/math/nexttowardf.h
@@ -0,0 +1,18 @@
+//===-- Implementation header for nexttowardf -------------------*- 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_NEXTTOWARDF_H
+#define LLVM_LIBC_SRC_MATH_NEXTTOWARDF_H
+
+namespace LIBC_NAMESPACE {
+
+float nexttowardf(float x, long double y);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_MATH_NEXTTOWARDF_H
diff --git a/libc/src/math/nexttowardl.h b/libc/src/math/nexttowardl.h
new file mode 100644
index 000000000000000..be1d8b298185d61
--- /dev/null
+++ b/libc/src/math/nexttowardl.h
@@ -0,0 +1,18 @@
+//===-- Implementation header for nexttowardl -------------------*- 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_NEXTTOWARDL_H
+#define LLVM_LIBC_SRC_MATH_NEXTTOWARDL_H
+
+namespace LIBC_NAMESPACE {
+
+long double nexttowardl(long double x, long double y);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_MATH_NEXTTOWARDL_H
diff --git a/libc/test/src/math/smoke/CMakeLists.txt b/libc/test/src/math/smoke/CMakeLists.txt
index 101bdac995355de..298a1b1a400f1d8 100644
--- a/libc/test/src/math/smoke/CMakeLists.txt
+++ b/libc/test/src/math/smoke/CMakeLists.txt
@@ -1251,6 +1251,54 @@ add_fp_unittest(
     libc.src.__support.FPUtil.fp_bits
 )
 
+# FIXME: These tests are currently spurious for NVPTX.
+if(NOT LIBC_GPU_TARGET_ARCHITECTURE_IS_NVPTX)
+  add_fp_unittest(
+    nexttoward_test
+    SUITE
+      libc-math-smoke-tests
+    SRCS
+      nexttoward_test.cpp
+    HDRS
+      NextTowardTest.h
+    DEPENDS
+      libc.include.math
+      libc.src.math.nexttoward
+      libc.src.__support.FPUtil.basic_operations
+      libc.src.__support.FPUtil.fp_bits
+  )
+
+  add_fp_unittest(
+    nexttowardf_test
+    SUITE
+      libc-math-smoke-tests
+    SRCS
+      nexttowardf_test.cpp
+    HDRS
+      NextTowardTest.h
+    DEPENDS
+      libc.include.math
+      libc.src.math.nexttowardf
+      libc.src.__support.FPUtil.basic_operations
+      libc.src.__support.FPUtil.fp_bits
+  )
+endif()
+
+add_fp_unittest(
+  nexttowardl_test
+  SUITE
+    libc-math-smoke-tests
+  SRCS
+    nexttowardl_test.cpp
+  HDRS
+    NextTowardTest.h
+  DEPENDS
+    libc.include.math
+    libc.src.math.nexttowardl
+    libc.src.__support.FPUtil.basic_operations
+    libc.src.__support.FPUtil.fp_bits
+)
+
 # TODO(lntue): The current implementation of fputil::general::fma<float> is only
 # correctly rounded for the default rounding mode round-to-nearest tie-to-even.
 add_fp_unittest(
diff --git a/libc/test/src/math/smoke/NextTowardTest.h b/libc/test/src/math/smoke/NextTowardTest.h
new file mode 100644
index 000000000000000..4d27592a11422bf
--- /dev/null
+++ b/libc/test/src/math/smoke/NextTowardTest.h
@@ -0,0 +1,223 @@
+//===-- Utility class to test different flavors of nexttoward ---*- 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_NEXTTOWARDTEST_H
+#define LLVM_LIBC_TEST_SRC_MATH_NEXTTOWARDTEST_H
+
+#include "src/__support/CPP/bit.h"
+#include "src/__support/CPP/type_traits.h"
+#include "src/__support/FPUtil/BasicOperations.h"
+#include "src/__support/FPUtil/FPBits.h"
+#include "test/UnitTest/FPMatcher.h"
+#include "test/UnitTest/Test.h"
+#include <fenv.h>
+#include <math.h>
+
+#define ASSERT_FP_EQ_WITH_EXCEPTION(result, expected, expected_exception)      \
+  ASSERT_FP_EQ(result, expected);                                              \
+  ASSERT_FP_EXCEPTION(expected_exception);                                     \
+  LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT)
+
+#define ASSERT_FP_EQ_WITH_UNDERFLOW(result, expected)                          \
+  ASSERT_FP_EQ_WITH_EXCEPTION(result, expected, FE_INEXACT | FE_UNDERFLOW)
+
+#define ASSERT_FP_EQ_WITH_OVERFLOW(result, expected)                           \
+  ASSERT_FP_EQ_WITH_EXCEPTION(result, expected, FE_INEXACT | FE_OVERFLOW)
+
+template <typename T>
+class NextTowardTestTemplate : public LIBC_NAMESPACE::testing::Test {
+  using FPBits = LIBC_NAMESPACE::fputil::FPBits<T>;
+  using ToFPBits = LIBC_NAMESPACE::fputil::FPBits<long double>;
+  using MantissaWidth = LIBC_NAMESPACE::fputil::MantissaWidth<T>;
+  using UIntType = typename FPBits::UIntType;
+
+  static constexpr int BIT_WIDTH_OF_TYPE =
+      LIBC_NAMESPACE::fputil::FloatProperties<T>::BIT_WIDTH;
+
+  const T zero = T(FPBits::zero());
+  const T neg_zero = T(FPBits::neg_zero());
+  const T inf = T(FPBits::inf());
+  const T neg_inf = T(FPBits::neg_inf());
+  const T nan = T(FPBits::build_quiet_nan(1));
+
+  const long double to_zero = ToFPBits::zero();
+  const long double to_neg_zero = ToFPBits::neg_zero();
+  const long double to_nan = ToFPBits::build_quiet_nan(1);
+
+  const UIntType min_subnormal = FPBits::MIN_SUBNORMAL;
+  const UIntType max_subnormal = FPBits::MAX_SUBNORMAL;
+  const UIntType min_normal = FPBits::MIN_NORMAL;
+  const UIntType max_normal = FPBits::MAX_NORMAL;
+
+public:
+  typedef T (*NextTowardFunc)(T, long double);
+
+  void testNaN(NextTowardFunc func) {
+    ASSERT_FP_EQ(func(nan, 0), nan);
+    ASSERT_FP_EQ(func(0, to_nan), nan);
+  }
+
+  void testBoundaries(NextTowardFunc func) {
+    ASSERT_FP_EQ(func(zero, to_neg_zero), neg_zero);
+    ASSERT_FP_EQ(func(neg_zero, to_zero), zero);
+
+    // 'from' is zero|neg_zero.
+    T x = zero;
+    T result = func(x, 1);
+    UIntType expected_bits = 1;
+    T expected = LIBC_NAMESPACE::cpp::bit_cast<T>(expected_bits);
+    ASSERT_FP_EQ_WITH_UNDERFLOW(result, expected);
+
+    result = func(x, -1);
+    expected_bits = (UIntType(1) << (BIT_WIDTH_OF_TYPE - 1)) + 1;
+    expected = LIBC_NAMESPACE::cpp::bit_cast<T>(expected_bits);
+    ASSERT_FP_EQ_WITH_UNDERFLOW(result, expected);
+
+    x = neg_zero;
+    result = func(x, 1);
+    expected_bits = 1;
+    expected = LIBC_NAMESPACE::cpp::bit_cast<T>(expected_bits);
+    ASSERT_FP_EQ_WITH_UNDERFLOW(result, expected);
+
+    result = func(x, -1);
+    expected_bits = (UIntType(1) << (BIT_WIDTH_OF_TYPE - 1)) + 1;
+    expected = LIBC_NAMESPACE::cpp::bit_cast<T>(expected_bits);
+    ASSERT_FP_EQ_WITH_UNDERFLOW(result, expected);
+
+    // 'from' is max subnormal value.
+    x = LIBC_NAMESPACE::cpp::bit_cast<T>(max_subnormal);
+    result = func(x, 1);
+    expected = LIBC_NAMESPACE::cpp::bit_cast<T>(min_normal);
+    ASSERT_FP_EQ(result, expected);
+
+    result = func(x, 0);
+    expected_bits = max_subnormal - 1;
+    expected = LIBC_NAMESPACE::cpp::bit_cast<T>(expected_bits);
+    ASSERT_FP_EQ_WITH_UNDERFLOW(result, expected);
+
+    x = -x;
+
+    result = func(x, -1);
+    expected_bits = (UIntType(1) << (BIT_WIDTH_OF_TYPE - 1)) + min_normal;
+    expected = LIBC_NAMESPACE::cpp::bit_cast<T>(expected_bits);
+    ASSERT_FP_EQ(result, expected);
+
+    result = func(x, 0);
+    expected_bits =
+        (UIntType(1) << (BIT_WIDTH_OF_TYPE - 1)) + max_subnormal - 1;
+    expected = LIBC_NAMESPACE::cpp::bit_cast<T>(expected_bits);
+    ASSERT_FP_EQ_WITH_UNDERFLOW(result, expected);
+
+    // 'from' is min subnormal value.
+    x = LIBC_NAMESPACE::cpp::bit_cast<T>(min_subnormal);
+    result = func(x, 1);
+    expected_bits = min_subnormal + 1;
+    expected = LIBC_NAMESPACE::cpp::bit_cast<T>(expected_bits);
+    ASSERT_FP_EQ_WITH_UNDERFLOW(result, expected);
+    ASSERT_FP_EQ_WITH_UNDERFLOW(func(x, 0), 0);
+
+    x = -x;
+    result = func(x, -1);
+    expected_bits =
+        (UIntType(1) << (BIT_WIDTH_OF_TYPE - 1)) + min_subnormal + 1;
+    expected = LIBC_NAMESPACE::cpp::bit_cast<T>(expected_bits);
+    ASSERT_FP_EQ_WITH_UNDERFLOW(result, expected);
+    ASSERT_FP_EQ_WITH_UNDERFLOW(func(x, 0), T(-0.0));
+
+    // 'from' is min normal.
+    x = LIBC_NAMESPACE::cpp::bit_cast<T>(min_normal);
+    result = func(x, 0);
+    expected_bits = max_subnormal;
+    expected = LIBC_NAMESPACE::cpp::bit_cast<T>(expected_bits);
+    ASSERT_FP_EQ_WITH_UNDERFLOW(result, expected);
+
+    result = func(x, inf);
+    expected_bits = min_normal + 1;
+    expected = LIBC_NAMESPACE::cpp::bit_cast<T>(expected_bits);
+    ASSERT_FP_EQ(result, expected);
+
+    x = -x;
+    result = func(x, 0);
+    expected_bits = (UIntType(1) << (BIT_WIDTH_OF_TYPE - 1)) + max_subnormal;
+    expected = LIBC_NAMESPACE::cpp::bit_cast<T>(expected_bits);
+    ASSERT_FP_EQ_WITH_UNDERFLOW(result, expected);
+
+    result = func(x, -inf);
+    expected_bits = (UIntType(1) << (BIT_WIDTH_OF_TYPE - 1)) + min_normal + 1;
+    expected = LIBC_NAMESPACE::cpp::bit_cast<T>(expected_bits);
+    ASSERT_FP_EQ(result, expected);
+
+    // 'from' is max normal
+    x = LIBC_NAMESPACE::cpp::bit_cast<T>(max_normal);
+    result = func(x, 0);
+    expected_bits = max_normal - 1;
+    expected = LIBC_NAMESPACE::cpp::bit_cast<T>(expected_bits);
+    ASSERT_FP_EQ(result, expected);
+    ASSERT_FP_EQ_WITH_OVERFLOW(func(x, inf), inf);
+
+    x = -x;
+    result = func(x, 0);
+    expected_bits = (UIntType(1) << (BIT_WIDTH_OF_TYPE - 1)) + max_normal - 1;
+    expected = LIBC_NAMESPACE::cpp::bit_cast<T>(expected_bits);
+    ASSERT_FP_EQ(result, expected);
+    ASSERT_FP_EQ_WITH_OVERFLOW(func(x, -inf), -inf);
+
+    // 'from' is infinity.
+    x = inf;
+    result = func(x, 0);
+    expected_bits = max_normal;
+    expected = LIBC_NAMESPACE::cpp::bit_cast<T>(expected_bits);
+    ASSERT_FP_EQ(result, expected);
+    ASSERT_FP_EQ(func(x, inf), inf);
+
+    x = neg_inf;
+    result = func(x, 0);
+    expected_bits = (UIntType(1) << (BIT_WIDTH_OF_TYPE - 1)) + max_normal;
+    expected = LIBC_NAMESPACE::cpp::bit_cast<T>(expected_bits);
+    ASSERT_FP_EQ(result, expected);
+    ASSERT_FP_EQ(func(x, neg_inf), neg_inf);
+
+    // 'from' is a power of 2.
+    x = T(32.0);
+    result = func(x, 0);
+    FPBits x_bits = FPBits(x);
+    FPBits result_bits = FPBits(result);
+    ASSERT_EQ(result_bits.get_unbiased_exponent(),
+              uint16_t(x_bits.get_unbiased_exponent() - 1));
+    ASSERT_EQ(result_bits.get_mantissa(),
+              (UIntType(1) << MantissaWidth::VALUE) - 1);
+
+    result = func(x, 33.0);
+    result_bits = FPBits(result);
+    ASSERT_EQ(result_bits.get_unbiased_exponent(),
+              x_bits.get_unbiased_exponent());
+    ASSERT_EQ(result_bits.get_mantissa(), x_bits.get_mantissa() + UIntType(1));
+
+    x = -x;
+
+    result = func(x, 0);
+    result_bits = FPBits(result);
+    ASSERT_EQ(result_bits.get_unbiased_exponent(),
+              uint16_t(x_bits.get_unbiased_exponent() - 1));
+    ASSERT_EQ(result_bits.get_mantissa(),
+              (UIntType(1) << MantissaWidth::VALUE) - 1);
+
+    result = func(x, -33.0);
+    result_bits = FPBits(result);
+    ASSERT_EQ(result_bits.get_unbiased_exponent(),
+              x_bits.get_unbiased_exponent());
+    ASSERT_EQ(result_bits.get_mantissa(), x_bits.get_mantissa() + UIntType(1));
+  }
+};
+
+#define LIST_NEXTTOWARD_TESTS(T, func)                                         \
+  using LlvmLibcNextTowardTest = NextTowardTestTemplate<T>;                    \
+  TEST_F(LlvmLibcNextTowardTest, TestNaN) { testNaN(&func); }                  \
+  TEST_F(LlvmLibcNextTowardTest, TestBoundaries) { testBoundaries(&func); }
+
+#endif // LLVM_LIBC_TEST_SRC_MATH_NEXTTOWARDTEST_H
diff --git a/libc/test/src/math/smoke/nexttoward_test.cpp b/libc/test/src/math/smoke/nexttoward_test.cpp
new file mode 100644
index 000000000000000..774c79efd747a34
--- /dev/null
+++ b/libc/test/src/math/smoke/nexttoward_test.cpp
@@ -0,0 +1,13 @@
+//===-- Unittests for nexttoward ------------------------------------------===//
+//
+// 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 "NextTowardTest.h"
+
+#include "src/math/nexttoward.h"
+
+LIST_NEXTTOWARD_TESTS(double, LIBC_NAMESPACE::nexttoward)
diff --git a/libc/test/src/math/smoke/nexttowardf_test.cpp b/libc/test/src/math/smoke/nexttowardf_test.cpp
new file mode 100644
index 000000000000000..cb6c5f9ab15f582
--- /dev/null
+++ b/libc/test/src/math/smoke/nexttowardf_test.cpp
@@ -0,0 +1,13 @@
+//===-- Unittests for nexttowardf -----------------------------------------===//
+//
+// 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 "NextTowardTest.h"
+
+#include "src/math/nexttowardf.h"
+
+LIST_NEXTTOWARD_TESTS(float, LIBC_NAMESPACE::nexttowardf)
diff --git a/libc/test/src/math/smoke/nexttowardl_test.cpp b/libc/test/src/math/smoke/nexttowardl_test.cpp
new file mode 100644
index 000000000000000..b3ee4f7887027f6
--- /dev/null
+++ b/libc/test/src/math/smoke/nexttowardl_test.cpp
@@ -0,0 +1,13 @@
+//===-- Unittests for nexttowardl -----------------------------------------===//
+//
+// 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 "NextTowardTest.h"
+
+#include "src/math/nexttowardl.h"
+
+LIST_NEXTTOWARD_TESTS(long double, LIBC_NAMESPACE::nexttowardl)

>From 0ada816ebdb7d8d2b3f0384046d83f73b12e028b Mon Sep 17 00:00:00 2001
From: Nishant Mittal <nishantwrp at google.com>
Date: Tue, 21 Nov 2023 09:50:07 +0000
Subject: [PATCH 2/2] Update bazel, progress docs, address review comments

---
 libc/docs/math/index.rst                               | 6 +++---
 libc/src/__support/FPUtil/CMakeLists.txt               | 1 +
 libc/src/__support/FPUtil/ManipulationFunctions.h      | 6 +++---
 libc/src/__support/FPUtil/x86_64/NextAfterLongDouble.h | 1 +
 libc/src/math/generic/CMakeLists.txt                   | 6 +++---
 utils/bazel/llvm-project-overlay/libc/BUILD.bazel      | 6 ++++++
 6 files changed, 17 insertions(+), 9 deletions(-)

diff --git a/libc/docs/math/index.rst b/libc/docs/math/index.rst
index 4af95aec60c0c26..eaa7a40a29ae73c 100644
--- a/libc/docs/math/index.rst
+++ b/libc/docs/math/index.rst
@@ -230,11 +230,11 @@ Basic Operations
 +--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
 | nextafterl   | |check| | |check| |         | |check| | |check| |         |         | |check| |         |         |         |         |
 +--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
-| nexttoward   |         |         |         |         |         |         |         |         |         |         |         |         |
+| nexttoward   | |check| | |check| |         | |check| | |check| |         |         | |check| |         |         |         |         |
 +--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
-| nexttowardf  |         |         |         |         |         |         |         |         |         |         |         |         |
+| nexttowardf  | |check| | |check| |         | |check| | |check| |         |         | |check| |         |         |         |         |
 +--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
-| nexttowardl  |         |         |         |         |         |         |         |         |         |         |         |         |
+| nexttowardl  | |check| | |check| |         | |check| | |check| |         |         | |check| |         |         |         |         |
 +--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
 | remainder    | |check| | |check| |         | |check| | |check| |         |         | |check| |         |         |         |         |
 +--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
diff --git a/libc/src/__support/FPUtil/CMakeLists.txt b/libc/src/__support/FPUtil/CMakeLists.txt
index 6b43a9fa4892fe8..164af4ac3ac5460 100644
--- a/libc/src/__support/FPUtil/CMakeLists.txt
+++ b/libc/src/__support/FPUtil/CMakeLists.txt
@@ -94,6 +94,7 @@ add_header_library(
   HDRS
     ManipulationFunctions.h
   DEPENDS
+    .fenv_impl
     .fp_bits
     .nearest_integer_operations
     .normal_float
diff --git a/libc/src/__support/FPUtil/ManipulationFunctions.h b/libc/src/__support/FPUtil/ManipulationFunctions.h
index ff131195528ddb0..4693c2699ae9ad8 100644
--- a/libc/src/__support/FPUtil/ManipulationFunctions.h
+++ b/libc/src/__support/FPUtil/ManipulationFunctions.h
@@ -19,8 +19,8 @@
 #include "src/__support/CPP/type_traits.h"
 #include "src/__support/macros/attributes.h"
 #include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
+#include "src/__support/FPUtil/FEnvImpl.h"
 
-#include <fenv.h>
 #include <limits.h>
 #include <math.h>
 
@@ -180,8 +180,8 @@ LIBC_INLINE T nextafter(T from, T to) {
   return cpp::bit_cast<T>(int_val);
 }
 
-template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
-LIBC_INLINE T nexttoward(T from, long double to) {
+template <typename T>
+LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, T> nexttoward(T from, long double to) {
   FPBits<T> from_bits(from);
   if (from_bits.is_nan())
     return from;
diff --git a/libc/src/__support/FPUtil/x86_64/NextAfterLongDouble.h b/libc/src/__support/FPUtil/x86_64/NextAfterLongDouble.h
index 354739bab1b1998..ab19d623bf7d1b3 100644
--- a/libc/src/__support/FPUtil/x86_64/NextAfterLongDouble.h
+++ b/libc/src/__support/FPUtil/x86_64/NextAfterLongDouble.h
@@ -17,6 +17,7 @@
 
 #include "src/__support/CPP/bit.h"
 #include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/FPUtil/FEnvImpl.h"
 
 #include <stdint.h>
 
diff --git a/libc/src/math/generic/CMakeLists.txt b/libc/src/math/generic/CMakeLists.txt
index da59e74a8e822a8..413ac049a22b840 100644
--- a/libc/src/math/generic/CMakeLists.txt
+++ b/libc/src/math/generic/CMakeLists.txt
@@ -1483,7 +1483,7 @@ add_entrypoint_object(
   DEPENDS
     libc.src.__support.FPUtil.manipulation_functions
   COMPILE_OPTIONS
-    -O2
+    -O3
 )
 
 add_entrypoint_object(
@@ -1495,7 +1495,7 @@ add_entrypoint_object(
   DEPENDS
     libc.src.__support.FPUtil.manipulation_functions
   COMPILE_OPTIONS
-    -O2
+    -O3
 )
 
 add_entrypoint_object(
@@ -1507,7 +1507,7 @@ add_entrypoint_object(
   DEPENDS
     libc.src.__support.FPUtil.manipulation_functions
   COMPILE_OPTIONS
-    -O2
+    -O3
 )
 
 add_entrypoint_object(
diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
index 486146187d3a629..c89c1f7950b974e 100644
--- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
@@ -1872,6 +1872,12 @@ libc_math_function(name = "nextafterf")
 
 libc_math_function(name = "nextafterl")
 
+libc_math_function(name = "nexttoward")
+
+libc_math_function(name = "nexttowardf")
+
+libc_math_function(name = "nexttowardl")
+
 libc_math_function(name = "scalbn")
 
 libc_math_function(name = "scalbnf")



More information about the llvm-commits mailing list