diff --git a/configure.ac b/configure.ac index c9405dd6d..610851fa6 100644 --- a/configure.ac +++ b/configure.ac @@ -366,6 +366,33 @@ AM_COND_IF([GENERIC], CXXFLAGS+=" -march=native" ]) +dnl !!! Experimental !!! +dnl +dnl Allows a signal handler to throw a C++ exception if the signal is from +dnl a CPU trap on an instruction (ex. SIGFPE from a floating point divide +dnl by zero). This allows the signal handler to transfer control to the +dnl catch block wrapping the area which errored, thereby aborting any further +dnl execution after the signal was received which would have to manually check +dnl for such an error after each floating point operation and throw. + +AC_ARG_ENABLE(asyncexcept, AC_HELP_STRING([--enable-asyncexcept], [Enable experimental asynchronous exceptions]), +[ + asyncexcept="yes" +], [ + asyncexcept="no" +]) + +dnl CHARYBDIS_C_GCC_TRY_FLAGS([-fnon-call-exceptions], charybdis_cv_c_gcc_f_non_call_exceptions) + +if test "$asyncexcept" = "yes"; then + IRCD_DEFINE(USE_ASYNC_EXCEPTIONS, [1], [Experimental asynchronous exceptions are supported]) + CXXFLAGS+=" -fasynchronous-unwind-tables" + CXXFLAGS+=" -fnon-call-exceptions" +fi + +AM_CONDITIONAL([ASYNCEXCEPT], [[[[ $asyncexcept = "yes" ]]]]) + + dnl dnl Optimization dnl @@ -415,15 +442,6 @@ dnl CXXFLAGS+=" -fcheck-pointer-bounds" CXXFLAGS+=" -fchkp-instrument-marked-only" CXXFLAGS+=" -fstack-protector-explicit" -dnl !!! Experimental !!! -dnl Allows a signal handler to throw a C++ exception if the signal is from -dnl a CPU trap on an instruction (ex. SIGFPE from a floating point divide -dnl by zero). This allows the signal handler to transfer control to the -dnl catch block wrapping the area which errored, thereby aborting any further -dnl execution after the SIGFPE was received which would have to manually check -dnl for such an error after each floating point operation and throw. -CXXFLAGS+=" -fnon-call-exceptions" - dnl -ffriend-injection allows us to deduplicate declarations of friend dnl functions in both the friend class and the enclosing namespace CXXFLAGS+=" -ffriend-injection" @@ -636,6 +654,7 @@ RB_CHK_SYSHEADER(regex, [REGEX]) dnl unix platform RB_CHK_SYSHEADER(unistd.h, [UNISTD_H]) +RB_CHK_SYSHEADER(signal.h, [SIGNAL_H]) RB_CHK_SYSHEADER(sys/time.h, [SYS_TIME_H]) RB_CHK_SYSHEADER(sys/stat.h, [SYS_STAT_H]) RB_CHK_SYSHEADER(sys/statvfs.h, [SYS_STATVFS_H]) diff --git a/include/ircd/fpe.h b/include/ircd/fpe.h index a7a7aac9e..f7d7bc4be 100644 --- a/include/ircd/fpe.h +++ b/include/ircd/fpe.h @@ -11,11 +11,6 @@ #pragma once #define HAVE_IRCD_FPE_H -extern "C" -{ - struct sigaction; -}; - namespace ircd::fpe { struct errors_handle; @@ -27,6 +22,9 @@ namespace ircd::fpe void throw_errors(const ushort &flags); } +/// Perform a single floating point operation at a time within the scope +/// of fpe::errors_handle. After each operation check the floating point +/// unit for an error status flag and throw a C++ exception. struct ircd::fpe::errors_handle { fexcept_t theirs; @@ -40,6 +38,18 @@ struct ircd::fpe::errors_handle ~errors_handle() noexcept(false); }; +// We don't include in IRCd's standard include list. Instead that +// is conditionally included in the appropriate .cc file. All we require here +// is a forward declaration. +extern "C" +{ + struct sigaction; +}; + +/// Experimental floating-point error handling strategy which throws +/// a C++ exception directly from the instruction which faulted the FPU. +/// The stack will unwind directly from that point in the execution and +/// control will be transferred to the appropriate catch block. struct ircd::fpe::errors_throw { custom_ptr their_sa; diff --git a/ircd/fpe.cc b/ircd/fpe.cc index a3ea62cc4..e1203463c 100644 --- a/ircd/fpe.cc +++ b/ircd/fpe.cc @@ -8,7 +8,7 @@ // copyright notice and this permission notice is present in all copies. The // full license for this software is available in the LICENSE file. -#include +#include sa_heap; - extern const std::exception_ptr sigfpe_eptr; extern "C" void ircd_fpe_handle_sigfpe(int signum, siginfo_t *const si, void *const uctx); } -decltype(ircd::fpe::sigfpe_eptr) -ircd::fpe::sigfpe_eptr -{ - std::make_exception_ptr(std::domain_error - { - "Floating Point Exception" - }) -}; - -__attribute__((noreturn)) -void +[[noreturn]] void ircd::fpe::ircd_fpe_handle_sigfpe(int signum, siginfo_t *const si, void *const uctx) { assert(si); assert(signum == SIGFPE); - std::rethrow_exception(sigfpe_eptr); //TODO: still malloc()'s :/ + //TODO: due to __cxa_allocate_exception() this still malloc()'s :/ + // We can't have a floating point error within malloc() and family + // or this signal will cleave it at an intermediate state and reenter. + throw std::domain_error + { + reflect_sicode(si->si_code) + }; } // @@ -86,6 +90,29 @@ noexcept syscall(std::fesetexceptflag, &their_fe, FE_ALL_EXCEPT); } +#else // IRCD_USE_ASYNC_EXCEPTIONS + +// +// errors_throw::errors_throw +// + +__attribute__((noreturn)) +ircd::fpe::errors_throw::errors_throw() +{ + throw assertive + { + "Not implemented in this environment." + }; +} + +ircd::fpe::errors_throw::~errors_throw() +noexcept +{ + assert(0); +} + +#endif // IRCD_USE_ASYNC_EXCEPTIONS + // // errors_handle // @@ -182,14 +209,16 @@ ircd::fpe::reflect_sicode(const int &code) { switch(code) { - case FPE_INTDIV: return "INTDIV"; - case FPE_INTOVF: return "INTOVF"; - case FPE_FLTDIV: return "FLTDIV"; - case FPE_FLTOVF: return "FLTOVF"; - case FPE_FLTUND: return "FLTUND"; - case FPE_FLTRES: return "FLTRES"; - case FPE_FLTINV: return "FLTINV"; - case FPE_FLTSUB: return "FLTSUB"; + #ifdef HAVE_SIGNAL_H + case FPE_INTDIV: return "INTDIV"; + case FPE_INTOVF: return "INTOVF"; + case FPE_FLTDIV: return "FLTDIV"; + case FPE_FLTOVF: return "FLTOVF"; + case FPE_FLTUND: return "FLTUND"; + case FPE_FLTRES: return "FLTRES"; + case FPE_FLTINV: return "FLTINV"; + case FPE_FLTSUB: return "FLTSUB"; + #endif // HAVE_SIGNAL_H } return "?????";