From 8a4a20730b156441194acd7ff242ce1a5bdd7160 Mon Sep 17 00:00:00 2001 From: Simon Byrne Date: Sat, 17 Dec 2022 20:19:02 -0800 Subject: [PATCH 01/11] add exception types --- base/boot.jl | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/base/boot.jl b/base/boot.jl index 33b2cd07688ad..d3ed3ea8028e9 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -344,6 +344,15 @@ struct AssertionError <: Exception end AssertionError() = AssertionError("") +abstract type FloatingPointException <: Exception end + +struct DivideByZeroFloatingPointException <: FloatingPointException end +struct OverflowFloatingPointException <: FloatingPointException end +struct UnderflowFloatingPointException <: FloatingPointException end +struct InexactFloatingPointException <: FloatingPointException end +struct InvalidFloatingPointException <: FloatingPointException end + + abstract type WrappedException <: Exception end struct LoadError <: WrappedException From 1e66ad48e67c77360dd7feec35a690692c1cd050 Mon Sep 17 00:00:00 2001 From: Simon Byrne Date: Sun, 18 Dec 2022 14:10:33 -0800 Subject: [PATCH 02/11] add to C --- src/init.c | 5 +++++ src/jl_exported_data.inc | 5 +++++ src/julia.h | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/src/init.c b/src/init.c index 19e4dafef44d3..308aa813acc86 100644 --- a/src/init.c +++ b/src/init.c @@ -887,6 +887,11 @@ static void post_boot_hooks(void) jl_errorexception_type = (jl_datatype_t*)core("ErrorException"); jl_stackovf_exception = jl_new_struct_uninit((jl_datatype_t*)core("StackOverflowError")); jl_diverror_exception = jl_new_struct_uninit((jl_datatype_t*)core("DivideError")); + jl_divbyzero_fp_exception = jl_new_struct_uninit((jl_datatype_t*)core("DivideByZeroFloatingPointException")); + jl_overflow_fp_exception = jl_new_struct_uninit((jl_datatype_t*)core("OverflowFloatingPointException")); + jl_underflow_fp_exception = jl_new_struct_uninit((jl_datatype_t*)core("UnderflowFloatingPointException")); + jl_inexact_fp_exception = jl_new_struct_uninit((jl_datatype_t*)core("InexactFloatingPointException")); + jl_invalid_fp_exception = jl_new_struct_uninit((jl_datatype_t*)core("InvalidFloatingPointException")); jl_undefref_exception = jl_new_struct_uninit((jl_datatype_t*)core("UndefRefError")); jl_undefvarerror_type = (jl_datatype_t*)core("UndefVarError"); jl_atomicerror_type = (jl_datatype_t*)core("ConcurrencyViolationError"); diff --git a/src/jl_exported_data.inc b/src/jl_exported_data.inc index a254fba5e2b28..baf5b3bc4df8a 100644 --- a/src/jl_exported_data.inc +++ b/src/jl_exported_data.inc @@ -33,6 +33,11 @@ XX(jl_datatype_type) \ XX(jl_densearray_type) \ XX(jl_diverror_exception) \ + XX(jl_divbyzero_fp_exception) \ + XX(jl_overflow_fp_exception) \ + XX(jl_underflow_fp_exception) \ + XX(jl_inexact_fp_exception) \ + XX(jl_invalid_fp_exception) \ XX(jl_emptysvec) \ XX(jl_emptytuple) \ XX(jl_emptytuple_type) \ diff --git a/src/julia.h b/src/julia.h index a27b66241793a..3a37718496062 100644 --- a/src/julia.h +++ b/src/julia.h @@ -737,6 +737,11 @@ extern JL_DLLIMPORT jl_value_t *jl_stackovf_exception JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_value_t *jl_memory_exception JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_value_t *jl_readonlymemory_exception JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_value_t *jl_diverror_exception JL_GLOBALLY_ROOTED; +extern JL_DLLIMPORT jl_value_t * jl_divbyzero_fp_exception JL_GLOBALLY_ROOTED; +extern JL_DLLIMPORT jl_value_t * jl_overflow_fp_exception JL_GLOBALLY_ROOTED; +extern JL_DLLIMPORT jl_value_t * jl_underflow_fp_exception JL_GLOBALLY_ROOTED; +extern JL_DLLIMPORT jl_value_t * jl_inexact_fp_exception JL_GLOBALLY_ROOTED; +extern JL_DLLIMPORT jl_value_t * jl_invalid_fp_exception JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_value_t *jl_undefref_exception JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_value_t *jl_interrupt_exception JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_datatype_t *jl_boundserror_type JL_GLOBALLY_ROOTED; From 29f880b3142c8a44504110623e2fd51411d6e052 Mon Sep 17 00:00:00 2001 From: Simon Byrne Date: Mon, 19 Dec 2022 10:41:35 -0800 Subject: [PATCH 03/11] add signal hooks --- base/boot.jl | 5 +++++ src/codegen.cpp | 5 +++++ src/signals-mach.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++ src/signals-unix.c | 13 +++++++++++-- src/signals-win.c | 15 ++++++++++++--- src/staticdata.c | 7 ++++++- 6 files changed, 87 insertions(+), 6 deletions(-) diff --git a/base/boot.jl b/base/boot.jl index d3ed3ea8028e9..72ee75437d629 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -186,6 +186,11 @@ export ErrorException, BoundsError, DivideError, DomainError, Exception, InterruptException, InexactError, OutOfMemoryError, ReadOnlyMemoryError, OverflowError, StackOverflowError, SegmentationFault, UndefRefError, UndefVarError, + DivideByZeroFloatingPointException, + OverflowFloatingPointException, + UnderflowFloatingPointException, + InexactFloatingPointException, + InvalidFloatingPointException, TypeError, ArgumentError, MethodError, AssertionError, LoadError, InitError, UndefKeywordError, ConcurrencyViolationError, # AST representation diff --git a/src/codegen.cpp b/src/codegen.cpp index b37364ef80a27..4509b698f113c 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -8402,6 +8402,11 @@ static void init_jit_functions(void) global_jlvalue_to_llvm(new JuliaVariable{"jl_emptysvec", true, get_pjlvalue}, (jl_value_t**)&jl_emptysvec); global_jlvalue_to_llvm(new JuliaVariable{"jl_emptytuple", true, get_pjlvalue}, &jl_emptytuple); global_jlvalue_to_llvm(new JuliaVariable{"jl_diverror_exception", true, get_pjlvalue}, &jl_diverror_exception); + global_jlvalue_to_llvm(new JuliaVariable{"jl_divbyzero_fp_exception", true, get_pjlvalue}, &jl_divbyzero_fp_exception); + global_jlvalue_to_llvm(new JuliaVariable{"jl_overflow_fp_exception", true, get_pjlvalue}, &jl_overflow_fp_exception); + global_jlvalue_to_llvm(new JuliaVariable{"jl_underflow_fp_exception", true, get_pjlvalue}, &jl_underflow_fp_exception); + global_jlvalue_to_llvm(new JuliaVariable{"jl_inexact_fp_exception", true, get_pjlvalue}, &jl_inexact_fp_exception); + global_jlvalue_to_llvm(new JuliaVariable{"jl_invalid_fp_exception", true, get_pjlvalue}, &jl_invalid_fp_exception); global_jlvalue_to_llvm(new JuliaVariable{"jl_undefref_exception", true, get_pjlvalue}, &jl_undefref_exception); add_named_global(jlgetworld_global, &jl_world_counter); add_named_global("__stack_chk_fail", &__stack_chk_fail); diff --git a/src/signals-mach.c b/src/signals-mach.c index 2b1da43b71f63..5ea8433e22826 100644 --- a/src/signals-mach.c +++ b/src/signals-mach.c @@ -297,7 +297,55 @@ kern_return_t catch_mach_exception_raise( if (ptls2->gc_state == JL_GC_STATE_WAITING) return KERN_FAILURE; if (exception == EXC_ARITHMETIC) { + // https://github.com/apple/darwin-xnu/blob/a1babec6b135d1f35b2590a1990af3c5c5393479/osfmk/kern/ux_handler.c#L149 + // uu_code = code[0]; uu_subcode = code[1] + +#ifdef _CPU_X86_64_ + // https://github.com/apple/darwin-xnu/blob/8f02f2a044b9bb1ad951987ef5bab20ec9486310/bsd/dev/i386/unix_signal.c#L535-L551 + if (code[0] == EXC_I386_DIV) { jl_throw_in_thread(ptls2, thread, jl_diverror_exception); + } else if (code[0] == EXC_I386_SSEEXTERR) { + // code[1] contains the contents of the MXCSR register + unsigned int flags = ~(code[1] >> 7) & code[1]; // extract unmasked flags + + if (flags & (1 << 0)) // IE = invalid + jl_throw_in_thread(ptls2, thread, jl_invalid_fp_exception); + else if (flags & (1 << 1)) // DE = denormal + jl_throw_in_thread(ptls2, thread, jl_underflow_fp_exception); + else if (flags & (1 << 2)) // ZE = zero divide + jl_throw_in_thread(ptls2, thread, jl_divbyzero_fp_exception); + else if (flags & (1 << 3)) // OE = overflow + jl_throw_in_thread(ptls2, thread, jl_overflow_fp_exception); + else if (flags & (1 << 4)) // UE = underflow + jl_throw_in_thread(ptls2, thread, jl_underflow_fp_exception); + else if (flags & (1 << 5)) // PE = precision (inexact) + jl_throw_in_thread(ptls2, thread, jl_inexact_fp_exception); + } +#elif defined(_CPU_AARCH64_) + // https://github.com/apple/darwin-xnu/blob/8f02f2a044b9bb1ad951987ef5bab20ec9486310/bsd/dev/i386/unix_signal.c#L535-L551 + switch (code[0]) { + case EXC_ARM_FP_UF: + jl_throw_in_thread(ptls2, thread, jl_underflow_fp_exception); + break; + case EXC_ARM_FP_OF: + jl_throw_in_thread(ptls2, thread, jl_overflow_fp_exception); + break; + case EXC_ARM_FP_IO: + jl_throw_in_thread(ptls2, thread, jl_invalid_fp_exception); + break; + case EXC_ARM_FP_DZ: + jl_throw_in_thread(ptls2, thread, jl_divbyzero_fp_exception); + break; + case EXC_ARM_FP_ID: // denormal + jl_throw_in_thread(ptls2, thread, jl_underflow_fp_exception); + break; + case EXC_ARM_FP_IX: + jl_throw_in_thread(ptls2, thread, jl_inexact_fp_exception); + break; + default: + break; + } +#endif return KERN_SUCCESS; } assert(exception == EXC_BAD_ACCESS); // SIGSEGV or SIGBUS diff --git a/src/signals-unix.c b/src/signals-unix.c index 6ed664199fd2b..1a2eae9c047e6 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -1016,7 +1016,6 @@ void restore_signals(void) static void fpe_handler(int sig, siginfo_t *info, void *context) { - (void)info; if (jl_get_safe_restore()) { // restarting jl_ or profile jl_call_in_ctx(NULL, &jl_sig_throw, sig, context); return; @@ -1024,8 +1023,18 @@ static void fpe_handler(int sig, siginfo_t *info, void *context) jl_task_t *ct = jl_get_current_task(); if (ct == NULL || ct->eh == NULL) // exception on foreign thread is fatal sigdie_handler(sig, info, context); - else + else if (info->si_code == FPE_INTDIV) jl_throw_in_ctx(ct, jl_diverror_exception, sig, context); + else if (info->si_code == FPE_FLTINV) + jl_throw_in_ctx(ct, jl_invalid_fp_exception, sig, context); + else if (info->si_code == FPE_FLTDIV) + jl_throw_in_ctx(ct, jl_divbyzero_fp_exception, sig, context); + else if (info->si_code == FPE_FLTOVF) + jl_throw_in_ctx(ct, jl_overflow_fp_exception, sig, context); + else if (info->si_code == FPE_FLTUND) + jl_throw_in_ctx(ct, jl_underflow_fp_exception, sig, context); + else if (info->si_code == FPE_FLTRES) + jl_throw_in_ctx(ct, jl_inexact_fp_exception, sig, context); } static void sigint_handler(int sig) diff --git a/src/signals-win.c b/src/signals-win.c index f20a4d5287669..34641931381de 100644 --- a/src/signals-win.c +++ b/src/signals-win.c @@ -66,14 +66,23 @@ void __cdecl crt_sig_handler(int sig, int num) fpreset(); signal(SIGFPE, (void (__cdecl *)(int))crt_sig_handler); switch(num) { + default: + jl_errorf("Unexpected FPE Error 0x%X", num); + break; case _FPE_INVALID: + jl_throw(jl_invalid_fp_exception); + break; case _FPE_OVERFLOW: + jl_throw(jl_overflow_fp_exception); + break; case _FPE_UNDERFLOW: - default: - jl_errorf("Unexpected FPE Error 0x%X", num); + jl_throw(jl_underflow_fp_exception); break; case _FPE_ZERODIVIDE: - jl_throw(jl_diverror_exception); + jl_throw(jl_divbyzero_fp_exception); + break; + case _FPE_INEXACT: + jl_throw(jl_inexact_fp_exception); break; } break; diff --git a/src/staticdata.c b/src/staticdata.c index 786d0c966693b..26deed7c14ada 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -98,7 +98,7 @@ extern "C" { // TODO: put WeakRefs on the weak_refs list during deserialization // TODO: handle finalizers -#define NUM_TAGS 158 +#define NUM_TAGS 163 // An array of references that need to be restored from the sysimg // This is a manually constructed dual of the gvars array, which would be produced by codegen for Julia code, for C. @@ -219,6 +219,11 @@ jl_value_t **const*const get_tags(void) { INSERT_TAG(jl_undefvarerror_type); INSERT_TAG(jl_stackovf_exception); INSERT_TAG(jl_diverror_exception); + INSERT_TAG(jl_divbyzero_fp_exception); + INSERT_TAG(jl_overflow_fp_exception); + INSERT_TAG(jl_underflow_fp_exception); + INSERT_TAG(jl_inexact_fp_exception); + INSERT_TAG(jl_invalid_fp_exception); INSERT_TAG(jl_interrupt_exception); INSERT_TAG(jl_boundserror_type); INSERT_TAG(jl_memory_exception); From ce3b2c1515de7abfb48983a10195e11daaaefb4b Mon Sep 17 00:00:00 2001 From: Simon Byrne Date: Mon, 19 Dec 2022 12:11:47 -0800 Subject: [PATCH 04/11] add Julia interface --- base/rounding.jl | 18 ++++++++++++++++++ src/jlapi.c | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/base/rounding.jl b/base/rounding.jl index 25cfe2dc09829..10ba8d71f99ab 100644 --- a/base/rounding.jl +++ b/base/rounding.jl @@ -253,4 +253,22 @@ for IEEE arithmetic, and `true` if they might be converted to zeros. """ get_zero_subnormals() = ccall(:jl_get_zero_subnormals,Int32,())!=0 + +function set_exceptions(; + invalid=false, + inexact=false, + overflow=false, + underflow=false, + dividebyzero=false + ) + + excepts = (invalid * JL_FE_INVALID) | + (inexact * JL_FE_INEXACT) | + (underflow * JL_FE_UNDERFLOW) | + (overflow * JL_FE_OVERFLOW) | + (dividebyzero * JL_FE_DIVBYZERO) + + ccall(:jl_set_fenv_except,Cint,(Cint,), excepts) == 0 +end + end #module diff --git a/src/jlapi.c b/src/jlapi.c index 5c6f01ab86a88..0e96ca0dc6fd1 100644 --- a/src/jlapi.c +++ b/src/jlapi.c @@ -526,6 +526,44 @@ JL_DLLEXPORT int jl_set_fenv_rounding(int i) return fesetround(i); } +JL_DLLEXPORT int jl_set_fenv_except(int excepts) +{ +#ifdef _OS_DARWIN_ + // https://stackoverflow.com/questions/71821666/trapping-floating-point-exceptions-and-signal-handling-on-apple-silicon + fenv_t env; + fegetenv(&env); +#if defined(_CPU_AARCH64_) + env.__fpcr = env.__fpcr | (excepts << FE_EXCEPT_SHIFT); +#elif defined(_CPU_X86_64_) + env.__control = env.__control & ~excepts; + env.__mxcsr = env.__mxcsr & ~(excepts << 7); +#else + return 1; +#endif + fesetenv(&env); + return 0; +#elif defined(_OS_WINDOWS_) + unsigned int flags = 0; + if (excepts & FE_INEXACT) + flags |= _EM_INEXACT; + if (excepts & FE_UNDERFLOW) + flags |= _EM_UNDERFLOW; + if (excepts & FE_OVERFLOW) + flags |= _EM_OVERFLOW; + if (excepts & FE_DIVBYZERO) + flags |= _EM_ZERODIVIDE; + if (excepts & FE_INVALID) + flags |= _EM_INVALID; + _controlfp(flags, _MCW_EM); + return 0; +#else + int prev_excepts = feenableexcept(excepts); + return prev_excepts >= 0; +#endif +} + + + static int exec_program(char *program) { JL_TRY { From 2631abc0ac3b72f56ae0f3757d56685812beb971 Mon Sep 17 00:00:00 2001 From: Simon Byrne Date: Mon, 19 Dec 2022 12:24:20 -0800 Subject: [PATCH 05/11] fix build errors --- src/jlapi.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/jlapi.c b/src/jlapi.c index 0e96ca0dc6fd1..c6d0764d2923c 100644 --- a/src/jlapi.c +++ b/src/jlapi.c @@ -15,6 +15,10 @@ #include "julia_assert.h" #include "julia_internal.h" +#ifdef _OS_WINDOWS_ +#include +#endif + #ifdef __cplusplus #include extern "C" { @@ -533,7 +537,7 @@ JL_DLLEXPORT int jl_set_fenv_except(int excepts) fenv_t env; fegetenv(&env); #if defined(_CPU_AARCH64_) - env.__fpcr = env.__fpcr | (excepts << FE_EXCEPT_SHIFT); + env.__fpcr = env.__fpcr | (excepts << 8); #elif defined(_CPU_X86_64_) env.__control = env.__control & ~excepts; env.__mxcsr = env.__mxcsr & ~(excepts << 7); From b1ba28287fbdb67c6df9f380a71b2116cd661ba2 Mon Sep 17 00:00:00 2001 From: Simon Byrne Date: Mon, 19 Dec 2022 12:35:19 -0800 Subject: [PATCH 06/11] fix darwin --- src/jlapi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/jlapi.c b/src/jlapi.c index c6d0764d2923c..a56f4b85124e3 100644 --- a/src/jlapi.c +++ b/src/jlapi.c @@ -537,10 +537,10 @@ JL_DLLEXPORT int jl_set_fenv_except(int excepts) fenv_t env; fegetenv(&env); #if defined(_CPU_AARCH64_) - env.__fpcr = env.__fpcr | (excepts << 8); + env.__fpcr = (env.__fpcr & ~(FE_ALL_EXCEPT << 8)) | (excepts << 8); #elif defined(_CPU_X86_64_) - env.__control = env.__control & ~excepts; - env.__mxcsr = env.__mxcsr & ~(excepts << 7); + env.__control = (env.__control | FE_ALL_EXCEPT) & ~excepts; + env.__mxcsr = (env.__mxcsr | FE_ALL_EXCEPT << 7) & ~(excepts << 7); #else return 1; #endif From beedde6c4d690d52eebecd71c33778113263bd1d Mon Sep 17 00:00:00 2001 From: Simon Byrne Date: Mon, 19 Dec 2022 13:06:02 -0800 Subject: [PATCH 07/11] only use feenableexcept with glibc --- src/jlapi.c | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/jlapi.c b/src/jlapi.c index a56f4b85124e3..f52b3ae45d883 100644 --- a/src/jlapi.c +++ b/src/jlapi.c @@ -532,20 +532,9 @@ JL_DLLEXPORT int jl_set_fenv_rounding(int i) JL_DLLEXPORT int jl_set_fenv_except(int excepts) { -#ifdef _OS_DARWIN_ - // https://stackoverflow.com/questions/71821666/trapping-floating-point-exceptions-and-signal-handling-on-apple-silicon - fenv_t env; - fegetenv(&env); -#if defined(_CPU_AARCH64_) - env.__fpcr = (env.__fpcr & ~(FE_ALL_EXCEPT << 8)) | (excepts << 8); -#elif defined(_CPU_X86_64_) - env.__control = (env.__control | FE_ALL_EXCEPT) & ~excepts; - env.__mxcsr = (env.__mxcsr | FE_ALL_EXCEPT << 7) & ~(excepts << 7); -#else - return 1; -#endif - fesetenv(&env); - return 0; +#if defined(__GLIBC__) + int prev_excepts = feenableexcept(excepts); + return prev_excepts >= 0; #elif defined(_OS_WINDOWS_) unsigned int flags = 0; if (excepts & FE_INEXACT) @@ -560,9 +549,22 @@ JL_DLLEXPORT int jl_set_fenv_except(int excepts) flags |= _EM_INVALID; _controlfp(flags, _MCW_EM); return 0; +#elif defined(_OS_DARWIN_) + // https://stackoverflow.com/questions/71821666/trapping-floating-point-exceptions-and-signal-handling-on-apple-silicon + fenv_t env; + fegetenv(&env); +#if defined(_CPU_AARCH64_) + env.__fpcr = (env.__fpcr & ~(FE_ALL_EXCEPT << 8)) | (excepts << 8); +#elif defined(_CPU_X86_64_) + env.__control = (env.__control | FE_ALL_EXCEPT) & ~excepts; + env.__mxcsr = (env.__mxcsr | FE_ALL_EXCEPT << 7) & ~(excepts << 7); #else - int prev_excepts = feenableexcept(excepts); - return prev_excepts >= 0; + return 1; +#endif + fesetenv(&env); + return 0; +#else + return 1; #endif } From e024f5df5315b2f09a9cc5f99c4e2a3a69a051c4 Mon Sep 17 00:00:00 2001 From: Simon Byrne Date: Mon, 19 Dec 2022 15:03:54 -0800 Subject: [PATCH 08/11] add getter --- base/rounding.jl | 75 +++++++++++++++++++++++++++++++++++++++--------- src/jlapi.c | 39 +++++++++++++++++++++++-- 2 files changed, 98 insertions(+), 16 deletions(-) diff --git a/base/rounding.jl b/base/rounding.jl index 10ba8d71f99ab..f46a4bbf80caa 100644 --- a/base/rounding.jl +++ b/base/rounding.jl @@ -254,21 +254,70 @@ for IEEE arithmetic, and `true` if they might be converted to zeros. get_zero_subnormals() = ccall(:jl_get_zero_subnormals,Int32,())!=0 +function _get_exceptions() + excepts = ccall(:jl_set_fenv_except,Cint,()) + if excepts < 0 + error("Floating point exceptions not supported on this platform") + end + return excepts +end +function get_exceptions() + excepts = _get_exceptions() + return (; + invalid = excepts & JL_FE_INVALID != 0, + inexact = excepts & JL_FE_INEXACT != 0, + underflow = excepts & JL_FE_UNDERFLOW != 0, + overflow = excepts & JL_FE_OVERFLOW != 0, + dividebyzero = excepts & JL_FE_DIVBYZERO != 0, + ) +end + function set_exceptions(; - invalid=false, - inexact=false, - overflow=false, - underflow=false, - dividebyzero=false + invalid=nothing, + inexact=nothing, + overflow=nothing, + underflow=nothing, + dividebyzero=nothing ) - - excepts = (invalid * JL_FE_INVALID) | - (inexact * JL_FE_INEXACT) | - (underflow * JL_FE_UNDERFLOW) | - (overflow * JL_FE_OVERFLOW) | - (dividebyzero * JL_FE_DIVBYZERO) - - ccall(:jl_set_fenv_except,Cint,(Cint,), excepts) == 0 + excepts = _get_exceptions() + if !isnothing(invalid) + if invalid + excepts |= JL_FE_INVALID + else + excepts &= ~JL_FE_INVALID + end + end + if !isnothing(inexact) + if inexact + excepts |= JL_FE_INEXACT + else + excepts &= ~JL_FE_INEXACT + end + end + if !isnothing(underflow) + if underflow + excepts |= JL_FE_UNDERFLOW + else + excepts &= ~JL_FE_UNDERFLOW + end + end + if !isnothing(overflow) + if overflow + excepts |= JL_FE_OVERFLOW + else + excepts &= ~JL_FE_OVERFLOW + end + end + if !isnothing(dividebyzero) + if dividebyzero + excepts |= JL_FE_DIVBYZERO + else + excepts &= ~JL_FE_DIVBYZERO + end + end + if ccall(:jl_set_fenv_except,Cint,(Cint,), excepts) < 0 + error("Floating point exceptions not supported on this platform") + end end end #module diff --git a/src/jlapi.c b/src/jlapi.c index f52b3ae45d883..bd6631a43ed62 100644 --- a/src/jlapi.c +++ b/src/jlapi.c @@ -530,6 +530,40 @@ JL_DLLEXPORT int jl_set_fenv_rounding(int i) return fesetround(i); } + +JL_DLLEXPORT int jl_get_fenv_except(void) +{ +#if defined(__GLIBC__) + return fegetexcept(); +#elif defined(_OS_WINDOWS_) + unsigned int flags = _controlfp(0, 0); + int excepts = 0; + if (flags & _EM_INEXACT) + excepts |= FE_INEXACT; + if (flags & _EM_UNDERFLOW) + excepts |= FE_UNDERFLOW; + if (flags & _EM_OVERFLOW) + excepts |= FE_OVERFLOW; + if (flags & _EM_ZERODIVIDE) + excepts |= FE_DIVBYZERO; + if (flags & _EM_INVALID) + excepts |= FE_INVALID; + return excepts; +#elif defined(_OS_DARWIN_) + fenv_t env; + fegetenv(&env); +#if defined(_CPU_AARCH64_) + return (env.__fpcr & FE_ALL_EXCEPT << 8); +#elif defined(_CPU_X86_64_) + return (~env.__mxcsr & FE_ALL_EXCEPT << 7); +#else + return -1; +#endif +#else + return -1; +#endif +} + JL_DLLEXPORT int jl_set_fenv_except(int excepts) { #if defined(__GLIBC__) @@ -559,17 +593,16 @@ JL_DLLEXPORT int jl_set_fenv_except(int excepts) env.__control = (env.__control | FE_ALL_EXCEPT) & ~excepts; env.__mxcsr = (env.__mxcsr | FE_ALL_EXCEPT << 7) & ~(excepts << 7); #else - return 1; + return -1; #endif fesetenv(&env); return 0; #else - return 1; + return -1; #endif } - static int exec_program(char *program) { JL_TRY { From 7436f340c8d84cd334baaff162e7b88590ab0335 Mon Sep 17 00:00:00 2001 From: Simon Byrne Date: Mon, 19 Dec 2022 17:26:49 -0800 Subject: [PATCH 09/11] add some tests --- src/jlapi.c | 4 ++-- test/rounding.jl | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/jlapi.c b/src/jlapi.c index bd6631a43ed62..4ec0e08a45a4c 100644 --- a/src/jlapi.c +++ b/src/jlapi.c @@ -553,9 +553,9 @@ JL_DLLEXPORT int jl_get_fenv_except(void) fenv_t env; fegetenv(&env); #if defined(_CPU_AARCH64_) - return (env.__fpcr & FE_ALL_EXCEPT << 8); + return (env.__fpcr >> 8 & FE_ALL_EXCEPT); #elif defined(_CPU_X86_64_) - return (~env.__mxcsr & FE_ALL_EXCEPT << 7); + return (~env.__mxcsr >> 7 & FE_ALL_EXCEPT); #else return -1; #endif diff --git a/test/rounding.jl b/test/rounding.jl index 508a68032e083..b6131cc29d790 100644 --- a/test/rounding.jl +++ b/test/rounding.jl @@ -5,6 +5,7 @@ using Base.MathConstants using Test + @testset "Float64 checks" begin # a + b returns a number exactly between prevfloat(1.) and 1., so its # final result depends strongly on the utilized rounding direction. @@ -351,3 +352,41 @@ end Base.Rounding.setrounding_raw(T, Base.Rounding.to_fenv(old)) end end + + +try + Base.Rounding.get_exceptions() + fp_exceptions = true +catch e + fp_exceptions = false +end + +if fp_exceptions + + @testset "floating point exceptions" begin + @test Base.Rounding.get_exceptions() == (invalid = false, inexact = false, underflow = false, overflow = false, dividebyzero = false) + + @test isnan(0/0) + @test 1/0 == Inf + + Base.Rounding.set_exceptions(invalid=true) + @test Base.Rounding.get_exceptions() == (invalid = true, inexact = false, underflow = false, overflow = false, dividebyzero = false) + + @test_throws InvalidFloatingPointException 0/0 + @test 1/0 == Inf + + Base.Rounding.set_exceptions(dividebyzero=true) + @test Base.Rounding.get_exceptions() == (invalid = true, inexact = false, underflow = false, overflow = false, dividebyzero = true) + + @test_throws InvalidFloatingPointException 0/0 + @test_throws DivideByZeroFloatingPointException 1/0 + + Base.Rounding.set_exceptions(invalid=false, dividebyzero=false) + + @test Base.Rounding.get_exceptions() == (invalid = false, inexact = false, underflow = false, overflow = false, dividebyzero = false) + + @test isnan(0/0) + @test 1/0 == Inf + + end +end From 2a810dcf667f2b9d07700b002193e51bbdab7ed1 Mon Sep 17 00:00:00 2001 From: Simon Byrne Date: Mon, 19 Dec 2022 17:38:47 -0800 Subject: [PATCH 10/11] typo --- base/rounding.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/rounding.jl b/base/rounding.jl index f46a4bbf80caa..efd9fd9f6cb07 100644 --- a/base/rounding.jl +++ b/base/rounding.jl @@ -255,7 +255,7 @@ get_zero_subnormals() = ccall(:jl_get_zero_subnormals,Int32,())!=0 function _get_exceptions() - excepts = ccall(:jl_set_fenv_except,Cint,()) + excepts = ccall(:jl_get_fenv_except,Cint,()) if excepts < 0 error("Floating point exceptions not supported on this platform") end From 9b8e5755d39111293d8613be5355668efe19dfe7 Mon Sep 17 00:00:00 2001 From: Simon Byrne Date: Sat, 2 Dec 2023 12:33:33 -0800 Subject: [PATCH 11/11] fall through --- src/signals-mach.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/signals-mach.c b/src/signals-mach.c index 4c1d4b90fcb88..675b9ee9cd353 100644 --- a/src/signals-mach.c +++ b/src/signals-mach.c @@ -364,7 +364,7 @@ kern_return_t catch_mach_exception_raise( jl_throw_in_thread(ptls2, thread, jl_inexact_fp_exception); } #elif defined(_CPU_AARCH64_) - // https://github.com/apple/darwin-xnu/blob/8f02f2a044b9bb1ad951987ef5bab20ec9486310/bsd/dev/i386/unix_signal.c#L535-L551 + // https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/bsd/dev/arm/unix_signal.c#L436-L459 switch (code[0]) { case EXC_ARM_FP_UF: jl_throw_in_thread(ptls2, thread, jl_underflow_fp_exception); @@ -387,6 +387,8 @@ kern_return_t catch_mach_exception_raise( default: break; } +#else + jl_throw_in_thread(ptls2, thread, jl_diverror_exception); #endif return KERN_SUCCESS; }