aom_encoder: enable floating point exceptions

with --enable-debug (CONFIG_DEBUG) when available

BUG=aomedia:388

Change-Id: I5dd0718d6bd0ba17bc1b52d5842e250244d2d1d8
diff --git a/aom/src/aom_encoder.c b/aom/src/aom_encoder.c
index 41c70ef..ac84c88 100644
--- a/aom/src/aom_encoder.c
+++ b/aom/src/aom_encoder.c
@@ -13,9 +13,15 @@
  * \brief Provides the high level interface to wrap encoder algorithms.
  *
  */
+#include "./aom_config.h"
+
+#if HAVE_FEXCEPT
+#define _GNU_SOURCE
+#include <fenv.h>
+#endif
+
 #include <limits.h>
 #include <string.h>
-#include "aom_config.h"
 #include "aom/internal/aom_codec_internal.h"
 
 #define SAVE_STATUS(ctx, var) (ctx ? (ctx->err = var) : var)
@@ -168,23 +174,42 @@
   return res;
 }
 
+/* clang-format off */
+#define FLOATING_POINT_BEGIN_SCOPE do {
+#define FLOATING_POINT_END_SCOPE } while (0);
+/* clang-format on */
+
 #if ARCH_X86 || ARCH_X86_64
 /* On X86, disable the x87 unit's internal 80 bit precision for better
  * consistency with the SSE unit's 64 bit precision.
  */
 #include "aom_ports/x86.h"
-#define FLOATING_POINT_INIT() \
-  do {                        \
-    unsigned short x87_orig_mode = x87_set_double_precision();
-#define FLOATING_POINT_RESTORE()       \
-  x87_set_control_word(x87_orig_mode); \
-  }                                    \
-  while (0)
-
+#define FLOATING_POINT_SET_PRECISION \
+  unsigned short x87_orig_mode = x87_set_double_precision();
+#define FLOATING_POINT_RESTORE_PRECISION x87_set_control_word(x87_orig_mode);
 #else
-static void FLOATING_POINT_INIT() {}
-static void FLOATING_POINT_RESTORE() {}
-#endif
+#define FLOATING_POINT_SET_PRECISION
+#define FLOATING_POINT_RESTORE_PRECISION
+#endif  // ARCH_X86 || ARCH_X86_64
+
+#if HAVE_FEXCEPT && CONFIG_DEBUG
+#define FLOATING_POINT_SET_EXCEPTIONS \
+  const int float_excepts = feenableexcept(FE_DIVBYZERO);
+#define FLOATING_POINT_RESTORE_EXCEPTIONS feenableexcept(float_excepts);
+#else
+#define FLOATING_POINT_SET_EXCEPTIONS
+#define FLOATING_POINT_RESTORE_EXCEPTIONS
+#endif  // HAVE_FEXCEPT && CONFIG_DEBUG
+
+#define FLOATING_POINT_INIT    \
+  FLOATING_POINT_BEGIN_SCOPE   \
+  FLOATING_POINT_SET_PRECISION \
+  FLOATING_POINT_SET_EXCEPTIONS
+
+#define FLOATING_POINT_RESTORE      \
+  FLOATING_POINT_RESTORE_EXCEPTIONS \
+  FLOATING_POINT_RESTORE_PRECISION  \
+  FLOATING_POINT_END_SCOPE
 
 aom_codec_err_t aom_codec_encode(aom_codec_ctx_t *ctx, const aom_image_t *img,
                                  aom_codec_pts_t pts, unsigned long duration,
@@ -204,7 +229,7 @@
     /* Execute in a normalized floating point environment, if the platform
      * requires it.
      */
-    FLOATING_POINT_INIT();
+    FLOATING_POINT_INIT
 
     if (num_enc == 1)
       res = ctx->iface->enc.encode(get_alg_priv(ctx), img, pts, duration, flags,
@@ -231,7 +256,7 @@
       ctx++;
     }
 
-    FLOATING_POINT_RESTORE();
+    FLOATING_POINT_RESTORE
   }
 
   return SAVE_STATUS(ctx, res);
diff --git a/build/cmake/aom_config.h.cmake b/build/cmake/aom_config.h.cmake
index d565559..2e95e23 100644
--- a/build/cmake/aom_config.h.cmake
+++ b/build/cmake/aom_config.h.cmake
@@ -36,6 +36,7 @@
 #define HAVE_AVX ${HAVE_AVX}
 #define HAVE_AVX2 ${HAVE_AVX2}
 #define HAVE_AOM_PORTS ${HAVE_AOM_PORTS}
