[libc] Match x86 long double NaN classification with that of the compiler.
authorSiva Chandra Reddy <sivachandra@google.com>
Mon, 22 Jun 2020 20:47:56 +0000 (13:47 -0700)
committerSiva Chandra Reddy <sivachandra@google.com>
Tue, 23 Jun 2020 06:02:05 +0000 (23:02 -0700)
Reviewers: asteinhauser

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

libc/test/CMakeLists.txt
libc/test/utils/CMakeLists.txt [new file with mode: 0644]
libc/test/utils/FPUtil/CMakeLists.txt [new file with mode: 0644]
libc/test/utils/FPUtil/x86_long_double_test.cpp [new file with mode: 0644]
libc/utils/FPUtil/LongDoubleBitsX86.h

index aa77dcd..500b294 100644 (file)
@@ -3,3 +3,4 @@ add_custom_target(check-libc)
 add_subdirectory(config)
 add_subdirectory(loader)
 add_subdirectory(src)
+add_subdirectory(utils)
diff --git a/libc/test/utils/CMakeLists.txt b/libc/test/utils/CMakeLists.txt
new file mode 100644 (file)
index 0000000..90ff4bb
--- /dev/null
@@ -0,0 +1 @@
+add_subdirectory(FPUtil)
diff --git a/libc/test/utils/FPUtil/CMakeLists.txt b/libc/test/utils/FPUtil/CMakeLists.txt
new file mode 100644 (file)
index 0000000..65cb677
--- /dev/null
@@ -0,0 +1,10 @@
+if((${LIBC_TARGET_OS} STREQUAL "linux") AND (${LIBC_TARGET_MACHINE} MATCHES "i386|x86_64"))
+  add_libc_unittest(
+    x86_long_double_test
+    SRCS
+      x86_long_double_test.cpp
+    DEPENDS
+      libc.include.math
+      libc.utils.FPUtil.fputil
+  )
+endif()
diff --git a/libc/test/utils/FPUtil/x86_long_double_test.cpp b/libc/test/utils/FPUtil/x86_long_double_test.cpp
new file mode 100644 (file)
index 0000000..d4fb870
--- /dev/null
@@ -0,0 +1,85 @@
+//===-- Unittests for x86 long double -------------------------------------===//
+//
+// 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 "utils/FPUtil/FPBits.h"
+#include "utils/UnitTest/Test.h"
+
+using FPBits = __llvm_libc::fputil::FPBits<long double>;
+
+TEST(X86LongDoubleTest, isNaN) {
+  // In the nan checks below, we use the macro isnan from math.h to ensure that
+  // a number is actually a NaN. The isnan macro resolves to the compiler
+  // builtin function. Hence, matching LLVM-libc's notion of NaN with the
+  // isnan result ensures that LLVM-libc's behavior matches the compiler's
+  // behavior.
+
+  FPBits bits(0.0l);
+  bits.exponent = FPBits::maxExponent;
+  for (unsigned int i = 0; i < 1000000; ++i) {
+    // If exponent has the max value and the implicit bit is 0,
+    // then the number is a NaN for all values of mantissa.
+    bits.mantissa = i;
+    long double nan = bits;
+    ASSERT_NE(isnan(nan), 0);
+    ASSERT_TRUE(bits.isNaN());
+  }
+
+  bits.implicitBit = 1;
+  for (unsigned int i = 1; i < 1000000; ++i) {
+    // If exponent has the max value and the implicit bit is 1,
+    // then the number is a NaN for all non-zero values of mantissa.
+    // Note the initial value of |i| of 1 to avoid a zero mantissa.
+    bits.mantissa = i;
+    long double nan = bits;
+    ASSERT_NE(isnan(nan), 0);
+    ASSERT_TRUE(bits.isNaN());
+  }
+
+  bits.exponent = 1;
+  bits.implicitBit = 0;
+  for (unsigned int i = 0; i < 1000000; ++i) {
+    // If exponent is non-zero and also not max, and the implicit bit is 0,
+    // then the number is a NaN for all values of mantissa.
+    bits.mantissa = i;
+    long double nan = bits;
+    ASSERT_NE(isnan(nan), 0);
+    ASSERT_TRUE(bits.isNaN());
+  }
+
+  bits.exponent = 1;
+  bits.implicitBit = 1;
+  for (unsigned int i = 0; i < 1000000; ++i) {
+    // If exponent is non-zero and also not max, and the implicit bit is 1,
+    // then the number is normal value for all values of mantissa.
+    bits.mantissa = i;
+    long double valid = bits;
+    ASSERT_EQ(isnan(valid), 0);
+    ASSERT_FALSE(bits.isNaN());
+  }
+
+  bits.exponent = 0;
+  bits.implicitBit = 1;
+  for (unsigned int i = 0; i < 1000000; ++i) {
+    // If exponent is zero, then the number is a valid but denormal value.
+    bits.mantissa = i;
+    long double valid = bits;
+    ASSERT_EQ(isnan(valid), 0);
+    ASSERT_FALSE(bits.isNaN());
+  }
+
+  bits.exponent = 0;
+  bits.implicitBit = 0;
+  for (unsigned int i = 0; i < 1000000; ++i) {
+    // If exponent is zero, then the number is a valid but denormal value.
+    bits.mantissa = i;
+    long double valid = bits;
+    ASSERT_EQ(isnan(valid), 0);
+    ASSERT_FALSE(bits.isNaN());
+  }
+}
index 1e92dba..3d7f455 100644 (file)
@@ -62,9 +62,18 @@ template <> struct __attribute__((packed)) FPBits<long double> {
     return exponent == maxExponent && mantissa == 0 && implicitBit == 1;
   }
 
-  bool isNaN() const { return exponent == maxExponent && mantissa != 0; }
+  bool isNaN() const {
+    if (exponent == maxExponent) {
+      return (implicitBit == 0) || mantissa != 0;
+    } else if (exponent != 0) {
+      return implicitBit == 0;
+    }
+    return false;
+  }
 
-  bool isInfOrNaN() const { return exponent == maxExponent; }
+  bool isInfOrNaN() const {
+    return (exponent == maxExponent) || (exponent != 0 && implicitBit == 0);
+  }
 
   // Methods below this are used by tests.