Skip to content

[libc] Add MPFR testing infra for float128. #119499

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

Merged
merged 2 commits into from
Dec 13, 2024
Merged

[libc] Add MPFR testing infra for float128. #119499

merged 2 commits into from
Dec 13, 2024

Conversation

lntue
Copy link
Contributor

@lntue lntue commented Dec 11, 2024

No description provided.

@llvmbot
Copy link
Member

llvmbot commented Dec 11, 2024

@llvm/pr-subscribers-libc

Author: None (lntue)

Changes

Full diff: https://github.com/llvm/llvm-project/pull/119499.diff

6 Files Affected:

  • (modified) libc/src/__support/complex_type.h (+3-5)
  • (modified) libc/src/__support/macros/properties/complex_types.h (+5)
  • (modified) libc/src/__support/macros/properties/types.h (+5)
  • (modified) libc/test/src/math/CMakeLists.txt (+13)
  • (added) libc/test/src/math/sqrtf128_test.cpp (+43)
  • (modified) libc/utils/MPFRWrapper/MPFRUtils.cpp (+96-6)
diff --git a/libc/src/__support/complex_type.h b/libc/src/__support/complex_type.h
index a6207d38d0eb57..d505d46d1a73f1 100644
--- a/libc/src/__support/complex_type.h
+++ b/libc/src/__support/complex_type.h
@@ -36,8 +36,7 @@ template <> struct make_complex<float16> {
   using type = cfloat16;
 };
 #endif
-#if defined(LIBC_TYPES_HAS_CFLOAT128) &&                                       \
-    !defined(LIBC_TYPES_LONG_DOUBLE_IS_FLOAT128)
+#ifdef LIBC_TYPES_CFLOAT128_IS_NOT_COMPLEX_LONG_DOUBLE
 template <> struct make_complex<float128> {
   using type = cfloat128;
 };
@@ -62,8 +61,7 @@ template <> struct make_real<cfloat16> {
   using type = float16;
 };
 #endif
-#if defined(LIBC_TYPES_HAS_CFLOAT128) &&                                       \
-    !defined(LIBC_TYPES_CFLOAT128_IS_COMPLEX_LONG_DOUBLE)
+#ifdef LIBC_TYPES_CFLOAT128_IS_NOT_COMPLEX_LONG_DOUBLE
 template <> struct make_real<cfloat128> {
   using type = float128;
 };