+#define HAVE_FEXCEPT ${HAVE_FEXCEPT}
 #define HAVE_PTHREAD_H ${HAVE_PTHREAD_H}
 #define HAVE_UNISTD_H ${HAVE_UNISTD_H}
 #define CONFIG_DEPENDENCY_TRACKING ${CONFIG_DEPENDENCY_TRACKING}
diff --git a/build/cmake/aom_config_defaults.cmake b/build/cmake/aom_config_defaults.cmake
index d457e58..441bdf3 100644
--- a/build/cmake/aom_config_defaults.cmake
+++ b/build/cmake/aom_config_defaults.cmake
@@ -36,6 +36,7 @@
 set(HAVE_AVX 0 CACHE BOOL "Enables AVX optimizations.")
 set(HAVE_AVX2 0 CACHE BOOL "Enables AVX2 optimizations.")
 set(HAVE_AOM_PORTS 0 CACHE BOOL "Internal flag, deprecated.")
+set(HAVE_FEXCEPT 0 CACHE BOOL "Internal flag, GNU fenv.h present for target.")
 set(HAVE_PTHREAD_H 0 CACHE BOOL "Internal flag, target pthread support.")
 set(HAVE_UNISTD_H 0 CACHE BOOL "Internal flag, unistd.h present for target.")
 set(CONFIG_DEPENDENCY_TRACKING 1 CACHE BOOL "Internal flag.")
diff --git a/build/cmake/aom_configure.cmake b/build/cmake/aom_configure.cmake
index 34e0142..31f826e 100644
--- a/build/cmake/aom_configure.cmake
+++ b/build/cmake/aom_configure.cmake
@@ -158,6 +158,17 @@
           "CONFIG_ANS and CONFIG_DAALA_EC cannot be enabled together.")
 endif ()
 
+if (NOT MSVC)
+  aom_push_var(CMAKE_REQUIRED_LIBRARIES "m")
+  aom_check_c_compiles("fenv_check"
+                       "#define _GNU_SOURCE
+                        #include <fenv.h>
+                        void unused(void) {
+                          (void)feenableexcept(FE_DIVBYZERO | FE_INVALID);
+                        }" HAVE_FEXCEPT)
+  aom_pop_var(CMAKE_REQUIRED_LIBRARIES)
+endif()
+
 # TODO(tomfinegan): consume trailing whitespace after configure_file() when
 # target platform check produces empty INLINE and RESTRICT values (aka empty
 # values require special casing).
diff --git a/build/cmake/compiler_tests.cmake b/build/cmake/compiler_tests.cmake
index e3c0e9a..e763597 100644
--- a/build/cmake/compiler_tests.cmake
+++ b/build/cmake/compiler_tests.cmake
@@ -21,6 +21,16 @@
 set(AOM_CXX_PASSED_TESTS)
 set(AOM_CXX_FAILED_TESTS)
 
+function(aom_push_var var new_value)
+  set(SAVED_${var} ${var} PARENT_SCOPE)
+  set(${var} ${new_value} PARENT_SCOPE)
+endfunction ()
+
+function(aom_pop_var var)
+  set(var ${SAVED_${var}} PARENT_SCOPE)
+  unset(SAVED_${var} PARENT_SCOPE)
+endfunction ()
+
 # Confirms $test_source compiles and stores $test_name in one of
 # $AOM_C_PASSED_TESTS or $AOM_C_FAILED_TESTS depending on out come. When the
 # test passes $result_var is set to 1. When it fails $result_var is unset.
diff --git a/configure b/configure
index 5a24bed..ad240ba 100755
--- a/configure
+++ b/configure
@@ -238,6 +238,7 @@
 HAVE_LIST="
     ${ARCH_EXT_LIST}
     aom_ports
+    fexcept
     pthread_h
     unistd_h
     wxwidgets
@@ -649,6 +650,12 @@
     check_header unistd.h # for sysconf(3) and friends.
 
     check_header aom/aom_integer.h -I${source_path} && enable_feature aom_ports
+
+    check_ld <<EOF && enable_feature fexcept
+#define _GNU_SOURCE
+#include <fenv.h>
+int main(void) { (void)feenableexcept(FE_DIVBYZERO | FE_INVALID); return 0; }
+EOF
 }
 
 process_toolchain() {