[libc-commits] [libc] 1390182 - [libc] Add implementations long double fabsl and truncl functions.

Siva Chandra Reddy via libc-commits libc-commits at lists.llvm.org
Thu Jun 18 11:09:04 PDT 2020


Author: Siva Chandra Reddy
Date: 2020-06-18T11:08:26-07:00
New Revision: 139018265bfecec450261292f24f8bf3c37be96f

URL: https://github.com/llvm/llvm-project/commit/139018265bfecec450261292f24f8bf3c37be96f
DIFF: https://github.com/llvm/llvm-project/commit/139018265bfecec450261292f24f8bf3c37be96f.diff

LOG: [libc] Add implementations long double fabsl and truncl functions.

Current implementations of single precision and double precision
floating point operations operate on bits of the integer type of
same size. The code made use of magic masks which were listed as
literal integer values. This is not possible in the case of long
double type as the mantissa of quad-precision long double type used
on non-x86 architectures is wider that the widest integer type for
which we can list literal values. So, in this patch, to avoid
using magic masks specified with literal values, we use packed
bit-field struct types and let the compiler generate the masks.
This new scheme allows us to implement long double flavors of the
various floating point operations. To keep the size of the patch
small, only the implementations of fabs and trunc have been
switched to the new scheme. In following patches, all exisiting
implementations will be switched to the new scheme.

Reviewers: asteinhauser

Differential Revision: https://reviews.llvm.org/D82036

Added: 
    libc/src/math/fabsl.cpp
    libc/src/math/fabsl.h
    libc/src/math/truncl.cpp
    libc/src/math/truncl.h
    libc/test/src/math/fabsl_test.cpp
    libc/test/src/math/truncl_test.cpp
    libc/utils/FPUtil/FPBits.h
    libc/utils/FPUtil/LongDoubleBitsX86.h

Modified: 
    libc/config/linux/aarch64/entrypoints.txt
    libc/config/linux/api.td
    libc/config/linux/x86_64/entrypoints.txt
    libc/spec/stdc.td
    libc/src/math/CMakeLists.txt
    libc/test/src/math/CMakeLists.txt
    libc/utils/FPUtil/BasicOperations.h
    libc/utils/FPUtil/CMakeLists.txt
    libc/utils/FPUtil/FloatOperations.h
    libc/utils/FPUtil/NearestIntegerOperations.h
    libc/utils/MPFRWrapper/MPFRUtils.cpp

Removed: 
    


################################################################################
diff  --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index 7a615809ed55..2b5b12926cb6 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -22,6 +22,7 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.exp2f
     libc.src.math.fabs
     libc.src.math.fabsf
+    libc.src.math.fabsl
     libc.src.math.floor
     libc.src.math.floorf
     libc.src.math.frexp
@@ -36,4 +37,5 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.sinf
     libc.src.math.trunc
     libc.src.math.truncf
+    libc.src.math.truncl
 )

diff  --git a/libc/config/linux/api.td b/libc/config/linux/api.td
index f2b7050661d3..79fdb15412ca 100644
--- a/libc/config/linux/api.td
+++ b/libc/config/linux/api.td
@@ -157,6 +157,7 @@ def MathAPI : PublicAPI<"math.h"> {
    "cosf",
    "fabs",
    "fabsf",
+   "fabsl",
    "floor",
    "floorf",
    "frexp",
@@ -173,6 +174,7 @@ def MathAPI : PublicAPI<"math.h"> {
    "sinf",
    "trunc",
    "truncf",
+   "truncl",
   ];
 }
 

diff  --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 93b0dfdcb3f3..656ccddfeedf 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -54,6 +54,7 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.exp2f
     libc.src.math.fabs
     libc.src.math.fabsf
+    libc.src.math.fabsl
     libc.src.math.floor
     libc.src.math.floorf
     libc.src.math.frexp
@@ -68,4 +69,5 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.sinf
     libc.src.math.trunc
     libc.src.math.truncf
+    libc.src.math.truncl
 )

