[libc-commits] [libc] ec14ab9 - [libc] Add a new test matcher for tests raising floating point exceptions.

Siva Chandra Reddy via libc-commits libc-commits at lists.llvm.org
Tue Jul 20 21:59:48 PDT 2021


Author: Siva Chandra Reddy
Date: 2021-07-21T04:59:39Z
New Revision: ec14ab96242144ac18ab32963d66855a0a6750c8

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

LOG: [libc] Add a new test matcher for tests raising floating point exceptions.

This new matcher does not use death tests to check if SIGFPE is raised.
Instead, that a SIGFPE was raised is checked using a SIGFPE signal handler.

Reviewed By: mcgrathr

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

Added: 
    

Modified: 
    libc/test/src/CMakeLists.txt
    libc/test/src/fenv/CMakeLists.txt
    libc/test/src/fenv/enabled_exceptions_test.cpp
    libc/test/src/fenv/feholdexcept_test.cpp
    libc/test/src/math/CMakeLists.txt
    libc/utils/FPUtil/TestHelpers.cpp
    libc/utils/FPUtil/TestHelpers.h

Removed: 
    


################################################################################
diff  --git a/libc/test/src/CMakeLists.txt b/libc/test/src/CMakeLists.txt
index d4689d9a24dc6..5fc3fbbc448df 100644
--- a/libc/test/src/CMakeLists.txt
+++ b/libc/test/src/CMakeLists.txt
@@ -1,3 +1,30 @@
+function(add_fp_unittest name)
+  cmake_parse_arguments(
+    "MATH_UNITTEST"
+    "NEED_MPFR" # Optional arguments
+    "" # Single value arguments
+    "" # Multi-value arguments
+    ${ARGN}
+  )
+
+  if(MATH_UNITTEST_NEED_MPFR)
+    if(NOT LIBC_TESTS_CAN_USE_MPFR)
+      message("WARNING: Math test ${name} will be skipped as MPFR library is not available.")
+      return()
+    endif()
+  endif()
+
+  add_libc_unittest(${name} ${MATH_UNITTEST_UNPARSED_ARGUMENTS})
+  get_fq_target_name(${name} fq_target_name)
+  if (NOT TARGET ${fq_target_name})
+    return()
+  endif()
+  if(MATH_UNITTEST_NEED_MPFR)
+    target_link_libraries(${fq_target_name} PRIVATE libcMPFRWrapper -lmpfr -lgmp)
+  endif()
+  target_link_libraries(${fq_target_name} PRIVATE LibcFPTestHelpers)
+endfunction(add_fp_unittest)
+
 add_subdirectory(__support)
 add_subdirectory(ctype)
 add_subdirectory(errno)

