diff --git a/libc/src/__support/FPUtil/CMakeLists.txt b/libc/src/__support/FPUtil/CMakeLists.txt --- a/libc/src/__support/FPUtil/CMakeLists.txt +++ b/libc/src/__support/FPUtil/CMakeLists.txt @@ -69,4 +69,12 @@ .multiply_add ) +add_header_library( + nearest_integer + HDRS + nearest_integer.h + DEPENDS + libc.src.__support.common +) + add_subdirectory(generic) diff --git a/libc/src/__support/FPUtil/aarch64/nearest_integer.h b/libc/src/__support/FPUtil/aarch64/nearest_integer.h new file mode 100644 --- /dev/null +++ b/libc/src/__support/FPUtil/aarch64/nearest_integer.h @@ -0,0 +1,30 @@ +//===--- Round floating point to nearest integer on aarch64 -----*- 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_AARCH64_NEAREST_INTEGER_H +#define LLVM_LIBC_SRC_SUPPORT_FPUTIL_AARCH64_NEAREST_INTEGER_H + +#include "src/__support/architectures.h" + +#if !defined(LLVM_LIBC_ARCH_AARCH64) +#error "Invalid include" +#endif + +namespace __llvm_libc { +namespace fputil { + +static inline double nearest_integer(double x) { + double result; + __asm__ __volatile__("frintn %d0, %d1\n\t" : "=w"(result) : "w"(x)); + return result; +} + +} // namespace fputil +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_SUPPORT_FPUTIL_AARCH64_NEAREST_INTEGER_H diff --git a/libc/src/__support/FPUtil/nearest_integer.h b/libc/src/__support/FPUtil/nearest_integer.h new file mode 100644 --- /dev/null +++ b/libc/src/__support/FPUtil/nearest_integer.h @@ -0,0 +1,51 @@ +//===-- Fast rounding to nearest integer for floating point -----*- 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_NEAREST_INTEGER_H +#define LLVM_LIBC_SRC_SUPPORT_FPUTIL_NEAREST_INTEGER_H + +#include "src/__support/architectures.h" +#include "src/__support/common.h" + +#if (defined(LLVM_LIBC_ARCH_X86_64) && defined(__SSE4_2__)) +#include "x86_64/nearest_integer.h" +#elif defined(LLVM_LIBC_ARCH_AARCH64) +#include "aarch64/nearest_integer.h" +#else + +namespace __llvm_libc { +namespace fputil { + +// This is a fast implementation for rounding to a nearest integer that, in case +// of a tie, might pick a random one among 2 closest integers when the rounding +// mode is not FE_TONEAREST. +// +// Notice that for AARCH64 and x86-64 with SSE4.2 support, we will use their +// corresponding rounding instruction instead. And in those cases, the results +// are rounded to the nearest integer, tie-to-even. +static inline double nearest_integer(double x) { + if (x < 0x1p53 && x > -0x1p53) { + double r = x < 0 ? (x - 0x1.0p52) + 0x1.0p52 : (x + 0x1.0p52) - 0x1.0p52; + double diff = x - r; + // The expression above is correct for the default rounding mode, round-to- + // nearest, tie-to-even. For other rounding modes, it might be off by 1, + // which is corrected below. + if (unlikely(diff > 0.5)) + return r + 1.0; + if (unlikely(diff < -0.5)) + return r - 1.0; + return r; + } + return x; +} + +} // namespace fputil +} // namespace __llvm_libc + +#endif +#endif // LLVM_LIBC_SRC_SUPPORT_FPUTIL_NEAREST_INTEGER_H diff --git a/libc/src/__support/FPUtil/x86_64/nearest_integer.h b/libc/src/__support/FPUtil/x86_64/nearest_integer.h new file mode 100644 --- /dev/null +++ b/libc/src/__support/FPUtil/x86_64/nearest_integer.h @@ -0,0 +1,37 @@ +//===--- Round floating point to nearest integer on x86-64 ------*- 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_X86_64_NEAREST_INTEGER_H +#define LLVM_LIBC_SRC_SUPPORT_FPUTIL_X86_64_NEAREST_INTEGER_H + +#include "src/__support/architectures.h" + +#if !defined(LLVM_LIBC_ARCH_X86_64) +#error "Invalid include" +#endif + +#if !defined(__SSE4_2__) +#error "SSE4.2 instruction set is not supported" +#endif + +#include + +namespace __llvm_libc { +namespace fputil { + +static inline double nearest_integer(double x) { + __m128d xmm = _mm_set_sd(x); // NOLINT + __m128d ymm = + _mm_round_sd(xmm, xmm, _MM_ROUND_NEAREST | _MM_FROUND_NO_EXC); // NOLINT + return ymm[0]; +} + +} // namespace fputil +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_SUPPORT_FPUTIL_X86_64_NEAREST_INTEGER_H diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel --- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel @@ -263,6 +263,28 @@ ], ) +nearest_integer_common_hdrs = [ + "src/__support/FPUtil/nearest_integer.h", +] + +nearest_integer_platform_hdrs = [ + "src/__support/FPUtil/x86_64/nearest_integer.h", + "src/__support/FPUtil/aarch64/nearest_integer.h", +] + +cc_library( + name = "__support_fputil_nearest_integer", + hdrs = nearest_integer_common_hdrs, + # These are conditionally included and will #error out if the platform + # doesn't support rounding instructions, so they can't be compiled on their + # own. + textual_hdrs = nearest_integer_platform_hdrs, + deps = [ + ":__support_common", + ":libc_root", + ], +) + ################################ fenv targets ################################ libc_function(