diff  --git a/libc/spec/stdc.td b/libc/spec/stdc.td
index b31f89d3d73a..998a63c6a02f 100644
--- a/libc/spec/stdc.td
+++ b/libc/spec/stdc.td
@@ -197,6 +197,7 @@ def StdC : StandardSpec<"stdc"> {
 
           FunctionSpec<"fabs", RetValSpec<DoubleType>, [ArgSpec<DoubleType>]>,
           FunctionSpec<"fabsf", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,
+          FunctionSpec<"fabsl", RetValSpec<LongDoubleType>, [ArgSpec<LongDoubleType>]>,
 
           FunctionSpec<"floor", RetValSpec<DoubleType>, [ArgSpec<DoubleType>]>,
           FunctionSpec<"floorf", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,
@@ -221,6 +222,7 @@ def StdC : StandardSpec<"stdc"> {
 
           FunctionSpec<"trunc", RetValSpec<DoubleType>, [ArgSpec<DoubleType>]>,
           FunctionSpec<"truncf", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,
+          FunctionSpec<"truncl", RetValSpec<LongDoubleType>, [ArgSpec<LongDoubleType>]>,
       ]
   >;
 

diff  --git a/libc/src/math/CMakeLists.txt b/libc/src/math/CMakeLists.txt
index c74e7e4ab5ff..0d673e086097 100644
--- a/libc/src/math/CMakeLists.txt
+++ b/libc/src/math/CMakeLists.txt
@@ -64,6 +64,8 @@ add_entrypoint_object(
     fabs.h
   DEPENDS
     libc.utils.FPUtil.fputil
+  COMPILE_OPTIONS
+    -O2
 )
 
 add_entrypoint_object(
@@ -74,6 +76,20 @@ add_entrypoint_object(
     fabsf.h
   DEPENDS
     libc.utils.FPUtil.fputil
+  COMPILE_OPTIONS
+    -O2
+)
+
+add_entrypoint_object(
+  fabsl
+  SRCS
+    fabsl.cpp
+  HDRS
+    fabsl.h
+  DEPENDS
+    libc.utils.FPUtil.fputil
+  COMPILE_OPTIONS
+    -O2
 )
 
 add_entrypoint_object(
@@ -84,6 +100,8 @@ add_entrypoint_object(
     trunc.h
   DEPENDS
     libc.utils.FPUtil.fputil
+  COMPILE_OPTIONS
+    -O2
 )
 
 add_entrypoint_object(
@@ -94,6 +112,20 @@ add_entrypoint_object(
     truncf.h
   DEPENDS
     libc.utils.FPUtil.fputil
+  COMPILE_OPTIONS
+    -O2
+)
+
+add_entrypoint_object(
+  truncl
+  SRCS
+    truncl.cpp
+  HDRS
+    truncl.h
+  DEPENDS
+    libc.utils.FPUtil.fputil
+  COMPILE_OPTIONS
+    -O2
 )
 
 add_entrypoint_object(
@@ -104,6 +136,8 @@ add_entrypoint_object(
     ceil.h
   DEPENDS
     libc.utils.FPUtil.fputil
+  COMPILE_OPTIONS
+    -O2
 )
 
 add_entrypoint_object(
@@ -114,6 +148,8 @@ add_entrypoint_object(
     ceilf.h
   DEPENDS
     libc.utils.FPUtil.fputil
+  COMPILE_OPTIONS
+    -O2
 )
 
 add_entrypoint_object(
@@ -124,6 +160,8 @@ add_entrypoint_object(
     floor.h
   DEPENDS
     libc.utils.FPUtil.fputil
+  COMPILE_OPTIONS
+    -O2
 )
 
 add_entrypoint_object(
@@ -134,6 +172,8 @@ add_entrypoint_object(
     floorf.h
   DEPENDS
     libc.utils.FPUtil.fputil
+  COMPILE_OPTIONS
+    -O2
 )
 
 add_entrypoint_object(

diff  --git a/libc/src/math/fabsl.cpp b/libc/src/math/fabsl.cpp
new file mode 100644
index 000000000000..6e8f72873a25
--- /dev/null
+++ b/libc/src/math/fabsl.cpp
@@ -0,0 +1,18 @@
+//===-- Implementation of fabsf 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(fabsl)(long double x) {
+  return fputil::abs(x);
+}
+
+} // namespace __llvm_libc

diff  --git a/libc/src/math/fabsl.h b/libc/src/math/fabsl.h
new file mode 100644
index 000000000000..fa25b1ab564d
--- /dev/null
+++ b/libc/src/math/fabsl.h
@@ -0,0 +1,18 @@
+//===-- Implementation header for fabsf -------------------------*- 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_FABSL_H
+#define LLVM_LIBC_SRC_MATH_FABSL_H
+
+namespace __llvm_libc {
+
+long double fabsl(long double x);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_MATH_FABSL_H

diff  --git a/libc/src/math/truncl.cpp b/libc/src/math/truncl.cpp
new file mode 100644
index 000000000000..0eb557a804f4
--- /dev/null
+++ b/libc/src/math/truncl.cpp
@@ -0,0 +1,18 @@
+//===-- Implementation of truncl 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/NearestIntegerOperations.h"
+
+namespace __llvm_libc {
+
+long double LLVM_LIBC_ENTRYPOINT(truncl)(long double x) {
+  return fputil::trunc(x);
+}
+
+} // namespace __llvm_libc

diff  --git a/libc/src/math/truncl.h b/libc/src/math/truncl.h
new file mode 100644
index 000000000000..2a78ffaa3c5e
--- /dev/null
+++ b/libc/src/math/truncl.h
@@ -0,0 +1,18 @@
+//===-- Implementation header for truncl ------------------------*- 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_TRUNCL_H
+#define LLVM_LIBC_SRC_MATH_TRUNCL_H
+
+namespace __llvm_libc {
+
+long double truncl(long double x);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_MATH_TRUNCL_H

diff  --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt
index ebc03d3452cc..1617905150e5 100644
--- a/libc/test/src/math/CMakeLists.txt
+++ b/libc/test/src/math/CMakeLists.txt
@@ -97,6 +97,19 @@ add_math_unittest(
     libc.utils.FPUtil.fputil
 )
 
+add_math_unittest(
+  fabsl_test
+  NEED_MPFR
+  SUITE
+    libc_math_unittests
+  SRCS
+    fabsl_test.cpp
+  DEPENDS
+    libc.include.math
+    libc.src.math.fabsl
+    libc.utils.FPUtil.fputil
+)
+
 add_math_unittest(
   trunc_test
   NEED_MPFR
@@ -123,6 +136,19 @@ add_math_unittest(
     libc.utils.FPUtil.fputil
 )
 
+add_math_unittest(
+  truncl_test
+  NEED_MPFR
+  SUITE
+    libc_math_unittests
+  SRCS
+    truncl_test.cpp
+  DEPENDS
+    libc.include.math
+    libc.src.math.truncl
+    libc.utils.FPUtil.fputil
+)
+
 add_math_unittest(
   ceil_test
   NEED_MPFR

diff  --git a/libc/test/src/math/fabsl_test.cpp b/libc/test/src/math/fabsl_test.cpp
new file mode 100644
index 000000000000..4a645682446a
--- /dev/null
+++ b/libc/test/src/math/fabsl_test.cpp
@@ -0,0 +1,46 @@
+//===-- Unittests for fabsl -----------------------------------------------===//
+//
+// 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 "src/math/fabsl.h"
+#include "utils/FPUtil/FPBits.h"
+#include "utils/MPFRWrapper/MPFRUtils.h"
+#include "utils/UnitTest/Test.h"
+
+using FPBits = __llvm_libc::fputil::FPBits<long double>;
+
+namespace mpfr = __llvm_libc::testing::mpfr;
+
+// Zero tolerance; As in, exact match with MPFR result.
+static constexpr mpfr::Tolerance tolerance{mpfr::Tolerance::floatPrecision, 0,
+                                           0};
+
+TEST(FabslTest, SpecialNumbers) {
+  EXPECT_TRUE(FPBits::zero() == __llvm_libc::fabsl(FPBits::zero()));
+  EXPECT_TRUE(FPBits::zero() == __llvm_libc::fabsl(FPBits::negZero()));
+
+  EXPECT_TRUE(FPBits::inf() == __llvm_libc::fabsl(FPBits::inf()));
+  EXPECT_TRUE(FPBits::inf() == __llvm_libc::fabsl(FPBits::negInf()));
+
+  long double nan = FPBits::buildNaN(1);
+  ASSERT_TRUE(isnan(nan) != 0);
+  ASSERT_TRUE(isnan(__llvm_libc::fabsl(nan)) != 0);
+}
+
+TEST(FabslTest, InLongDoubleRange) {
+  using UIntType = FPBits::UIntType;
+  constexpr UIntType count = 10000000;
+  constexpr UIntType step = UIntType(-1) / count;
+  for (UIntType i = 0, v = 0; i <= count; ++i, v += step) {
+    long double x = FPBits(v);
+    if (isnan(x) || isinf(x))
+      continue;
+    ASSERT_MPFR_MATCH(mpfr::Operation::Abs, x, __llvm_libc::fabsl(x),
+                      tolerance);
+  }
+}

diff  --git a/libc/test/src/math/truncl_test.cpp b/libc/test/src/math/truncl_test.cpp
new file mode 100644
index 000000000000..37f9be74eb42
--- /dev/null
+++ b/libc/test/src/math/truncl_test.cpp
@@ -0,0 +1,65 @@
+//===-- Unittests for truncl ----------------------------------------------===//
+//
+// 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 "src/math/truncl.h"
+#include "utils/FPUtil/FPBits.h"
+#include "utils/MPFRWrapper/MPFRUtils.h"
+#include "utils/UnitTest/Test.h"
+
+using FPBits = __llvm_libc::fputil::FPBits<long double>;
+
+namespace mpfr = __llvm_libc::testing::mpfr;
+
+// Zero tolerance; As in, exact match with MPFR result.
+static constexpr mpfr::Tolerance tolerance{mpfr::Tolerance::floatPrecision, 0,
+                                           0};
+
+TEST(TrunclTest, SpecialNumbers) {
+  ASSERT_TRUE(FPBits::zero() == __llvm_libc::truncl(FPBits::zero()));
+  ASSERT_TRUE(FPBits::negZero() == __llvm_libc::truncl(FPBits::negZero()));
+
+  ASSERT_TRUE(FPBits::inf() == __llvm_libc::truncl(FPBits::inf()));
+  ASSERT_TRUE(FPBits::negInf() == __llvm_libc::truncl(FPBits::negInf()));
+
+  long double nan = FPBits::buildNaN(1);
+  ASSERT_TRUE(isnan(nan) != 0);
+  ASSERT_TRUE(isnan(__llvm_libc::truncl(nan)) != 0);
+}
+
+TEST(TrunclTest, RoundedNumbers) {
+  ASSERT_TRUE(FPBits(1.0l) == __llvm_libc::truncl(1.0l));
+  ASSERT_TRUE(FPBits(-1.0l) == __llvm_libc::truncl(-1.0l));
+  ASSERT_TRUE(FPBits(10.0l) == __llvm_libc::truncl(10.0l));
+  ASSERT_TRUE(FPBits(-10.0l) == __llvm_libc::truncl(-10.0l));
+  ASSERT_TRUE(FPBits(1234.0l) == __llvm_libc::truncl(1234.0l));
+  ASSERT_TRUE(FPBits(-1234.0l) == __llvm_libc::truncl(-1234.0l));
+}
+
+TEST(TrunclTest, Fractions) {
+  ASSERT_TRUE(FPBits(1.0l) == __llvm_libc::truncl(1.5l));
+  ASSERT_TRUE(FPBits(-1.0l) == __llvm_libc::truncl(-1.75l));
+  ASSERT_TRUE(FPBits(10.0l) == __llvm_libc::truncl(10.32l));
+  ASSERT_TRUE(FPBits(-10.0l) == __llvm_libc::truncl(-10.65l));
+  ASSERT_TRUE(FPBits(1234.0l) == __llvm_libc::truncl(1234.78l));
+  ASSERT_TRUE(FPBits(-1234.0l) == __llvm_libc::truncl(-1234.96l));
+}
+
+TEST(TrunclTest, InLongDoubleRange) {
+  using UIntType = FPBits::UIntType;
+  constexpr UIntType count = 10000000;
+  constexpr UIntType step = UIntType(-1) / count;
+  for (UIntType i = 0, v = 0; i <= count; ++i, v += step) {
+    long double x = FPBits(v);
+    if (isnan(x) || isinf(x))
+      continue;
+
+    ASSERT_MPFR_MATCH(mpfr::Operation::Trunc, x, __llvm_libc::truncl(x),
+                      tolerance);
+  }
+}

diff  --git a/libc/utils/FPUtil/BasicOperations.h b/libc/utils/FPUtil/BasicOperations.h
index d5c7004f8bc9..70c0bb557cce 100644
--- a/libc/utils/FPUtil/BasicOperations.h
+++ b/libc/utils/FPUtil/BasicOperations.h
@@ -6,7 +6,12 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "FloatOperations.h"
+#ifndef LLVM_LIBC_UTILS_FPUTIL_BASIC_OPERATIONS_H
+#define LLVM_LIBC_UTILS_FPUTIL_BASIC_OPERATIONS_H
+
+#include "FPBits.h"
+
+#include "utils/CPP/TypeTraits.h"
 
 namespace __llvm_libc {
 namespace fputil {
@@ -14,8 +19,12 @@ namespace fputil {
 template <typename T,
           cpp::EnableIfType<cpp::IsFloatingPointType<T>::Value, int> = 0>
 static inline T abs(T x) {
-  return valueFromBits(absBits(x));
+  FPBits<T> bits(x);
+  bits.sign = 0;
+  return T(bits);
 }
 
 } // namespace fputil
 } // namespace __llvm_libc
+
+#endif // LLVM_LIBC_UTILS_FPUTIL_BASIC_OPERATIONS_H

diff  --git a/libc/utils/FPUtil/CMakeLists.txt b/libc/utils/FPUtil/CMakeLists.txt
index 21013ee1771f..d9084f72e841 100644
--- a/libc/utils/FPUtil/CMakeLists.txt
+++ b/libc/utils/FPUtil/CMakeLists.txt
@@ -1,10 +1,18 @@
+if(${LIBC_TARGET_MACHINE} MATCHES "^x86.*")
+  set(LONG_DOUBLE_HDR LongDoubleBitsX86.h)
+else()
+  set(LONG_DOUBLE_HDR)
+endif()
+
 add_header_library(
   fputil
   HDRS
+    ${LONG_DOUBLE_HDR}
     BitPatterns.h
     FloatOperations.h
     FloatProperties.h
+    FPBits.h
     ManipulationFunctions.h
-  DEPS
+  DEPENDS
     libc.utils.CPP.standalone_cpp
 )

diff  --git a/libc/utils/FPUtil/FPBits.h b/libc/utils/FPUtil/FPBits.h
new file mode 100644
index 000000000000..2c630dba2af7
--- /dev/null
+++ b/libc/utils/FPUtil/FPBits.h
@@ -0,0 +1,148 @@
+//===-- Abstract class for bit manipulation of float 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_UTILS_FPUTIL_FP_BITS_H
+#define LLVM_LIBC_UTILS_FPUTIL_FP_BITS_H
+
+#include "utils/CPP/TypeTraits.h"
+
+#include <stdint.h>
+
+namespace __llvm_libc {
+namespace fputil {
+
+template <typename T> struct MantissaWidth {};
+template <> struct MantissaWidth<float> {
+  static constexpr unsigned value = 23;
+};
+template <> struct MantissaWidth<double> {
+  static constexpr unsigned value = 52;
+};
+
+template <typename T> struct ExponentWidth {};
+template <> struct ExponentWidth<float> {
+  static constexpr unsigned value = 8;
+};
+template <> struct ExponentWidth<double> {
+  static constexpr unsigned value = 11;
+};
+template <> struct ExponentWidth<long double> {
+  static constexpr unsigned value = 15;
+};
+
+template <typename T> struct FPUIntType {};
+template <> struct FPUIntType<float> { using Type = uint32_t; };
+template <> struct FPUIntType<double> { using Type = uint64_t; };
+
+#if !(defined(__x86_64__) || defined(__i386__))
+// TODO: This has to be extended for visual studio where long double and
+// double are equivalent.
+template <> struct MantissaWidth<long double> {
+  static constexpr unsigned value = 112;
+};
+
+template <> struct FPUIntType<long double> { using Type = __uint128_t; };
+#endif
+
+// A generic class to represent single precision, double precision, and quad
+// precision IEEE 754 floating point formats.
+// On most platforms, the 'float' type corresponds to single precision floating
+// point numbers, the 'double' type corresponds to double precision floating
+// point numers, and the 'long double' type corresponds to the quad precision
+// floating numbers. On x86 platforms however, the 'long double' type maps to
+// an x87 floating point format. This format is an IEEE 754 extension format.
+// It is handled as an explicit specialization of this class.
+template <typename T> struct __attribute__((packed)) FPBits {
+  static_assert(cpp::IsFloatingPointType<T>::Value,
+                "FPBits instantiated with invalid type.");
+
+  // Reinterpreting bits as an integer value and interpreting the bits of an
+  // integer value as a floating point value is used in tests. So, a convenient
+  // type is provided for such reinterpretations.
+  using UIntType = typename FPUIntType<T>::Type;
+
+  UIntType mantissa : MantissaWidth<T>::value;
+  uint16_t exponent : ExponentWidth<T>::value;
+  uint8_t sign : 1;
+
+  static constexpr int exponentBias = (1 << (ExponentWidth<T>::value - 1)) - 1;
+  static constexpr int maxExponent = (1 << ExponentWidth<T>::value) - 1;
+
+  // We don't want accidental type promotions/conversions so we require exact
+  // type match.
+  template <typename XType,
+            cpp::EnableIfType<cpp::IsSame<T, XType>::Value, int> = 0>
+  explicit FPBits(XType x) {
+    *this = *reinterpret_cast<FPBits<T> *>(&x);
+  }
+
+  operator T() { return *reinterpret_cast<T *>(this); }
+
+  int getExponent() const { return int(exponent) - exponentBias; }
+
+  bool isZero() const { return mantissa == 0 && exponent == 0; }
+
+  bool isInf() const { return mantissa == 0 && exponent == maxExponent; }
+
+  bool isNaN() const { return exponent == maxExponent && mantissa != 0; }
+
+  bool isInfOrNaN() const { return exponent == maxExponent; }
+
+  // Methods below this are used by tests.
+  // The to and from integer bits converters are only used in tests. Hence,
+  // the potential software implementations of UIntType will not slow real
+  // code.
+
+  template <typename XType,
+            cpp::EnableIfType<cpp::IsSame<UIntType, XType>::Value, int> = 0>
+  explicit FPBits<long double>(XType x) {
+    // The last 4 bytes of v are ignored in case of i386.
+    *this = *reinterpret_cast<FPBits<T> *>(&x);
+  }
+
+  UIntType bitsAsUInt() const {
+    return *reinterpret_cast<const UIntType *>(this);
+  }
+
+  static FPBits<T> zero() { return FPBits(T(0.0)); }
+
+  static FPBits<T> negZero() {
+    FPBits<T> bits(T(0.0));
+    bits.sign = 1;
+    return bits;
+  }
+
+  static FPBits<T> inf() {
+    FPBits<T> bits(T(0.0));
+    bits.exponent = maxExponent;
+    return bits;
+  }
+
+  static FPBits<T> negInf() {
+    FPBits<T> bits(T(0.0));
+    bits.exponent = maxExponent;
+    bits.sign = 1;
+    return bits;
+  }
+
+  static T buildNaN(UIntType v) {
+    FPBits<T> bits(T(0.0));
+    bits.exponent = maxExponent;
+    bits.mantissa = v;
+    return bits;
+  }
+};
+
+} // namespace fputil
+} // namespace __llvm_libc
+
+#if defined(__x86_64__) || defined(__i386__)
+#include "utils/FPUtil/LongDoubleBitsX86.h"
+#endif
+
+#endif // LLVM_LIBC_UTILS_FPUTIL_FP_BITS_H

diff  --git a/libc/utils/FPUtil/FloatOperations.h b/libc/utils/FPUtil/FloatOperations.h
index 0739548f567d..ffd138d6cc9b 100644
--- a/libc/utils/FPUtil/FloatOperations.h
+++ b/libc/utils/FPUtil/FloatOperations.h
@@ -57,7 +57,6 @@ static inline int getExponent(T x) {
   return getExponentFromBits(valueAsBits(x));
 }
 
-
 } // namespace fputil
 } // namespace __llvm_libc
 

diff  --git a/libc/utils/FPUtil/LongDoubleBitsX86.h b/libc/utils/FPUtil/LongDoubleBitsX86.h
new file mode 100644
index 000000000000..1e92dba4383d
--- /dev/null
+++ b/libc/utils/FPUtil/LongDoubleBitsX86.h
@@ -0,0 +1,127 @@
+//===-- Bit representation of x86 long double 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_UTILS_FPUTIL_LONG_DOUBLE_BITS_X86_H
+#define LLVM_LIBC_UTILS_FPUTIL_LONG_DOUBLE_BITS_X86_H
+
+#include "utils/FPUtil/FPBits.h"
+
+#include <stdint.h>
+
+namespace __llvm_libc {
+namespace fputil {
+
+template <> struct MantissaWidth<long double> {
+  static constexpr unsigned value = 63;
+};
+
+template <unsigned Width> struct Padding;
+
+// i386 padding.
+template <> struct Padding<4> { static constexpr unsigned Value = 16; };
+
+// x86_64 padding.
+template <> struct Padding<8> { static constexpr unsigned Value = 48; };
+
+template <> struct __attribute__((packed)) FPBits<long double> {
+  using UIntType = __uint128_t;
+
+  static constexpr int exponentBias = 0x3FFF;
+  static constexpr int maxExponent = 0x7FFF;
+
+  UIntType mantissa : MantissaWidth<long double>::value;
+  uint8_t implicitBit : 1;
+  uint16_t exponent : ExponentWidth<long double>::value;
+  uint8_t sign : 1;
+  uint64_t padding : Padding<sizeof(uintptr_t)>::Value;
+
+  template <typename XType,
+            cpp::EnableIfType<cpp::IsSame<long double, XType>::Value, int> = 0>
+  explicit FPBits<long double>(XType x) {
+    *this = *reinterpret_cast<FPBits<long double> *>(&x);
+  }
+
+  operator long double() { return *reinterpret_cast<long double *>(this); }
+
+  int getExponent() const {
+    if (exponent == 0)
+      return int(1) - exponentBias;
+    return int(exponent) - exponentBias;
+  }
+
+  bool isZero() const {
+    return exponent == 0 && mantissa == 0 && implicitBit == 0;
+  }
+
+  bool isInf() const {
+    return exponent == maxExponent && mantissa == 0 && implicitBit == 1;
+  }
+
+  bool isNaN() const { return exponent == maxExponent && mantissa != 0; }
+
+  bool isInfOrNaN() const { return exponent == maxExponent; }
+
+  // Methods below this are used by tests.
+
+  template <typename XType,
+            cpp::EnableIfType<cpp::IsSame<UIntType, XType>::Value, int> = 0>
+  explicit FPBits<long double>(XType x) {
+    // The last 4 bytes of v are ignored in case of i386.
+    *this = *reinterpret_cast<FPBits<long double> *>(&x);
+  }
+
+  UIntType bitsAsUInt() const {
+    // We cannot just return the bits as is as it will lead to reading
+    // out of bounds in case of i386. So, we first copy the wider value
+    // before returning the value. This makes the last 4 bytes are always
+    // zero in case i386.
+    UIntType result = UIntType(0);
+    *reinterpret_cast<FPBits<long double> *>(&result) = *this;
+    return result;
+  }
+
+  static FPBits<long double> zero() { return FPBits<long double>(0.0l); }
+
+  static FPBits<long double> negZero() {
+    FPBits<long double> bits(0.0l);
+    bits.sign = 1;
+    return bits;
+  }
+
+  static FPBits<long double> inf() {
+    FPBits<long double> bits(0.0l);
+    bits.exponent = maxExponent;
+    bits.implicitBit = 1;
+    return bits;
+  }
+
+  static FPBits<long double> negInf() {
+    FPBits<long double> bits(0.0l);
+    bits.exponent = maxExponent;
+    bits.implicitBit = 1;
+    bits.sign = 1;
+    return bits;
+  }
+
+  static long double buildNaN(UIntType v) {
+    FPBits<long double> bits(0.0l);
+    bits.exponent = maxExponent;
+    bits.implicitBit = 1;
+    bits.mantissa = v;
+    return bits;
+  }
+};
+
+static_assert(
+    sizeof(FPBits<long double>) == sizeof(long double),
+    "Internal long double representation does not match the machine format.");
+
+} // namespace fputil
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_UTILS_FPUTIL_LONG_DOUBLE_BITS_X86_H

diff  --git a/libc/utils/FPUtil/NearestIntegerOperations.h b/libc/utils/FPUtil/NearestIntegerOperations.h
index 7f93de670112..c0535f599365 100644
--- a/libc/utils/FPUtil/NearestIntegerOperations.h
+++ b/libc/utils/FPUtil/NearestIntegerOperations.h
@@ -10,6 +10,7 @@
 #define LLVM_LIBC_UTILS_FPUTIL_NEAREST_INTEGER_OPERATIONS_H
 
 #include "ClassificationFunctions.h"
+#include "FPBits.h"
 #include "FloatOperations.h"
 #include "FloatProperties.h"
 
@@ -21,32 +22,33 @@ namespace fputil {
 template <typename T,
           cpp::EnableIfType<cpp::IsFloatingPointType<T>::Value, int> = 0>
 static inline T trunc(T x) {
-  using Properties = FloatProperties<T>;
-  using BitsType = typename FloatProperties<T>::BitsType;
+  FPBits<T> bits(x);
 
-  BitsType bits = valueAsBits(x);
-
-  // If x is infinity, NaN or zero, return it.
-  if (bitsAreInfOrNaN(bits) || bitsAreZero(bits))
+  // If x is infinity or NaN, return it.
+  // If it is zero also we should return it as is, but the logic
+  // later in this function takes care of it. But not doing a zero
+  // check, we improve the run time of non-zero values.
+  if (bits.isInfOrNaN())
     return x;
 
-  int exponent = getExponentFromBits(bits);
+  int exponent = bits.getExponent();
 
   // If the exponent is greater than the most negative mantissa
   // exponent, then x is already an integer.
-  if (exponent >= static_cast<int>(Properties::mantissaWidth))
+  if (exponent >= static_cast<int>(MantissaWidth<T>::value))
     return x;
 
   // If the exponent is such that abs(x) is less than 1, then return 0.
   if (exponent <= -1) {
-    if (Properties::signMask & bits)
+    if (bits.sign)
       return T(-0.0);
     else
       return T(0.0);
   }
 
-  uint32_t trimSize = Properties::mantissaWidth - exponent;
-  return valueFromBits((bits >> trimSize) << trimSize);
+  int trimSize = MantissaWidth<T>::value - exponent;
+  bits.mantissa = (bits.mantissa >> trimSize) << trimSize;
+  return bits;
 }
 
 template <typename T,

diff  --git a/libc/utils/MPFRWrapper/MPFRUtils.cpp b/libc/utils/MPFRWrapper/MPFRUtils.cpp
index 1bbc84d554c0..c6020f471e88 100644
--- a/libc/utils/MPFRWrapper/MPFRUtils.cpp
+++ b/libc/utils/MPFRWrapper/MPFRUtils.cpp
@@ -8,7 +8,7 @@
 
 #include "MPFRUtils.h"
 
-#include "utils/FPUtil/FloatOperations.h"
+#include "utils/FPUtil/FPBits.h"
 
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringRef.h"
@@ -17,14 +17,16 @@
 #include <stdint.h>
 #include <string>
 
+template <typename T> using FPBits = __llvm_libc::fputil::FPBits<T>;
+
 namespace __llvm_libc {
 namespace testing {
 namespace mpfr {
 
 class MPFRNumber {
   // A precision value which allows sufficiently large additional
-  // precision even compared to double precision floating point values.
-  static constexpr unsigned int mpfrPrecision = 96;
+  // precision even compared to quad-precision floating point values.
+  static constexpr unsigned int mpfrPrecision = 128;
 
   mpfr_t value;
 
@@ -48,6 +50,13 @@ class MPFRNumber {
     mpfr_set_d(value, x, MPFR_RNDN);
   }
 
+  template <typename XType,
+            cpp::EnableIfType<cpp::IsSame<long double, XType>::Value, int> = 0>
+  explicit MPFRNumber(XType x) {
+    mpfr_init2(value, mpfrPrecision);
+    mpfr_set_ld(value, x, MPFR_RNDN);
+  }
+
   template <typename XType,
             cpp::EnableIfType<cpp::IsIntegral<XType>::Value, int> = 0>
   explicit MPFRNumber(XType x) {
@@ -58,7 +67,7 @@ class MPFRNumber {
   template <typename XType> MPFRNumber(XType x, const Tolerance &t) {
     mpfr_init2(value, mpfrPrecision);
     mpfr_set_zero(value, 1); // Set to positive zero.
-    MPFRNumber xExponent(fputil::getExponent(x));
+    MPFRNumber xExponent(fputil::FPBits<XType>(x).getExponent());
     // E = 2^E
     mpfr_exp2(xExponent.value, xExponent.value, MPFR_RNDN);
     uint32_t bitMask = 1 << (t.width - 1);
@@ -155,24 +164,28 @@ namespace internal {
 
 template <typename T>
 void MPFRMatcher<T>::explainError(testutils::StreamWrapper &OS) {
-  using fputil::valueAsBits;
-
   MPFRNumber mpfrResult(operation, input);
   MPFRNumber mpfrInput(input);
   MPFRNumber mpfrMatchValue(matchValue);
   MPFRNumber mpfrToleranceValue(matchValue, tolerance);
+  FPBits<T> inputBits(input);
+  FPBits<T> matchBits(matchValue);
+  // TODO: Call to llvm::utohexstr implicitly converts __uint128_t values to
+  // uint64_t values. This can be fixed using a custom wrapper for
+  // llvm::utohexstr to handle __uint128_t values correctly.
   OS << "Match value not within tolerance value of MPFR result:\n"
      << "  Input decimal: " << mpfrInput.str() << '\n'
-     << "     Input bits: 0x" << llvm::utohexstr(valueAsBits(input)) << '\n'
+     << "     Input bits: 0x" << llvm::utohexstr(inputBits.bitsAsUInt()) << '\n'
      << "  Match decimal: " << mpfrMatchValue.str() << '\n'
-     << "     Match bits: 0x" << llvm::utohexstr(valueAsBits(matchValue))
-     << '\n'
+     << "     Match bits: 0x" << llvm::utohexstr(matchBits.bitsAsUInt()) << '\n'
      << "    MPFR result: " << mpfrResult.str() << '\n'
      << "Tolerance value: " << mpfrToleranceValue.str() << '\n';
 }
 
 template void MPFRMatcher<float>::explainError(testutils::StreamWrapper &);
 template void MPFRMatcher<double>::explainError(testutils::StreamWrapper &);
+template void
+MPFRMatcher<long double>::explainError(testutils::StreamWrapper &);
 
 template <typename T>
 bool compare(Operation op, T input, T libcResult, const Tolerance &t) {
@@ -185,6 +198,8 @@ bool compare(Operation op, T input, T libcResult, const Tolerance &t) {
 
 template bool compare<float>(Operation, float, float, const Tolerance &);
 template bool compare<double>(Operation, double, double, const Tolerance &);
+template bool compare<long double>(Operation, long double, long double,
+                                   const Tolerance &);
 
 } // namespace internal
 


        


More information about the libc-commits mailing list