diff  --git a/libc/test/src/fenv/CMakeLists.txt b/libc/test/src/fenv/CMakeLists.txt
index 1fa3e687e5cbb..88d9663ae472d 100644
--- a/libc/test/src/fenv/CMakeLists.txt
+++ b/libc/test/src/fenv/CMakeLists.txt
@@ -74,7 +74,7 @@ add_libc_unittest(
 if (NOT LLVM_USE_SANITIZER)
   # Sanitizers don't like SIGFPE. So, we will run the 
   # tests which raise SIGFPE only in non-sanitizer builds.
-  add_libc_unittest(
+  add_fp_unittest(
     enabled_exceptions_test
     SUITE
       libc_fenv_unittests
@@ -88,14 +88,14 @@ if (NOT LLVM_USE_SANITIZER)
       libc.utils.FPUtil.fputil
   )
 
-  add_libc_unittest(
+  add_fp_unittest(
     feholdexcept_test
     SUITE
       libc_fenv_unittests
     SRCS
       feholdexcept_test.cpp
     DEPENDS
-      libc.include.signal
+      libc.include.fenv
       libc.src.fenv.feholdexcept
       libc.utils.FPUtil.fputil
   )

diff  --git a/libc/test/src/fenv/enabled_exceptions_test.cpp b/libc/test/src/fenv/enabled_exceptions_test.cpp
index 8b8649df3ae98..a403f247d5709 100644
--- a/libc/test/src/fenv/enabled_exceptions_test.cpp
+++ b/libc/test/src/fenv/enabled_exceptions_test.cpp
@@ -11,6 +11,7 @@
 #include "src/fenv/fetestexcept.h"
 
 #include "utils/FPUtil/FEnv.h"
+#include "utils/FPUtil/TestHelpers.h"
 #include "utils/UnitTest/Test.h"
 
 #include <fenv.h>
@@ -34,23 +35,16 @@ TEST(LlvmLibcExceptionStatusTest, RaiseAndCrash) {
       FE_DIVBYZERO | FE_INVALID | FE_INEXACT | FE_OVERFLOW | FE_UNDERFLOW;
 
   for (int e : excepts) {
-    ASSERT_DEATH(
-        [=] {
-          __llvm_libc::fputil::disableExcept(FE_ALL_EXCEPT);
-          __llvm_libc::fputil::enableExcept(e);
-          ASSERT_EQ(__llvm_libc::feclearexcept(FE_ALL_EXCEPT), 0);
-          // Raising all exceptions except |e| should not call the
-          // SIGFPE handler. They should set the exception flag though,
-          // so we verify that.
-          int others = allExcepts & ~e;
-          ASSERT_EQ(__llvm_libc::feraiseexcept(others), 0);
-          ASSERT_EQ(__llvm_libc::fetestexcept(others), others);
-
-          // We don't check the return value when raising |e| as
-          // feraiseexcept will not return when it raises an enabled
-          // exception.
-          __llvm_libc::feraiseexcept(e);
-        },
-        WITH_SIGNAL(SIGFPE));
+    __llvm_libc::fputil::disableExcept(FE_ALL_EXCEPT);
+    __llvm_libc::fputil::enableExcept(e);
+    ASSERT_EQ(__llvm_libc::feclearexcept(FE_ALL_EXCEPT), 0);
+    // Raising all exceptions except |e| should not call the
+    // SIGFPE handler. They should set the exception flag though,
+    // so we verify that.
+    int others = allExcepts & ~e;
+    ASSERT_EQ(__llvm_libc::feraiseexcept(others), 0);
+    ASSERT_EQ(__llvm_libc::fetestexcept(others), others);
+
+    ASSERT_RAISES_FP_EXCEPT([=] { __llvm_libc::feraiseexcept(e); });
   }
 }

diff  --git a/libc/test/src/fenv/feholdexcept_test.cpp b/libc/test/src/fenv/feholdexcept_test.cpp
index 2517b3ebbcb20..0b860a9534514 100644
--- a/libc/test/src/fenv/feholdexcept_test.cpp
+++ b/libc/test/src/fenv/feholdexcept_test.cpp
@@ -9,10 +9,10 @@
 #include "src/fenv/feholdexcept.h"
 
 #include "utils/FPUtil/FEnv.h"
+#include "utils/FPUtil/TestHelpers.h"
 #include "utils/UnitTest/Test.h"
 
 #include <fenv.h>
-#include <signal.h>
 
 TEST(LlvmLibcFEnvTest, RaiseAndCrash) {
   int excepts[] = {FE_DIVBYZERO, FE_INVALID, FE_INEXACT, FE_OVERFLOW,
@@ -31,7 +31,6 @@ TEST(LlvmLibcFEnvTest, RaiseAndCrash) {
     // When we put back the saved env which has the exception enabled, it
     // should crash with SIGFPE.
     __llvm_libc::fputil::setEnv(&env);
-    ASSERT_DEATH([=] { __llvm_libc::fputil::raiseExcept(e); },
-                 WITH_SIGNAL(SIGFPE));
+    ASSERT_RAISES_FP_EXCEPT([=] { __llvm_libc::fputil::raiseExcept(e); });
   }
 }

diff  --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt
index 44cdb7afe089d..321ac79361376 100644
--- a/libc/test/src/math/CMakeLists.txt
+++ b/libc/test/src/math/CMakeLists.txt
@@ -1,32 +1,5 @@
 add_libc_testsuite(libc_math_unittests)
 
-function(add_fp_unittest name)
-  cmake_parse_arguments(
-    "MATH_UNITTEST"
-    "NEED_MPFR" # Optional arguments
-    "" # Single value arguments
-    "" # Multi-value arguments
-    ${ARGN}
-  )
-
-  if(MATH_UNITTEST_NEED_MPFR)
-    if(NOT LIBC_TESTS_CAN_USE_MPFR)
-      message("WARNING: Math test ${name} will be skipped as MPFR library is not available.")
-      return()
-    endif()
-  endif()
-
-  add_libc_unittest(${name} ${MATH_UNITTEST_UNPARSED_ARGUMENTS})
-  get_fq_target_name(${name} fq_target_name)
-  if (NOT TARGET ${fq_target_name})
-    return()
-  endif()
-  if(MATH_UNITTEST_NEED_MPFR)
-    target_link_libraries(${fq_target_name} PRIVATE libcMPFRWrapper -lmpfr -lgmp)
-  endif()
-  target_link_libraries(${fq_target_name} PRIVATE LibcFPTestHelpers)
-endfunction(add_fp_unittest)
-
 add_fp_unittest(
   cosf_test
   NEED_MPFR

diff  --git a/libc/utils/FPUtil/TestHelpers.cpp b/libc/utils/FPUtil/TestHelpers.cpp
index 2c7614e93053f..5f33eedf9bc69 100644
--- a/libc/utils/FPUtil/TestHelpers.cpp
+++ b/libc/utils/FPUtil/TestHelpers.cpp
@@ -8,8 +8,12 @@
 
 #include "TestHelpers.h"
 
+#include "FEnv.h"
 #include "FPBits.h"
 
+#include <memory>
+#include <setjmp.h>
+#include <signal.h>
 #include <string>
 
 namespace __llvm_libc {
@@ -70,6 +74,36 @@ template void describeValue<double>(const char *, double,
 template void describeValue<long double>(const char *, long double,
                                          testutils::StreamWrapper &);
 
+#if defined(__WIN32) || defined(_WIN64)
+#define sigjmp_buf jmp_buf
+#define sigsetjmp(buf, save) setjmp(buf)
+#define siglongjmp(buf) longjmp(buf)
+#endif
+
+static thread_local sigjmp_buf jumpBuffer;
+static thread_local bool caughtExcept;
+
+static void sigfpeHandler(int sig) {
+  caughtExcept = true;
+  siglongjmp(jumpBuffer, -1);
+}
+
+FPExceptMatcher::FPExceptMatcher(FunctionCaller *func) {
+  auto oldSIGFPEHandler = signal(SIGFPE, &sigfpeHandler);
+  std::unique_ptr<FunctionCaller> funcUP(func);
+
+  caughtExcept = false;
+  fenv_t oldEnv;
+  fegetenv(&oldEnv);
+  if (sigsetjmp(jumpBuffer, 1) == 0)
+    funcUP->call();
+  // We restore the previous floating point environment after
+  // the call to the function which can potentially raise SIGFPE.
+  fesetenv(&oldEnv);
+  signal(SIGFPE, oldSIGFPEHandler);
+  exceptionRaised = caughtExcept;
+}
+
 } // namespace testing
 } // namespace fputil
 } // namespace __llvm_libc

diff  --git a/libc/utils/FPUtil/TestHelpers.h b/libc/utils/FPUtil/TestHelpers.h
index 263eace786fd8..286f4437055d8 100644
--- a/libc/utils/FPUtil/TestHelpers.h
+++ b/libc/utils/FPUtil/TestHelpers.h
@@ -61,6 +61,39 @@ FPMatcher<T, C> getMatcher(T expectedValue) {
   return FPMatcher<T, C>(expectedValue);
 }
 
+// TODO: Make the matcher match specific exceptions instead of just identifying
+// that an exception was raised.
+class FPExceptMatcher : public __llvm_libc::testing::Matcher<bool> {
+  bool exceptionRaised;
+
+public:
+  class FunctionCaller {
+  public:
+    virtual ~FunctionCaller(){};
+    virtual void call() = 0;
+  };
+
+  template <typename Func> static FunctionCaller *getFunctionCaller(Func func) {
+    struct Callable : public FunctionCaller {
+      Func f;
+      explicit Callable(Func theFunc) : f(theFunc) {}
+      void call() override { f(); }
+    };
+
+    return new Callable(func);
+  }
+
+  // Takes ownership of func.
+  explicit FPExceptMatcher(FunctionCaller *func);
+
+  bool match(bool unused) { return exceptionRaised; }
+
+  void explainError(testutils::StreamWrapper &stream) override {
+    stream << "A floating point exception should have been raised but it "
+           << "wasn't\n";
+  }
+};
+
 } // namespace testing
 } // namespace fputil
 } // namespace __llvm_libc
@@ -98,4 +131,15 @@ FPMatcher<T, C> getMatcher(T expectedValue) {
       __llvm_libc::fputil::testing::getMatcher<__llvm_libc::testing::Cond_NE>( \
           expected))
 
+#ifdef LLVM_LIBC_TEST_USE_FUCHSIA
+#define ASSERT_RAISES_FP_EXCEPT(func) ASSERT_DEATH(func, WITH_SIGNAL(SIGFPE))
+#else
+#define ASSERT_RAISES_FP_EXCEPT(func)                                          \
+  ASSERT_THAT(                                                                 \
+      true,                                                                    \
+      __llvm_libc::fputil::testing::FPExceptMatcher(                           \
+          __llvm_libc::fputil::testing::FPExceptMatcher::getFunctionCaller(    \
+              func)))
+#endif // LLVM_LIBC_TEST_USE_FUCHSIA
+
 #endif // LLVM_LIBC_UTILS_FPUTIL_TEST_HELPERS_H


        


More information about the libc-commits mailing list