Skip to content

[libc][NFC] Decouple FP properties from C++ types #73517

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 4 commits into from
Nov 27, 2023

Conversation

gchatelet
Copy link
Contributor

We simplify the floating point properties by splitting concerns:

  • We define all distinct floating point formats in the FPType enum.
  • We map them to properties via the FPProperties trait.
  • We map from C++ type to FPType in the getFPType function so logic is easier to understand and extend.

@llvmbot llvmbot added the libc label Nov 27, 2023
@llvmbot
Copy link
Member

llvmbot commented Nov 27, 2023

@llvm/pr-subscribers-libc

Author: Guillaume Chatelet (gchatelet)

Changes

We simplify the floating point properties by splitting concerns:

  • We define all distinct floating point formats in the FPType enum.
  • We map them to properties via the FPProperties trait.
  • We map from C++ type to FPType in the getFPType function so logic is easier to understand and extend.

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

3 Files Affected:

  • (modified) libc/src/__support/FPUtil/FloatProperties.h (+45-80)
  • (modified) libc/src/__support/FPUtil/dyadic_float.h (+3-5)
  • (modified) libc/src/__support/macros/properties/float.h (+56-18)
diff --git a/libc/src/__support/FPUtil/FloatProperties.h b/libc/src/__support/FPUtil/FloatProperties.h
index 9d91688e023e923..b91e7ace5233f91 100644
--- a/libc/src/__support/FPUtil/FloatProperties.h
+++ b/libc/src/__support/FPUtil/FloatProperties.h
@@ -17,12 +17,18 @@
 namespace LIBC_NAMESPACE {
 namespace fputil {
 
-template <typename T> struct FloatProperties {};
+// The supported floating point types.
+enum class FPType {
+  IEEE754_Binary16,
+  IEEE754_Binary32,
+  IEEE754_Binary64,
+  IEEE754_Binary128,
+  X86_Binary80,
+};
 
-template <> struct FloatProperties<float> {
+template <FPType> struct FPProperties {};
+template <> struct FPProperties<FPType::IEEE754_Binary32> {
   typedef uint32_t BitsType;
-  static_assert(sizeof(BitsType) == sizeof(float),
-                "Unexpected size of 'float' type.");
 
   static constexpr uint32_t BIT_WIDTH = sizeof(BitsType) * 8;
 
@@ -46,10 +52,8 @@ template <> struct FloatProperties<float> {
   static constexpr BitsType QUIET_NAN_MASK = 0x00400000U;
 };
 
-template <> struct FloatProperties<double> {
+template <> struct FPProperties<FPType::IEEE754_Binary64> {
   typedef uint64_t BitsType;
-  static_assert(sizeof(BitsType) == sizeof(double),
-                "Unexpected size of 'double' type.");
 
   static constexpr uint32_t BIT_WIDTH = sizeof(BitsType) * 8;
 
@@ -72,47 +76,10 @@ template <> struct FloatProperties<double> {
   static constexpr BitsType QUIET_NAN_MASK = 0x0008000000000000ULL;
 };
 
-#if defined(LONG_DOUBLE_IS_DOUBLE)
-// Properties for numbers represented in 64 bits long double on Windows
-// platform.
-template <> struct FloatProperties<long double> {
-  typedef uint64_t BitsType;
-  static_assert(sizeof(BitsType) == sizeof(double),
-                "Unexpected size of 'double' type.");
-
-  static constexpr uint32_t BIT_WIDTH = FloatProperties<double>::BIT_WIDTH;
-
-  static constexpr uint32_t MANTISSA_WIDTH =
-      FloatProperties<double>::MANTISSA_WIDTH;
-  static constexpr uint32_t MANTISSA_PRECISION = MANTISSA_WIDTH + 1;
-  static constexpr uint32_t EXPONENT_WIDTH =
-      FloatProperties<double>::EXPONENT_WIDTH;
-  static constexpr BitsType MANTISSA_MASK =
-      FloatProperties<double>::MANTISSA_MASK;
-  static constexpr BitsType SIGN_MASK = FloatProperties<double>::SIGN_MASK;
-  static constexpr BitsType EXPONENT_MASK =
-      FloatProperties<double>::EXPONENT_MASK;
-  static constexpr uint32_t EXPONENT_BIAS =
-      FloatProperties<double>::EXPONENT_BIAS;
-
-  static constexpr BitsType EXP_MANT_MASK =
-      FloatProperties<double>::EXP_MANT_MASK;
-  static_assert(EXP_MANT_MASK == ~SIGN_MASK,
-                "Exponent and mantissa masks are not as expected.");
-
-  // If a number x is a NAN, then it is a quiet NAN if:
-  //   QuietNaNMask & bits(x) != 0
-  // Else, it is a signalling NAN.
-  static constexpr BitsType QUIET_NAN_MASK =
-      FloatProperties<double>::QUIET_NAN_MASK;
-};
-#elif defined(SPECIAL_X86_LONG_DOUBLE)
 // Properties for numbers represented in 80 bits long double on non-Windows x86
 // platforms.
-template <> struct FloatProperties<long double> {
+template <> struct FPProperties<FPType::X86_Binary80> {
   typedef UInt128 BitsType;
-  static_assert(sizeof(BitsType) == sizeof(long double),
-                "Unexpected size of 'long double' type.");
 
   static constexpr uint32_t BIT_WIDTH = (sizeof(BitsType) * 8) - 48;
   static constexpr BitsType FULL_WIDTH_MASK = ((BitsType(1) << BIT_WIDTH) - 1);
@@ -143,13 +110,11 @@ template <> struct FloatProperties<long double> {
   static constexpr BitsType QUIET_NAN_MASK = BitsType(1)
                                              << (MANTISSA_WIDTH - 1);
 };
-#else
+
 // Properties for numbers represented in 128 bits long double on non x86
 // platform.
-template <> struct FloatProperties<long double> {
+template <> struct FPProperties<FPType::IEEE754_Binary128> {
   typedef UInt128 BitsType;
-  static_assert(sizeof(BitsType) == sizeof(long double),
-                "Unexpected size of 'long double' type.");
 
   static constexpr uint32_t BIT_WIDTH = sizeof(BitsType) << 3;
 
@@ -172,39 +137,39 @@ template <> struct FloatProperties<long double> {
   static constexpr BitsType QUIET_NAN_MASK = BitsType(1)
                                              << (MANTISSA_WIDTH - 1);
 };
-#endif
 
-#if (defined(LIBC_COMPILER_HAS_FLOAT128) &&                                    \
-     !defined(LIBC_FLOAT128_IS_LONG_DOUBLE))
-// Properties for numbers represented in 128 bits long double on non x86
-// platform.
-template <> struct FloatProperties<float128> {
-  typedef UInt128 BitsType;
-  static_assert(sizeof(BitsType) == sizeof(float128),
-                "Unexpected size of 'float128' type.");
-
-  static constexpr uint32_t BIT_WIDTH = sizeof(BitsType) << 3;
-
-  static constexpr uint32_t MANTISSA_WIDTH = 112;
-  static constexpr uint32_t MANTISSA_PRECISION = MANTISSA_WIDTH + 1;
-  static constexpr uint32_t EXPONENT_WIDTH = 15;
-  static constexpr BitsType MANTISSA_MASK = (BitsType(1) << MANTISSA_WIDTH) - 1;
-  static constexpr BitsType SIGN_MASK = BitsType(1)
-                                        << (EXPONENT_WIDTH + MANTISSA_WIDTH);
-  static constexpr BitsType EXPONENT_MASK = ~(SIGN_MASK | MANTISSA_MASK);
-  static constexpr uint32_t EXPONENT_BIAS = 16383;
-
-  static constexpr BitsType EXP_MANT_MASK = MANTISSA_MASK | EXPONENT_MASK;
-  static_assert(EXP_MANT_MASK == ~SIGN_MASK,
-                "Exponent and mantissa masks are not as expected.");
+//-----------------------------------------------------------------------------
+template <typename FP> static constexpr FPType getFPType() {
+  if constexpr (cpp::is_same_v<FP, float> && __FLT_MANT_DIG__ == 24)
+    return FPType::IEEE754_Binary32;
+  else if constexpr (cpp::is_same_v<FP, double> && __DBL_MANT_DIG__ == 53)
+    return FPType::IEEE754_Binary64;
+  else if constexpr (cpp::is_same_v<FP, long double>) {
+    if constexpr (__LDBL_MANT_DIG__ == 53)
+      return FPType::IEEE754_Binary64;
+    else if constexpr (__LDBL_MANT_DIG__ == 64)
+      return FPType::X86_Binary80;
+    else if constexpr (__LDBL_MANT_DIG__ == 113)
+      return FPType::IEEE754_Binary128;
+  }
+#if defined(LIBC_COMPILER_HAS_C23_FLOAT16)
+  else if constexpr (cpp::is_same_v<FP, _Float16>)
+    return FPType::IEEE754_Binary16;
+#endif
+#if defined(LIBC_COMPILER_HAS_C23_FLOAT128)
+  else if constexpr (cpp::is_same_v<FP, _Float128>)
+    return FPType::IEEE754_Binary128;
+#endif
+#if defined(LIBC_COMPILER_HAS_FLOAT128_EXTENSION)
+  else if constexpr (cpp::is_same_v<FP, __float128>)
+    return FPType::IEEE754_Binary128;
+#endif
+  else
+    static_assert(cpp::always_false<FP>, "Unsupported type");
+}
 
-  // If a number x is a NAN, then it is a quiet NAN if:
-  //   QuietNaNMask & bits(x) != 0
-  // Else, it is a signalling NAN.
-  static constexpr BitsType QUIET_NAN_MASK = BitsType(1)
-                                             << (MANTISSA_WIDTH - 1);
-};
-#endif // LIBC_COMPILER_HAS_FLOAT128
+template <typename FP>
+struct FloatProperties : public FPProperties<getFPType<FP>()> {};
 
 } // namespace fputil
 } // namespace LIBC_NAMESPACE
diff --git a/libc/src/__support/FPUtil/dyadic_float.h b/libc/src/__support/FPUtil/dyadic_float.h
index 2ee97b4ac7f7836..b7920943804e610 100644
--- a/libc/src/__support/FPUtil/dyadic_float.h
+++ b/libc/src/__support/FPUtil/dyadic_float.h
@@ -38,13 +38,11 @@ template <size_t Bits> struct DyadicFloat {
   int exponent = 0;
   MantissaType mantissa = MantissaType(0);
 
-  DyadicFloat() = default;
+  constexpr DyadicFloat() = default;
 
-  template <typename T,
-            cpp::enable_if_t<cpp::is_floating_point_v<T> &&
-                                 (FloatProperties<T>::MANTISSA_WIDTH < Bits),
-                             int> = 0>
+  template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
   DyadicFloat(T x) {
+    static_assert(FloatProperties<T>::MANTISSA_WIDTH < Bits);
     FPBits<T> x_bits(x);
     sign = x_bits.get_sign();
     exponent = x_bits.get_exponent() - FloatProperties<T>::MANTISSA_WIDTH;
diff --git a/libc/src/__support/macros/properties/float.h b/libc/src/__support/macros/properties/float.h
index c40ca6120e4753e..3bfd04156893699 100644
--- a/libc/src/__support/macros/properties/float.h
+++ b/libc/src/__support/macros/properties/float.h
@@ -15,32 +15,70 @@
 #include "src/__support/macros/properties/compiler.h"
 #include "src/__support/macros/properties/os.h"
 
-// https://developer.arm.com/documentation/dui0491/i/C-and-C---Implementation-Details/Basic-data-types
-// https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms
-// https://docs.amd.com/bundle/HIP-Programming-Guide-v5.1/page/Programming_with_HIP.html
-#if defined(LIBC_TARGET_OS_IS_WINDOWS) ||                                      \
-    (defined(LIBC_TARGET_OS_IS_MACOS) &&                                       \
-     defined(LIBC_TARGET_ARCH_IS_AARCH64)) ||                                  \
-    defined(LIBC_TARGET_ARCH_IS_ARM) || defined(LIBC_TARGET_ARCH_IS_NVPTX) ||  \
-    defined(LIBC_TARGET_ARCH_IS_AMDGPU)
+#include <float.h> // LDBL_MANT_DIG
+
+// 'long double' properties.
+#if (LDBL_MANT_DIG == DBL_MANT_DIG)
+// TODO: Replace with LIBC_LONG_DOUBLE_IS_DOUBLE
 #define LONG_DOUBLE_IS_DOUBLE
 #endif
-
-#if !defined(LONG_DOUBLE_IS_DOUBLE) && defined(LIBC_TARGET_ARCH_IS_X86)
+#if (LDBL_MANT_DIG == 64)
+// TODO: Replace with LIBC_LONG_DOUBLE_IS_X86_BIN80
 #define SPECIAL_X86_LONG_DOUBLE
+#elif (LDBL_MANT_DIG == 113)
+#define LIBC_LONG_DOUBLE_IS_IEEE754_BIN128
 #endif
 
-// Check compiler features
-#if defined(FLT128_MANT_DIG)
-#define LIBC_COMPILER_HAS_FLOAT128
+// float16 support.
+#if defined(LIBC_TARGET_ARCH_IS_X86_64)
+#if (defined(LIBC_COMPILER_CLANG_VER) && (LIBC_COMPILER_CLANG_VER >= 1500)) || \
+    (defined(LIBC_COMPILER_GCC_VER) && (LIBC_COMPILER_GCC_VER >= 1201))
+#define LIBC_COMPILER_HAS_C23_FLOAT16
+#endif
+#endif
+#if defined(LIBC_TARGET_ARCH_IS_AARCH64)
+#if (defined(LIBC_COMPILER_CLANG_VER) && (LIBC_COMPILER_CLANG_VER >= 0900)) || \
+    (defined(LIBC_COMPILER_GCC_VER) && (LIBC_COMPILER_GCC_VER >= 1301))
+#define LIBC_COMPILER_HAS_C23_FLOAT16
+#endif
+#endif
+#if defined(LIBC_TARGET_ARCH_IS_ANY_RISCV)
+#if (defined(LIBC_COMPILER_CLANG_VER) && (LIBC_COMPILER_CLANG_VER >= 1300)) || \
+    (defined(LIBC_COMPILER_GCC_VER) && (LIBC_COMPILER_GCC_VER >= 1301))
+#define LIBC_COMPILER_HAS_C23_FLOAT16
+#endif
+#endif
+
+#if defined(LIBC_COMPILER_HAS_C23_FLOAT16)
+using float16 = _Float16;
+#define LIBC_HAS_FLOAT16
+#endif
+
+// float128 support.
+#if (defined(LIBC_COMPILER_GCC_VER) && (LIBC_COMPILER_GCC_VER >= 1301)) &&     \
+    (defined(LIBC_TARGET_ARCH_IS_AARCH64) ||                                   \
+     defined(LIBC_TARGET_ARCH_IS_ANY_RISCV) ||                                 \
+     defined(LIBC_TARGET_ARCH_IS_X86_64))
+#define LIBC_COMPILER_HAS_C23_FLOAT128
+#endif
+#if (defined(LIBC_COMPILER_CLANG_VER) && (LIBC_COMPILER_CLANG_VER >= 0500)) && \
+    (defined(LIBC_TARGET_ARCH_IS_X86_64))
+#define LIBC_COMPILER_HAS_FLOAT128_EXTENSION
+#endif
+
+#if defined(LIBC_COMPILER_HAS_C23_FLOAT128)
 using float128 = _Float128;
-#elif defined(__SIZEOF_FLOAT128__)
-#define LIBC_COMPILER_HAS_FLOAT128
+#elif defined(LIBC_COMPILER_HAS_FLOAT128_EXTENSION)
 using float128 = __float128;
-#elif (defined(__linux__) && defined(__aarch64__))
-#define LIBC_COMPILER_HAS_FLOAT128
-#define LIBC_FLOAT128_IS_LONG_DOUBLE
+#elif defined(LIBC_LONG_DOUBLE_IS_IEEE754_BIN128)
 using float128 = long double;
 #endif
 
+#if defined(LIBC_COMPILER_HAS_C23_FLOAT128) ||                                 \
+    defined(LIBC_COMPILER_HAS_FLOAT128_EXTENSION) ||                           \
+    defined(LIBC_LONG_DOUBLE_IS_IEEE754_BIN128)
+// TODO: Replace with LIBC_HAS_FLOAT128
+#define LIBC_COMPILER_HAS_FLOAT128
+#endif
+
 #endif // LLVM_LIBC_SRC___SUPPORT_MACROS_PROPERTIES_FLOAT_H

@gchatelet
Copy link
Contributor Author

gchatelet commented Nov 27, 2023

This is a follow up on #73372. Changes to libc/src/__support/macros/properties/float.h will disappear once it's submitted.

We simplify the floating point properties by splitting concerns:
 - We define all distinct floating point formats in the `FPType` enum.
 - We map them to properties via the `FPProperties` trait.
 - We map from C++ type to `FPType` in the `getFPType` function so logic is easier to understand and extend.
@gchatelet gchatelet force-pushed the decouple_properties_from_types branch from 027c6c3 to 4c2c55c Compare November 27, 2023 15:30
@gchatelet gchatelet merged commit c599b8e into llvm:main Nov 27, 2023
@gchatelet gchatelet deleted the decouple_properties_from_types branch November 28, 2023 09:27
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.

3 participants