@@ -71,7 +69,7 @@ template <> struct make_real<cfloat128> {
 
 template <typename T> using make_real_t = typename make_real<T>::type;
 
-template <typename T> LIBC_INLINE constexpr T conjugate(T c) {
+template <typename T> LIBC_INLINE static constexpr T conjugate(T c) {
   Complex<make_real_t<T>> c_c = cpp::bit_cast<Complex<make_real_t<T>>>(c);
   c_c.imag = -c_c.imag;
   return cpp::bit_cast<T>(c_c);
diff --git a/libc/src/__support/macros/properties/complex_types.h b/libc/src/__support/macros/properties/complex_types.h
index 3f4a7646649c64..ede4d6b7c7d9de 100644
--- a/libc/src/__support/macros/properties/complex_types.h
+++ b/libc/src/__support/macros/properties/complex_types.h
@@ -22,4 +22,9 @@
 // LIBC_TYPES_HAS_CFLOAT128 and 'cfloat128' type are provided by
 // "include/llvm-libc-types/cfloat128.h"
 
+#if defined(LIBC_TYPES_HAS_CFLOAT128) &&                                       \
+    !defined(LIBC_TYPES_CFLOAT128_IS_COMPLEX_LONG_DOUBLE)
+#define LIBC_TYPES_CFLOAT128_IS_NOT_COMPLEX_LONG_DOUBLE
+#endif
+
 #endif // LLVM_LIBC_SRC___SUPPORT_MACROS_PROPERTIES_CTYPES_H
diff --git a/libc/src/__support/macros/properties/types.h b/libc/src/__support/macros/properties/types.h
index 30c742c007ca19..6293b9d4d292ae 100644
--- a/libc/src/__support/macros/properties/types.h
+++ b/libc/src/__support/macros/properties/types.h
@@ -31,6 +31,11 @@
 #define LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
 #endif
 
+#if defined(LIBC_TYPES_HAS_FLOAT128) &&                                        \
+    !defined(LIBC_TYPES_LONG_DOUBLE_IS_FLOAT128)
+#define LIBC_TYPES_FLOAT128_IS_NOT_LONG_DOUBLE
+#endif
+
 // int64 / uint64 support
 #if defined(UINT64_MAX)
 #define LIBC_TYPES_HAS_INT64
diff --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt
index ea75720df4f430..02e974f34abf88 100644
--- a/libc/test/src/math/CMakeLists.txt
+++ b/libc/test/src/math/CMakeLists.txt
@@ -1549,6 +1549,19 @@ add_fp_unittest(
     libc.src.math.sqrtf16
 )
 
+add_fp_unittest(
+  sqrtf128_test
+  NEED_MPFR
+  SUITE
+    libc-math-unittests
+  SRCS
+    sqrtf128_test.cpp
+  HDRS
+    SqrtTest.h
+  DEPENDS
+    libc.src.math.sqrtf128
+)
+
 add_fp_unittest(
   generic_sqrtf_test
   NEED_MPFR
diff --git a/libc/test/src/math/sqrtf128_test.cpp b/libc/test/src/math/sqrtf128_test.cpp
new file mode 100644
index 00000000000000..25229f834d33c7
--- /dev/null
+++ b/libc/test/src/math/sqrtf128_test.cpp
@@ -0,0 +1,43 @@
+//===-- Unittests for sqrtf128 --------------------------------------------===//
+//
+// 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 "SqrtTest.h"
+
+#include "src/math/sqrtf128.h"
+
+#include "src/__support/integer_literals.h"
+
+LIST_SQRT_TESTS(float128, LIBC_NAMESPACE::sqrtf128)
+
+TEST_F(LlvmLibcSqrtTest, SpecialInputs) {
+  constexpr float128 INPUTS[] = {
+      0x0.000000dee2f5b6a26c8f07f05442p-16382q,
+      0x0.000000c86d174c5ad8ae54a548e7p-16382q,
+      0x0.000020ab15cfe0b8e488e128f535p-16382q,
+      0x0.0000219e97732a9970f2511989bap-16382q,
+      0x0.000026e477546ae99ef57066f9fdp-16382q,
+      0x0.00002d0f88d27a496b3e533f5067p-16382q,
+      0x1.0000000000000000000000000001p+0q,
+      0x1.0000000000000000000000000003p+0q,
+      0x1.0000000000000000000000000005p+0q,
+      0x1.2af17a4ae6f93d11310c49c11b59p+0q,
+      0x1.c4f5074269525063a26051a0ad27p+0q,
+      0x1.035cb5f298a801dc4be9b1f8cd97p+1q,
+      0x1.274be02380427e709beab4dedeb4p+1q,
+      0x1.64e797cfdbaa3f7e2f33279dbc6p+1q,
+      0x1.d78d8352b48608b510bfd5c75315p+1q,
+      0x1.fffffffffffffffffffffffffffbp+1q,
+      0x1.fffffffffffffffffffffffffffdp+1q,
+      0x1.ffffffffffffffffffffffffffffp+1q,
+  };
+
+  for (auto input : INPUTS) {
+    ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Sqrt, input,
+                                   LIBC_NAMESPACE::sqrtf128(input), 0.5);
+  }
+}
diff --git a/libc/utils/MPFRWrapper/MPFRUtils.cpp b/libc/utils/MPFRWrapper/MPFRUtils.cpp
index 00592c5cb15f38..0dac497bb779af 100644
--- a/libc/utils/MPFRWrapper/MPFRUtils.cpp
+++ b/libc/utils/MPFRWrapper/MPFRUtils.cpp
@@ -22,6 +22,13 @@
 
 #include "mpfr_inc.h"
 
+#ifdef LIBC_TYPES_FLOAT128_IS_NOT_LONG_DOUBLE
+extern "C" {
+int mpfr_set_float128(mpfr_ptr, float128, mpfr_rnd_t);
+float128 mpfr_get_float128(mpfr_srcptr, mpfr_rnd_t);
+}
+#endif
+
 template <typename T> using FPBits = LIBC_NAMESPACE::fputil::FPBits<T>;
 
 namespace LIBC_NAMESPACE_DECL {
@@ -47,8 +54,18 @@ template <> struct ExtraPrecision<double> {
 };
 
 template <> struct ExtraPrecision<long double> {
+#ifdef LIBC_TYPES_LONG_DOUBLE_IS_FLOAT128
+  static constexpr unsigned int VALUE = 512;
+#else
   static constexpr unsigned int VALUE = 256;
+#endif
+};
+
+#if defined(LIBC_TYPES_FLOAT128_IS_NOT_LONG_DOUBLE)
+template <> struct ExtraPrecision<float128> {
+  static constexpr unsigned int VALUE = 512;
 };
+#endif // LIBC_TYPES_FLOAT128_IS_NOT_LONG_DOUBLE
 
 // If the ulp tolerance is less than or equal to 0.5, we would check that the
 // result is rounded correctly with respect to the rounding mode by using the
@@ -134,6 +151,19 @@ class MPFRNumber {
     mpfr_set_ld(value, x, mpfr_rounding);
   }
 
+#ifdef LIBC_TYPES_FLOAT128_IS_NOT_LONG_DOUBLE
+  template <typename XType,
+            cpp::enable_if_t<cpp::is_same_v<float128, XType>, int> = 0>
+  explicit MPFRNumber(XType x,
+                      unsigned int precision = ExtraPrecision<XType>::VALUE,
+                      RoundingMode rounding = RoundingMode::Nearest)
+      : mpfr_precision(precision),
+        mpfr_rounding(get_mpfr_rounding_mode(rounding)) {
+    mpfr_init2(value, mpfr_precision);
+    mpfr_set_float128(value, x, mpfr_rounding);
+  }
+#endif // LIBC_TYPES_FLOAT128_IS_NOT_LONG_DOUBLE
+
   template <typename XType,
             cpp::enable_if_t<cpp::is_integral_v<XType>, int> = 0>
   explicit MPFRNumber(XType x,
@@ -647,7 +677,7 @@ class MPFRNumber {
   // These functions are useful for debugging.
   template <typename T> T as() const;
 
-  void dump(const char *msg) const { mpfr_printf("%s%.128Rf\n", msg, value); }
+  void dump(const char *msg) const { mpfr_printf("%s%.128g\n", msg, value); }
 
   // Return the ULP (units-in-the-last-place) difference between the
   // stored MPFR and a floating point number.
@@ -770,6 +800,13 @@ template <> float16 MPFRNumber::as<float16>() const {
 }
 #endif
 
+#ifdef LIBC_TYPES_FLOAT128_IS_NOT_LONG_DOUBLE
+template <> float128 MPFRNumber::as<float128>() const {
+  return mpfr_get_float128(value, mpfr_rounding);
+}
+
+#endif // LIBC_TYPES_FLOAT128_IS_NOT_LONG_DOUBLE
+
 namespace internal {
 
 template <typename InputType>
@@ -997,7 +1034,27 @@ template void explain_unary_operation_single_output_error(Operation op, double,
 template void explain_unary_operation_single_output_error(Operation op,
                                                           long double, float16,
                                                           double, RoundingMode);
-#endif
+#ifdef LIBC_TYPES_FLOAT128_IS_NOT_LONG_DOUBLE
+template void explain_unary_operation_single_output_error(Operation op,
+                                                          float128, float16,
+                                                          double, RoundingMode);
+#endif // LIBC_TYPES_FLOAT128_IS_NOT_LONG_DOUBLE
+#endif // LIBC_TYPES_HAS_FLOAT16
+
+#ifdef LIBC_TYPES_FLOAT128_IS_NOT_LONG_DOUBLE
+template void explain_unary_operation_single_output_error(Operation op,
+                                                          float128, float128,
+                                                          double, RoundingMode);
+template void explain_unary_operation_single_output_error(Operation op,
+                                                          float128, float,
+                                                          double, RoundingMode);
+template void explain_unary_operation_single_output_error(Operation op,
+                                                          float128, double,
+                                                          double, RoundingMode);
+template void explain_unary_operation_single_output_error(Operation op,
+                                                          float128, long double,
+                                                          double, RoundingMode);
+#endif // LIBC_TYPES_FLOAT128_IS_NOT_LONG_DOUBLE
 
 template <typename T>
 void explain_unary_operation_two_outputs_error(
@@ -1228,7 +1285,25 @@ template bool compare_unary_operation_single_output(Operation, double, float16,
 template bool compare_unary_operation_single_output(Operation, long double,
                                                     float16, double,
                                                     RoundingMode);
-#endif
+#ifdef LIBC_TYPES_FLOAT128_IS_NOT_LONG_DOUBLE
+template bool compare_unary_operation_single_output(Operation, float128,
+                                                    float16, double,
+                                                    RoundingMode);
+#endif // LIBC_TYPES_FLOAT128_IS_NOT_LONG_DOUBLE
+#endif // LIBC_TYPES_HAS_FLOAT16
+
+#ifdef LIBC_TYPES_FLOAT128_IS_NOT_LONG_DOUBLE
+template bool compare_unary_operation_single_output(Operation, float128,
+                                                    float128, double,
+                                                    RoundingMode);
+template bool compare_unary_operation_single_output(Operation, float128, float,
+                                                    double, RoundingMode);
+template bool compare_unary_operation_single_output(Operation, float128, double,
+                                                    double, RoundingMode);
+template bool compare_unary_operation_single_output(Operation, float128,
+                                                    long double, double,
+                                                    RoundingMode);
+#endif // LIBC_TYPES_FLOAT128_IS_NOT_LONG_DOUBLE
 
 template <typename T>
 bool compare_unary_operation_two_outputs(Operation op, T input,
@@ -1398,9 +1473,14 @@ template <typename T> bool round_to_long(T x, long &result) {
 template bool round_to_long<float>(float, long &);
 template bool round_to_long<double>(double, long &);
 template bool round_to_long<long double>(long double, long &);
+
 #ifdef LIBC_TYPES_HAS_FLOAT16
 template bool round_to_long<float16>(float16, long &);
-#endif
+#endif // LIBC_TYPES_HAS_FLOAT16
+
+#ifdef LIBC_TYPES_FLOAT128_IS_NOT_LONG_DOUBLE
+template bool round_to_long<float128>(float128, long &);
+#endif // LIBC_TYPES_FLOAT128_IS_NOT_LONG_DOUBLE
 
 template <typename T> bool round_to_long(T x, RoundingMode mode, long &result) {
   MPFRNumber mpfr(x);
@@ -1410,9 +1490,14 @@ template <typename T> bool round_to_long(T x, RoundingMode mode, long &result) {
 template bool round_to_long<float>(float, RoundingMode, long &);
 template bool round_to_long<double>(double, RoundingMode, long &);
 template bool round_to_long<long double>(long double, RoundingMode, long &);
+
 #ifdef LIBC_TYPES_HAS_FLOAT16
 template bool round_to_long<float16>(float16, RoundingMode, long &);
-#endif
+#endif // LIBC_TYPES_HAS_FLOAT16
+
+#ifdef LIBC_TYPES_FLOAT128_IS_NOT_LONG_DOUBLE
+template bool round_to_long<float128>(float128, RoundingMode, long &);
+#endif // LIBC_TYPES_FLOAT128_IS_NOT_LONG_DOUBLE
 
 template <typename T> T round(T x, RoundingMode mode) {
   MPFRNumber mpfr(x);
@@ -1423,9 +1508,14 @@ template <typename T> T round(T x, RoundingMode mode) {
 template float round<float>(float, RoundingMode);
 template double round<double>(double, RoundingMode);
 template long double round<long double>(long double, RoundingMode);
+
 #ifdef LIBC_TYPES_HAS_FLOAT16
 template float16 round<float16>(float16, RoundingMode);
-#endif
+#endif // LIBC_TYPES_HAS_FLOAT16
+
+#ifdef LIBC_TYPES_FLOAT128_IS_NOT_LONG_DOUBLE
+template float128 round<float128>(float128, RoundingMode);
+#endif // LIBC_TYPES_FLOAT128_IS_NOT_LONG_DOUBLE
 
 } // namespace mpfr
 } // namespace testing

template <> struct make_real<cfloat128> {
using type = float128;
};
#endif

template <typename T> using make_real_t = typename make_real<T>::type;

template <typename T> LIBC_INLINE constexpr T conjugate(T c) {
template <typename T> LIBC_INLINE static constexpr T conjugate(T c) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If conjugate is already in a namespace, does it still need static linkage?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to make conj to be local to the TU?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was just playing around to see if this symbol is exposed in the static archives and object files or not. Reverted it back to before.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think more generally, we can probably remove this pattern throughout the codebase; I've been noticing that we're not super consistent.

@lntue lntue merged commit 4eec286 into llvm:main Dec 13, 2024
11 checks passed
@lntue lntue deleted the float128 branch December 13, 2024 14:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants