xref: /petsc/src/sys/error/fp.c (revision 6524c165f7ddaf30fd7457737f668f984c8ababf)
1 
2 /*
3    IEEE error handler for all machines. Since each OS has
4    enough slight differences we have completely separate codes for each one.
5 */
6 
7 /*
8   This feature test macro provides FE_NOMASK_ENV on GNU.  It must be defined
9   at the top of the file because other headers may pull in fenv.h even when
10   not strictly necessary.  Strictly speaking, we could include ONLY petscconf.h,
11   check PETSC_HAVE_FENV_H, and only define _GNU_SOURCE in that case, but such
12   shenanigans ought to be unnecessary.
13 */
14 #if !defined(_GNU_SOURCE)
15 #define _GNU_SOURCE
16 #endif
17 
18 #include <petsc/private/petscimpl.h> /*I  "petscsys.h"  I*/
19 #include <signal.h>
20 
21 struct PetscFPTrapLink {
22   PetscFPTrap             trapmode;
23   struct PetscFPTrapLink *next;
24 };
25 static PetscFPTrap             _trapmode = PETSC_FP_TRAP_OFF; /* Current trapping mode; see PetscDetermineInitialFPTrap() */
26 static struct PetscFPTrapLink *_trapstack;                    /* Any pushed states of _trapmode */
27 
28 /*@
29    PetscFPTrapPush - push a floating point trapping mode, restored using `PetscFPTrapPop()`
30 
31    Not Collective
32 
33    Input Parameter:
34 .    trap - `PETSC_FP_TRAP_ON` or `PETSC_FP_TRAP_OFF` or any of the values passable to `PetscSetFPTrap()`
35 
36    Level: advanced
37 
38    Notes:
39      This only changes the trapping if the new mode is different than the current mode.
40 
41      This routine is called to turn off trapping for certain LAPACK routines that assume that dividing
42      by zero is acceptable. In particular the routine ieeeck().
43 
44      Most systems by default have all trapping turned off, but certain Fortran compilers have
45      link flags that turn on trapping before the program begins.
46 $       gfortran -ffpe-trap=invalid,zero,overflow,underflow,denormal
47 $       ifort -fpe0
48 
49 .seealso: `PetscFPTrapPop()`, `PetscSetFPTrap()`, `PetscDetermineInitialFPTrap()`
50 @*/
51 PetscErrorCode PetscFPTrapPush(PetscFPTrap trap) {
52   struct PetscFPTrapLink *link;
53 
54   PetscFunctionBegin;
55   PetscCall(PetscNew(&link));
56 #if defined(PETSC_HAVE_THREADSAFETY) && defined(PETSC_HAVE_OPENMP)
57 #pragma omp critical
58 #endif
59   {
60     link->trapmode = _trapmode;
61     link->next     = _trapstack;
62     _trapstack     = link;
63   }
64   if (trap != _trapmode) PetscCall(PetscSetFPTrap(trap));
65   PetscFunctionReturn(0);
66 }
67 
68 /*@
69    PetscFPTrapPop - push a floating point trapping mode, to be restored using `PetscFPTrapPop()`
70 
71    Not Collective
72 
73    Level: advanced
74 
75 .seealso: `PetscFPTrapPush()`, `PetscSetFPTrap()`, `PetscDetermineInitialFPTrap()`
76 @*/
77 PetscErrorCode PetscFPTrapPop(void) {
78   struct PetscFPTrapLink *link;
79 
80   PetscFunctionBegin;
81   if (_trapstack->trapmode != _trapmode) PetscCall(PetscSetFPTrap(_trapstack->trapmode));
82 #if defined(PETSC_HAVE_THREADSAFETY) && defined(PETSC_HAVE_OPENMP)
83 #pragma omp critical
84 #endif
85   {
86     link       = _trapstack;
87     _trapstack = _trapstack->next;
88   }
89   PetscCall(PetscFree(link));
90   PetscFunctionReturn(0);
91 }
92 
93 /*--------------------------------------- ---------------------------------------------------*/
94 #if defined(PETSC_HAVE_SUN4_STYLE_FPTRAP)
95 #include <floatingpoint.h>
96 
97 PETSC_EXTERN PetscErrorCode ieee_flags(char *, char *, char *, char **);
98 PETSC_EXTERN PetscErrorCode ieee_handler(char *, char *, sigfpe_handler_type(int, int, struct sigcontext *, char *));
99 
100 static struct {
101   int   code_no;
102   char *name;
103 } error_codes[] = {
104   {FPE_INTDIV_TRAP,   "integer divide"               },
105   {FPE_FLTOPERR_TRAP, "IEEE operand error"           },
106   {FPE_FLTOVF_TRAP,   "floating point overflow"      },
107   {FPE_FLTUND_TRAP,   "floating point underflow"     },
108   {FPE_FLTDIV_TRAP,   "floating pointing divide"     },
109   {FPE_FLTINEX_TRAP,  "inexact floating point result"},
110   {0,                 "unknown error"                }
111 };
112 #define SIGPC(scp) (scp->sc_pc)
113 
114 /* this function gets called if a trap has occurred and been caught */
115 sigfpe_handler_type PetscDefaultFPTrap(int sig, int code, struct sigcontext *scp, char *addr) {
116   int err_ind = -1;
117 
118   PetscFunctionBegin;
119   for (int j = 0; error_codes[j].code_no; j++) {
120     if (error_codes[j].code_no == code) err_ind = j;
121   }
122 
123   if (err_ind >= 0) (*PetscErrorPrintf)("*** %s occurred at pc=%X ***\n", error_codes[err_ind].name, SIGPC(scp));
124   else (*PetscErrorPrintf)("*** floating point error 0x%x occurred at pc=%X ***\n", code, SIGPC(scp));
125 
126   (void)PetscError(PETSC_COMM_SELF, PETSC_ERR_FP, NULL, NULL, PETSC_ERR_FP, PETSC_ERROR_REPEAT, "floating point error");
127   PETSCABORT(MPI_COMM_WORLD, PETSC_ERR_FP);
128   PetscFunctionReturn(0);
129 }
130 
131 /*@
132    PetscSetFPTrap - Enables traps/exceptions on common floating point errors. This option may not work on certain systems or only a
133    subset of exceptions may be trapable.
134 
135    Not Collective
136 
137    Input Parameters:
138 .  flag - values are
139 .vb
140     PETSC_FP_TRAP_OFF   - do not trap any exceptions
141     PETSC_FP_TRAP_ON - all exceptions that are possible on the system except underflow
142     PETSC_FP_TRAP_INDIV - integer divide by zero
143     PETSC_FP_TRAP_FLTOPERR - improper argument to function, for example with real numbers, the square root of a negative number
144     PETSC_FP_TRAP_FLTOVF - overflow
145     PETSC_FP_TRAP_FLTUND - underflow - not trapped by default on most systems
146     PETSC_FP_TRAP_FLTDIV - floating point divide by zero
147     PETSC_FP_TRAP_FLTINEX - inexact floating point result
148 .ve
149 
150    Options Database Keys:
151 .  -fp_trap <off,on> - turn on or off trapping of floating point exceptions
152 
153    Level: advanced
154 
155    Notes:
156    Currently only `PETSC_FP_TRAP_OFF` and `PETSC_FP_TRAP_ON` are handled. All others are treated as `PETSC_FP_TRAP_ON`.
157 
158    The values are bit values and may be |ed together in the function call
159 
160    On systems that support it this routine causes floating point
161    overflow, divide-by-zero, and invalid-operand (e.g., a NaN), but not underflow, to
162    cause a message to be printed and the program to exit.
163 
164    On many common systems, the floating
165    point exception state is not preserved from the location where the trap
166    occurred through to the signal handler.  In this case, the signal handler
167    will just say that an unknown floating point exception occurred and which
168    function it occurred in.  If you run with -fp_trap in a debugger, it will
169    break on the line where the error occurred.  On systems that support C99
170    floating point exception handling You can check which
171    exception occurred using fetestexcept(FE_ALL_EXCEPT).  See fenv.h
172    (usually at /usr/include/bits/fenv.h) for the enum values on your system.
173 
174 .seealso: `PetscFPTrapPush()`, `PetscFPTrapPop()`, `PetscDetermineInitialFPTrap()`
175 @*/
176 PetscErrorCode PetscSetFPTrap(PetscFPTrap flag) {
177   char *out;
178 
179   PetscFunctionBegin;
180   /* Clear accumulated exceptions.  Used to suppress meaningless messages from f77 programs */
181   (void)ieee_flags("clear", "exception", "all", &out);
182   if (flag == PETSC_FP_TRAP_ON) {
183     /*
184       To trap more fp exceptions, including underflow, change the line below to
185       if (ieee_handler("set","all",PetscDefaultFPTrap)) {
186     */
187     if (ieee_handler("set", "common", PetscDefaultFPTrap)) (*PetscErrorPrintf)("Can't set floatingpoint handler\n");
188   } else if (ieee_handler("clear", "common", PetscDefaultFPTrap)) (*PetscErrorPrintf)("Can't clear floatingpoint handler\n");
189 
190   _trapmode = flag;
191   PetscCall(PetscInfo(NULL, "Using PETSC_HAVE_SUN4_STYLE_FPTRAP\n"));
192   PetscFunctionReturn(0);
193 }
194 
195 /*@
196    PetscDetermineInitialFPTrap - Attempts to determine the floating point trapping that exists when `PetscInitialize()` is called
197 
198    Not Collective
199 
200    Note:
201       Currently only supported on Linux and MacOS. Checks if divide by zero is enable and if so declares that trapping is on.
202 
203    Level: advanced
204 
205 .seealso: `PetscFPTrapPush()`, `PetscFPTrapPop()`, `PetscDetermineInitialFPTrap()`
206 @*/
207 PetscErrorCode PetscDetermineInitialFPTrap(void) {
208   PetscFunctionBegin;
209   PetscCall(PetscInfo(NULL, "Unable to determine initial floating point trapping. Assuming it is off\n"));
210   PetscFunctionReturn(0);
211 }
212 
213 /* -------------------------------------------------------------------------------------------*/
214 #elif defined(PETSC_HAVE_SOLARIS_STYLE_FPTRAP)
215 #include <sunmath.h>
216 #include <floatingpoint.h>
217 #include <siginfo.h>
218 #include <ucontext.h>
219 
220 static struct {
221   int   code_no;
222   char *name;
223 } error_codes[] = {
224   {FPE_FLTINV, "invalid floating point operand"},
225   {FPE_FLTRES, "inexact floating point result" },
226   {FPE_FLTDIV, "division-by-zero"              },
227   {FPE_FLTUND, "floating point underflow"      },
228   {FPE_FLTOVF, "floating point overflow"       },
229   {0,          "unknown error"                 }
230 };
231 #define SIGPC(scp) (scp->si_addr)
232 
233 void PetscDefaultFPTrap(int sig, siginfo_t *scp, ucontext_t *uap) {
234   int err_ind = -1, code = scp->si_code;
235 
236   PetscFunctionBegin;
237   for (int j = 0; error_codes[j].code_no; j++) {
238     if (error_codes[j].code_no == code) err_ind = j;
239   }
240 
241   if (err_ind >= 0) (*PetscErrorPrintf)("*** %s occurred at pc=%X ***\n", error_codes[err_ind].name, SIGPC(scp));
242   else (*PetscErrorPrintf)("*** floating point error 0x%x occurred at pc=%X ***\n", code, SIGPC(scp));
243 
244   (void)PetscError(PETSC_COMM_SELF, 0, NULL, NULL, PETSC_ERR_FP, PETSC_ERROR_REPEAT, "floating point error");
245   PETSCABORT(MPI_COMM_WORLD, PETSC_ERR_FP);
246 }
247 
248 PetscErrorCode PetscSetFPTrap(PetscFPTrap flag) {
249   char *out;
250 
251   PetscFunctionBegin;
252   /* Clear accumulated exceptions.  Used to suppress meaningless messages from f77 programs */
253   (void)ieee_flags("clear", "exception", "all", &out);
254   if (flag == PETSC_FP_TRAP_ON) {
255     if (ieee_handler("set", "common", (sigfpe_handler_type)PetscDefaultFPTrap)) (*PetscErrorPrintf)("Can't set floating point handler\n");
256   } else {
257     if (ieee_handler("clear", "common", (sigfpe_handler_type)PetscDefaultFPTrap)) (*PetscErrorPrintf)("Can't clear floatingpoint handler\n");
258   }
259   _trapmode = flag;
260   PetscCall(PetscInfo(NULL,"Using PETSC_HAVE_SOLARIS_STYLE_FPTRAP\n");
261   PetscFunctionReturn(0);
262 }
263 
264 PetscErrorCode PetscDetermineInitialFPTrap(void) {
265   PetscFunctionBegin;
266   PetscCall(PetscInfo(NULL, "Unable to determine initial floating point trapping. Assuming it is off\n"));
267   PetscFunctionReturn(0);
268 }
269 
270 /* ------------------------------------------------------------------------------------------*/
271 #elif defined(PETSC_HAVE_IRIX_STYLE_FPTRAP)
272 #include <sigfpe.h>
273 static struct {
274   int   code_no;
275   char *name;
276 } error_codes[] = {
277   {_INVALID, "IEEE operand error"      },
278   {_OVERFL,  "floating point overflow" },
279   {_UNDERFL, "floating point underflow"},
280   {_DIVZERO, "floating point divide"   },
281   {0,        "unknown error"           }
282 };
283 void PetscDefaultFPTrap(unsigned exception[], int val[]) {
284   int err_ind = -1, code = exception[0];
285 
286   PetscFunctionBegin;
287   for (int j = 0; error_codes[j].code_no; j++) {
288     if (error_codes[j].code_no == code) err_ind = j;
289   }
290   if (err_ind >= 0) (*PetscErrorPrintf)("*** %s occurred ***\n", error_codes[err_ind].name);
291   else (*PetscErrorPrintf)("*** floating point error 0x%x occurred ***\n", code);
292 
293   (void)PetscError(PETSC_COMM_SELF, 0, NULL, NULL, PETSC_ERR_FP, PETSC_ERROR_REPEAT, "floating point error");
294   PETSCABORT(MPI_COMM_WORLD, PETSC_ERR_FP);
295 }
296 
297 PetscErrorCode PetscSetFPTrap(PetscFPTrap flag) {
298   PetscFunctionBegin;
299   if (flag == PETSC_FP_TRAP_ON) handle_sigfpes(_ON, , _EN_UNDERFL | _EN_OVERFL | _EN_DIVZERO | _EN_INVALID, PetscDefaultFPTrap, _ABORT_ON_ERROR, 0);
300   else handle_sigfpes(_OFF, _EN_UNDERFL | _EN_OVERFL | _EN_DIVZERO | _EN_INVALID, 0, _ABORT_ON_ERROR, 0);
301   _trapmode = flag;
302   PetscCall(PetscInfo(NULL, "Using PETSC_HAVE_IRIX_STYLE_FPTRAP\n"));
303   PetscFunctionReturn(0);
304 }
305 
306 PetscErrorCode PetscDetermineInitialFPTrap(void) {
307   PetscFunctionBegin;
308   PetscCall(PetscInfo(NULL, "Unable to determine initial floating point trapping. Assuming it is off\n"));
309   PetscFunctionReturn(0);
310 }
311 
312 /*----------------------------------------------- --------------------------------------------*/
313 #elif defined(PETSC_HAVE_RS6000_STYLE_FPTRAP)
314 /* In "fast" mode, floating point traps are imprecise and ignored.
315    This is the reason for the fptrap(FP_TRAP_SYNC) call */
316 struct sigcontext;
317 #include <fpxcp.h>
318 #include <fptrap.h>
319 #define FPE_FLTOPERR_TRAP (fptrap_t)(0x20000000)
320 #define FPE_FLTOVF_TRAP   (fptrap_t)(0x10000000)
321 #define FPE_FLTUND_TRAP   (fptrap_t)(0x08000000)
322 #define FPE_FLTDIV_TRAP   (fptrap_t)(0x04000000)
323 #define FPE_FLTINEX_TRAP  (fptrap_t)(0x02000000)
324 
325 static struct {
326   int   code_no;
327   char *name;
328 } error_codes[] = {
329   {FPE_FLTOPERR_TRAP, "IEEE operand error"           },
330   {FPE_FLTOVF_TRAP,   "floating point overflow"      },
331   {FPE_FLTUND_TRAP,   "floating point underflow"     },
332   {FPE_FLTDIV_TRAP,   "floating point divide"        },
333   {FPE_FLTINEX_TRAP,  "inexact floating point result"},
334   < {0,                 "unknown error"                }
335 };
336 #define SIGPC(scp)        (0) /* Info MIGHT be in scp->sc_jmpbuf.jmp_context.iar */
337 /*
338    For some reason, scp->sc_jmpbuf does not work on the RS6000, even though
339    it looks like it should from the include definitions.  It is probably
340    some strange interaction with the "POSIX_SOURCE" that we require.
341 */
342 
343 void PetscDefaultFPTrap(int sig, int code, struct sigcontext *scp) {
344   int      err_ind, j;
345   fp_ctx_t flt_context;
346 
347   PetscFunctionBegin;
348   fp_sh_trap_info(scp, &flt_context);
349 
350   err_ind = -1;
351   for (j = 0; error_codes[j].code_no; j++) {
352     if (error_codes[j].code_no == flt_context.trap) err_ind = j;
353   }
354 
355   if (err_ind >= 0) (*PetscErrorPrintf)("*** %s occurred ***\n", error_codes[err_ind].name);
356   else (*PetscErrorPrintf)("*** floating point error 0x%x occurred ***\n", flt_context.trap);
357 
358   (void)PetscError(PETSC_COMM_SELF, 0, NULL, NULL, PETSC_ERR_FP, PETSC_ERROR_REPEAT, "floating point error");
359   PETSCABORT(MPI_COMM_WORLD, PETSC_ERR_FP);
360 }
361 
362 PetscErrorCode PetscSetFPTrap(PetscFPTrap flag) {
363   PetscFunctionBegin;
364   if (flag == PETSC_FP_TRAP_ON) {
365     signal(SIGFPE, (void (*)(int))PetscDefaultFPTrap);
366     fp_trap(FP_TRAP_SYNC);
367     /* fp_enable(TRP_INVALID | TRP_DIV_BY_ZERO | TRP_OVERFLOW | TRP_UNDERFLOW); */
368     fp_enable(TRP_INVALID | TRP_DIV_BY_ZERO | TRP_OVERFLOW);
369   } else {
370     signal(SIGFPE, SIG_DFL);
371     fp_disable(TRP_INVALID | TRP_DIV_BY_ZERO | TRP_OVERFLOW | TRP_UNDERFLOW);
372     fp_trap(FP_TRAP_OFF);
373   }
374   _trapmode = flag;
375   PetscCall(PetscInfo(NULL, "Using PETSC_HAVE_RS6000_STYLE_FPTRAP\n"));
376   PetscFunctionReturn(0);
377 }
378 
379 PetscErrorCode PetscDetermineInitialFPTrap(void) {
380   PetscFunctionBegin;
381   PetscCall(PetscInfo(NULL, "Unable to determine initial floating point trapping. Assuming it is off\n"));
382   PetscFunctionReturn(0);
383 }
384 
385 /* ------------------------------------------------------------*/
386 #elif defined(PETSC_HAVE_WINDOWS_COMPILERS)
387 #include <float.h>
388 void PetscDefaultFPTrap(int sig) {
389   PetscFunctionBegin;
390   (*PetscErrorPrintf)("*** floating point error occurred ***\n");
391   PetscError(PETSC_COMM_SELF, 0, NULL, NULL, PETSC_ERR_FP, PETSC_ERROR_REPEAT, "floating point error");
392   PETSCABORT(MPI_COMM_WORLD, PETSC_ERR_FP);
393 }
394 
395 PetscErrorCode PetscSetFPTrap(PetscFPTrap flag) {
396   unsigned int cw;
397 
398   PetscFunctionBegin;
399   if (flag == PETSC_FP_TRAP_ON) {
400     /* cw = _EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW | _EM_UNDERFLOW; */
401     cw = _EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW;
402     PetscCheck(SIG_ERR != signal(SIGFPE, PetscDefaultFPTrap), PETSC_COMM_SELF, PETSC_ERR_LIB, "Can't set floating point handler");
403   } else {
404     cw = 0;
405     PetscCheck(SIG_ERR != signal(SIGFPE, SIG_DFL), PETSC_COMM_SELF, PETSC_ERR_LIB, "Can't clear floating point handler");
406   }
407   (void)_controlfp(0, cw);
408   _trapmode = flag;
409   PetscCall(PetscInfo(NULL, "Using PETSC_HAVE_WINDOWS_COMPILERS FPTRAP\n"));
410   PetscFunctionReturn(0);
411 }
412 
413 PetscErrorCode PetscDetermineInitialFPTrap(void) {
414   PetscFunctionBegin;
415   PetscCall(PetscInfo(NULL, "Unable to determine initial floating point trapping. Assuming it is off\n"));
416   PetscFunctionReturn(0);
417 }
418 
419 /* ------------------------------------------------------------*/
420 #elif defined(PETSC_HAVE_FENV_H) && !defined(__cplusplus)
421 /*
422    C99 style floating point environment.
423 
424    Note that C99 merely specifies how to save, restore, and clear the floating
425    point environment as well as defining an enumeration of exception codes.  In
426    particular, C99 does not specify how to make floating point exceptions raise
427    a signal.  Glibc offers this capability through FE_NOMASK_ENV (or with finer
428    granularity, feenableexcept()), xmmintrin.h offers _MM_SET_EXCEPTION_MASK().
429 */
430 #include <fenv.h>
431 typedef struct {
432   int         code;
433   const char *name;
434 } FPNode;
435 static const FPNode error_codes[] = {
436   {FE_DIVBYZERO, "divide by zero"                                 },
437   {FE_INEXACT,   "inexact floating point result"                  },
438   {FE_INVALID,   "invalid floating point arguments (domain error)"},
439   {FE_OVERFLOW,  "floating point overflow"                        },
440   {FE_UNDERFLOW, "floating point underflow"                       },
441   {0,            "unknown error"                                  }
442 };
443 
444 void PetscDefaultFPTrap(int sig) {
445   const FPNode *node;
446   int           code;
447   PetscBool     matched = PETSC_FALSE;
448 
449   PetscFunctionBegin;
450   /* Note: While it is possible for the exception state to be preserved by the
451    * kernel, this seems to be rare which makes the following flag testing almost
452    * useless.  But on a system where the flags can be preserved, it would provide
453    * more detail.
454    */
455   code = fetestexcept(FE_ALL_EXCEPT);
456   for (node = &error_codes[0]; node->code; node++) {
457     if (code & node->code) {
458       matched = PETSC_TRUE;
459       (*PetscErrorPrintf)("*** floating point error \"%s\" occurred ***\n", node->name);
460       code &= ~node->code; /* Unset this flag since it has been processed */
461     }
462   }
463   if (!matched || code) { /* If any remaining flags are set, or we didn't process any flags */
464     (*PetscErrorPrintf)("*** unknown floating point error occurred ***\n");
465     (*PetscErrorPrintf)("The specific exception can be determined by running in a debugger.  When the\n");
466     (*PetscErrorPrintf)("debugger traps the signal, the exception can be found with fetestexcept(0x%x)\n", FE_ALL_EXCEPT);
467     (*PetscErrorPrintf)("where the result is a bitwise OR of the following flags:\n");
468     (*PetscErrorPrintf)("FE_INVALID=0x%x FE_DIVBYZERO=0x%x FE_OVERFLOW=0x%x FE_UNDERFLOW=0x%x FE_INEXACT=0x%x\n", FE_INVALID, FE_DIVBYZERO, FE_OVERFLOW, FE_UNDERFLOW, FE_INEXACT);
469   }
470 
471   (*PetscErrorPrintf)("Try option -start_in_debugger\n");
472 #if PetscDefined(USE_DEBUG)
473   (*PetscErrorPrintf)("likely location of problem given in stack below\n");
474   (*PetscErrorPrintf)("---------------------  Stack Frames ------------------------------------\n");
475   PetscStackView(PETSC_STDOUT);
476 #else
477   (*PetscErrorPrintf)("configure using --with-debugging=yes, recompile, link, and run \n");
478   (*PetscErrorPrintf)("with -start_in_debugger to get more information on the crash.\n");
479 #endif
480   PetscError(PETSC_COMM_SELF, 0, NULL, NULL, PETSC_ERR_FP, PETSC_ERROR_INITIAL, "trapped floating point error");
481   PETSCABORT(MPI_COMM_WORLD, PETSC_ERR_FP);
482 }
483 
484 PetscErrorCode PetscSetFPTrap(PetscFPTrap flag) {
485   PetscFunctionBegin;
486   if (flag == PETSC_FP_TRAP_ON) {
487     /* Clear any flags that are currently set so that activating trapping will not immediately call the signal handler. */
488     PetscCheck(!feclearexcept(FE_ALL_EXCEPT), PETSC_COMM_SELF, PETSC_ERR_LIB, "Cannot clear floating point exception flags");
489 #if defined(FE_NOMASK_ENV)
490     /* Could use fesetenv(FE_NOMASK_ENV), but that causes spurious exceptions (like gettimeofday() -> PetscLogDouble). */
491     /* PetscCheck(feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW) != -1,PETSC_COMM_SELF,PETSC_ERR_LIB,"Cannot activate floating point exceptions"); */
492     /* Doesn't work on AArch64 targets. There's a known hardware limitation. Need to detect hardware at configure time? */
493     PetscCheck(feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW) != -1, PETSC_COMM_SELF, PETSC_ERR_LIB, "Cannot activate floating point exceptions");
494     PetscCall(PetscInfo(NULL, "Using PETSC_HAVE_FENV_H FPTRAP with FE_NOMASK_ENV\n"));
495 #elif defined PETSC_HAVE_XMMINTRIN_H
496     _MM_SET_EXCEPTION_MASK(_MM_GET_EXCEPTION_MASK() & ~_MM_MASK_DIV_ZERO);
497     /* _MM_SET_EXCEPTION_MASK(_MM_GET_EXCEPTION_MASK() & ~_MM_MASK_UNDERFLOW); */
498     _MM_SET_EXCEPTION_MASK(_MM_GET_EXCEPTION_MASK() & ~_MM_MASK_OVERFLOW);
499     _MM_SET_EXCEPTION_MASK(_MM_GET_EXCEPTION_MASK() & ~_MM_MASK_INVALID);
500     PetscCall(PetscInfo(NULL, "Using PETSC_HAVE_FENV_H FPTRAP with PETSC_HAVE_XMMINTRIN_H\n"));
501 #else
502     /* C99 does not provide a way to modify the environment so there is no portable way to activate trapping. */
503     PetscCall(PetscInfo(NULL, "Using PETSC_HAVE_FENV_H FPTRAP\n"));
504 #endif
505     PetscCheck(SIG_ERR != signal(SIGFPE, PetscDefaultFPTrap), PETSC_COMM_SELF, PETSC_ERR_LIB, "Can't set floating point handler");
506   } else {
507     PetscCheck(!fesetenv(FE_DFL_ENV), PETSC_COMM_SELF, PETSC_ERR_LIB, "Cannot disable floating point exceptions");
508     /* can use _MM_SET_EXCEPTION_MASK(_MM_GET_EXCEPTION_MASK() | _MM_MASK_UNDERFLOW); if PETSC_HAVE_XMMINTRIN_H exists */
509     PetscCheck(SIG_ERR != signal(SIGFPE, SIG_DFL), PETSC_COMM_SELF, PETSC_ERR_LIB, "Can't clear floating point handler");
510   }
511   _trapmode = flag;
512   PetscFunctionReturn(0);
513 }
514 
515 PetscErrorCode PetscDetermineInitialFPTrap(void) {
516 #if defined(FE_NOMASK_ENV) || defined PETSC_HAVE_XMMINTRIN_H
517   unsigned int flags;
518 #endif
519 
520   PetscFunctionBegin;
521 #if defined(FE_NOMASK_ENV)
522   flags = fegetexcept();
523   if (flags & FE_DIVBYZERO) {
524 #elif defined PETSC_HAVE_XMMINTRIN_H
525   flags = _MM_GET_EXCEPTION_MASK();
526   if (!(flags & _MM_MASK_DIV_ZERO)) {
527 #else
528   PetscCall(PetscInfo(NULL, "Floating point trapping unknown, assuming off\n"));
529   PetscFunctionReturn(0);
530 #endif
531 #if defined(FE_NOMASK_ENV) || defined PETSC_HAVE_XMMINTRIN_H
532     _trapmode = PETSC_FP_TRAP_ON;
533     PetscCall(PetscInfo(NULL, "Floating point trapping is on by default %d\n", flags));
534   } else {
535     _trapmode = PETSC_FP_TRAP_OFF;
536     PetscCall(PetscInfo(NULL, "Floating point trapping is off by default %d\n", flags));
537   }
538   PetscFunctionReturn(0);
539 #endif
540 }
541 
542 /* ------------------------------------------------------------*/
543 #elif defined(PETSC_HAVE_IEEEFP_H)
544 #include <ieeefp.h>
545 void PetscDefaultFPTrap(int sig) {
546   PetscFunctionBegin;
547   (*PetscErrorPrintf)("*** floating point error occurred ***\n");
548   PetscError(PETSC_COMM_SELF, 0, NULL, NULL, PETSC_ERR_FP, PETSC_ERROR_REPEAT, "floating point error");
549   PETSCABORT(MPI_COMM_WORLD, PETSC_ERR_FP);
550 }
551 
552 PetscErrorCode PetscSetFPTrap(PetscFPTrap flag) {
553   PetscFunctionBegin;
554   if (flag == PETSC_FP_TRAP_ON) {
555 #if defined(PETSC_HAVE_FPPRESETSTICKY)
556     fpresetsticky(fpgetsticky());
557 #elif defined(PETSC_HAVE_FPSETSTICKY)
558     fpsetsticky(fpgetsticky());
559 #endif
560     fpsetmask(FP_X_INV | FP_X_DZ | FP_X_OFL | FP_X_OFL);
561     PetscCheck(SIG_ERR != signal(SIGFPE, PetscDefaultFPTrap), PETSC_COMM_SELF, PETSC_ERR_LIB, "Can't set floating point handler");
562   } else {
563 #if defined(PETSC_HAVE_FPPRESETSTICKY)
564     fpresetsticky(fpgetsticky());
565 #elif defined(PETSC_HAVE_FPSETSTICKY)
566     fpsetsticky(fpgetsticky());
567 #endif
568     fpsetmask(0);
569     PetscCheck(SIG_ERR != signal(SIGFPE, SIG_DFL), PETSC_COMM_SELF, PETSC_ERR_LIB, "Can't clear floating point handler");
570   }
571   _trapmode = flag;
572   PetscCall(PetscInfo(NULL, "Using PETSC_HAVE_IEEEFP_H FPTRAP\n"));
573   PetscFunctionReturn(0);
574 }
575 
576 PetscErrorCode PetscDetermineInitialFPTrap(void) {
577   PetscFunctionBegin;
578   PetscCall(PetscInfo(NULL, "Unable to determine initial floating point trapping. Assuming it is off\n"));
579   PetscFunctionReturn(0);
580 }
581 
582 /* -------------------------Default -----------------------------------*/
583 #else
584 
585 void PetscDefaultFPTrap(int sig) {
586   PetscFunctionBegin;
587   (*PetscErrorPrintf)("*** floating point error occurred ***\n");
588   PetscError(PETSC_COMM_SELF, 0, NULL, NULL, PETSC_ERR_FP, PETSC_ERROR_REPEAT, "floating point error");
589   PETSCABORT(MPI_COMM_WORLD, PETSC_ERR_FP);
590 }
591 
592 PetscErrorCode PetscSetFPTrap(PetscFPTrap flag) {
593   PetscFunctionBegin;
594   if (flag == PETSC_FP_TRAP_ON) {
595     if (SIG_ERR == signal(SIGFPE, PetscDefaultFPTrap)) (*PetscErrorPrintf)("Can't set floatingpoint handler\n");
596   } else if (SIG_ERR == signal(SIGFPE, SIG_DFL)) (*PetscErrorPrintf)("Can't clear floatingpoint handler\n");
597 
598   _trapmode = flag;
599   PetscCall(PetscInfo(NULL, "Using default FPTRAP\n"));
600   PetscFunctionReturn(0);
601 }
602 
603 PetscErrorCode PetscDetermineInitialFPTrap(void) {
604   PetscFunctionBegin;
605   PetscCall(PetscInfo(NULL, "Unable to determine initial floating point trapping. Assuming it is off\n"));
606   PetscFunctionReturn(0);
607 }
608 #endif
609