xref: /petsc/src/sys/error/fp.c (revision bd2b07b136135af06fb9210476dad9bf59a36938)
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    Caution:
175    On certain machines, in particular the IBM PowerPC, floating point
176    trapping may be VERY slow!
177 
178 .seealso: `PetscFPTrapPush()`, `PetscFPTrapPop()`, `PetscDetermineInitialFPTrap()`
179 @*/
180 PetscErrorCode PetscSetFPTrap(PetscFPTrap flag) {
181   char *out;
182 
183   PetscFunctionBegin;
184   /* Clear accumulated exceptions.  Used to suppress meaningless messages from f77 programs */
185   (void)ieee_flags("clear", "exception", "all", &out);
186   if (flag == PETSC_FP_TRAP_ON) {
187     /*
188       To trap more fp exceptions, including underflow, change the line below to
189       if (ieee_handler("set","all",PetscDefaultFPTrap)) {
190     */
191     if (ieee_handler("set", "common", PetscDefaultFPTrap)) (*PetscErrorPrintf)("Can't set floatingpoint handler\n");
192   } else if (ieee_handler("clear", "common", PetscDefaultFPTrap)) (*PetscErrorPrintf)("Can't clear floatingpoint handler\n");
193 
194   _trapmode = flag;
195   PetscCall(PetscInfo(NULL, "Using PETSC_HAVE_SUN4_STYLE_FPTRAP\n"));
196   PetscFunctionReturn(0);
197 }
198 
199 /*@
200    PetscDetermineInitialFPTrap - Attempts to determine the floating point trapping that exists when PetscInitialize() is called
201 
202    Not Collective
203 
204    Notes:
205       Currently only supported on Linux and MacOS. Checks if divide by zero is enable and if so declares that trapping is on.
206 
207    Level: advanced
208 
209 .seealso: `PetscFPTrapPush()`, `PetscFPTrapPop()`, `PetscDetermineInitialFPTrap()`
210 @*/
211 PetscErrorCode PetscDetermineInitialFPTrap(void) {
212   PetscFunctionBegin;
213   PetscCall(PetscInfo(NULL, "Unable to determine initial floating point trapping. Assuming it is off\n"));
214   PetscFunctionReturn(0);
215 }
216 
217 /* -------------------------------------------------------------------------------------------*/
218 #elif defined(PETSC_HAVE_SOLARIS_STYLE_FPTRAP)
219 #include <sunmath.h>
220 #include <floatingpoint.h>
221 #include <siginfo.h>
222 #include <ucontext.h>
223 
224 static struct {
225   int   code_no;
226   char *name;
227 } error_codes[] = {
228   {FPE_FLTINV, "invalid floating point operand"},
229   {FPE_FLTRES, "inexact floating point result" },
230   {FPE_FLTDIV, "division-by-zero"              },
231   {FPE_FLTUND, "floating point underflow"      },
232   {FPE_FLTOVF, "floating point overflow"       },
233   {0,          "unknown error"                 }
234 };
235 #define SIGPC(scp) (scp->si_addr)
236 
237 void PetscDefaultFPTrap(int sig, siginfo_t *scp, ucontext_t *uap) {
238   int err_ind = -1, code = scp->si_code;
239 
240   PetscFunctionBegin;
241   for (int j = 0; error_codes[j].code_no; j++) {
242     if (error_codes[j].code_no == code) err_ind = j;
243   }
244 
245   if (err_ind >= 0) (*PetscErrorPrintf)("*** %s occurred at pc=%X ***\n", error_codes[err_ind].name, SIGPC(scp));
246   else (*PetscErrorPrintf)("*** floating point error 0x%x occurred at pc=%X ***\n", code, SIGPC(scp));
247 
248   (void)PetscError(PETSC_COMM_SELF, 0, NULL, NULL, PETSC_ERR_FP, PETSC_ERROR_REPEAT, "floating point error");
249   PETSCABORT(MPI_COMM_WORLD, PETSC_ERR_FP);
250 }
251 
252 PetscErrorCode PetscSetFPTrap(PetscFPTrap flag) {
253   char *out;
254 
255   PetscFunctionBegin;
256   /* Clear accumulated exceptions.  Used to suppress meaningless messages from f77 programs */
257   (void)ieee_flags("clear", "exception", "all", &out);
258   if (flag == PETSC_FP_TRAP_ON) {
259     if (ieee_handler("set", "common", (sigfpe_handler_type)PetscDefaultFPTrap)) (*PetscErrorPrintf)("Can't set floating point handler\n");
260   } else {
261     if (ieee_handler("clear", "common", (sigfpe_handler_type)PetscDefaultFPTrap)) (*PetscErrorPrintf)("Can't clear floatingpoint handler\n");
262   }
263   _trapmode = flag;
264   PetscCall(PetscInfo(NULL,"Using PETSC_HAVE_SOLARIS_STYLE_FPTRAP\n");
265   PetscFunctionReturn(0);
266 }
267 
268 PetscErrorCode PetscDetermineInitialFPTrap(void) {
269   PetscFunctionBegin;
270   PetscCall(PetscInfo(NULL, "Unable to determine initial floating point trapping. Assuming it is off\n"));
271   PetscFunctionReturn(0);
272 }
273 
274 /* ------------------------------------------------------------------------------------------*/
275 #elif defined(PETSC_HAVE_IRIX_STYLE_FPTRAP)
276 #include <sigfpe.h>
277 static struct {
278   int   code_no;
279   char *name;
280 } error_codes[] = {
281   {_INVALID, "IEEE operand error"      },
282   {_OVERFL,  "floating point overflow" },
283   {_UNDERFL, "floating point underflow"},
284   {_DIVZERO, "floating point divide"   },
285   {0,        "unknown error"           }
286 };
287 void PetscDefaultFPTrap(unsigned exception[], int val[]) {
288   int err_ind = -1, code = exception[0];
289 
290   PetscFunctionBegin;
291   for (int j = 0; error_codes[j].code_no; j++) {
292     if (error_codes[j].code_no == code) err_ind = j;
293   }
294   if (err_ind >= 0) (*PetscErrorPrintf)("*** %s occurred ***\n", error_codes[err_ind].name);
295   else (*PetscErrorPrintf)("*** floating point error 0x%x occurred ***\n", code);
296 
297   (void)PetscError(PETSC_COMM_SELF, 0, NULL, NULL, PETSC_ERR_FP, PETSC_ERROR_REPEAT, "floating point error");
298   PETSCABORT(MPI_COMM_WORLD, PETSC_ERR_FP);
299 }
300 
301 PetscErrorCode PetscSetFPTrap(PetscFPTrap flag) {
302   PetscFunctionBegin;
303   if (flag == PETSC_FP_TRAP_ON) handle_sigfpes(_ON, , _EN_UNDERFL | _EN_OVERFL | _EN_DIVZERO | _EN_INVALID, PetscDefaultFPTrap, _ABORT_ON_ERROR, 0);
304   else handle_sigfpes(_OFF, _EN_UNDERFL | _EN_OVERFL | _EN_DIVZERO | _EN_INVALID, 0, _ABORT_ON_ERROR, 0);
305   _trapmode = flag;
306   PetscCall(PetscInfo(NULL, "Using PETSC_HAVE_IRIX_STYLE_FPTRAP\n"));
307   PetscFunctionReturn(0);
308 }
309 
310 PetscErrorCode PetscDetermineInitialFPTrap(void) {
311   PetscFunctionBegin;
312   PetscCall(PetscInfo(NULL, "Unable to determine initial floating point trapping. Assuming it is off\n"));
313   PetscFunctionReturn(0);
314 }
315 
316 /*----------------------------------------------- --------------------------------------------*/
317 #elif defined(PETSC_HAVE_RS6000_STYLE_FPTRAP)
318 /* In "fast" mode, floating point traps are imprecise and ignored.
319    This is the reason for the fptrap(FP_TRAP_SYNC) call */
320 struct sigcontext;
321 #include <fpxcp.h>
322 #include <fptrap.h>
323 #define FPE_FLTOPERR_TRAP (fptrap_t)(0x20000000)
324 #define FPE_FLTOVF_TRAP   (fptrap_t)(0x10000000)
325 #define FPE_FLTUND_TRAP   (fptrap_t)(0x08000000)
326 #define FPE_FLTDIV_TRAP   (fptrap_t)(0x04000000)
327 #define FPE_FLTINEX_TRAP  (fptrap_t)(0x02000000)
328 
329 static struct {
330   int   code_no;
331   char *name;
332 } error_codes[] = {
333   {FPE_FLTOPERR_TRAP, "IEEE operand error"           },
334   {FPE_FLTOVF_TRAP,   "floating point overflow"      },
335   {FPE_FLTUND_TRAP,   "floating point underflow"     },
336   {FPE_FLTDIV_TRAP,   "floating point divide"        },
337   {FPE_FLTINEX_TRAP,  "inexact floating point result"},
338   < {0,                 "unknown error"                }
339 };
340 #define SIGPC(scp)        (0) /* Info MIGHT be in scp->sc_jmpbuf.jmp_context.iar */
341 /*
342    For some reason, scp->sc_jmpbuf does not work on the RS6000, even though
343    it looks like it should from the include definitions.  It is probably
344    some strange interaction with the "POSIX_SOURCE" that we require.
345 */
346 
347 void PetscDefaultFPTrap(int sig, int code, struct sigcontext *scp) {
348   int      err_ind, j;
349   fp_ctx_t flt_context;
350 
351   PetscFunctionBegin;
352   fp_sh_trap_info(scp, &flt_context);
353 
354   err_ind = -1;
355   for (j = 0; error_codes[j].code_no; j++) {
356     if (error_codes[j].code_no == flt_context.trap) err_ind = j;
357   }
358 
359   if (err_ind >= 0) (*PetscErrorPrintf)("*** %s occurred ***\n", error_codes[err_ind].name);
360   else (*PetscErrorPrintf)("*** floating point error 0x%x occurred ***\n", flt_context.trap);
361 
362   (void)PetscError(PETSC_COMM_SELF, 0, NULL, NULL, PETSC_ERR_FP, PETSC_ERROR_REPEAT, "floating point error");
363   PETSCABORT(MPI_COMM_WORLD, PETSC_ERR_FP);
364 }
365 
366 PetscErrorCode PetscSetFPTrap(PetscFPTrap flag) {
367   PetscFunctionBegin;
368   if (flag == PETSC_FP_TRAP_ON) {
369     signal(SIGFPE, (void (*)(int))PetscDefaultFPTrap);
370     fp_trap(FP_TRAP_SYNC);
371     /* fp_enable(TRP_INVALID | TRP_DIV_BY_ZERO | TRP_OVERFLOW | TRP_UNDERFLOW); */
372     fp_enable(TRP_INVALID | TRP_DIV_BY_ZERO | TRP_OVERFLOW);
373   } else {
374     signal(SIGFPE, SIG_DFL);
375     fp_disable(TRP_INVALID | TRP_DIV_BY_ZERO | TRP_OVERFLOW | TRP_UNDERFLOW);
376     fp_trap(FP_TRAP_OFF);
377   }
378   _trapmode = flag;
379   PetscCall(PetscInfo(NULL, "Using PETSC_HAVE_RS6000_STYLE_FPTRAP\n"));
380   PetscFunctionReturn(0);
381 }
382 
383 PetscErrorCode PetscDetermineInitialFPTrap(void) {
384   PetscFunctionBegin;
385   PetscCall(PetscInfo(NULL, "Unable to determine initial floating point trapping. Assuming it is off\n"));
386   PetscFunctionReturn(0);
387 }
388 
389 /* ------------------------------------------------------------*/
390 #elif defined(PETSC_HAVE_WINDOWS_COMPILERS)
391 #include <float.h>
392 void PetscDefaultFPTrap(int sig) {
393   PetscFunctionBegin;
394   (*PetscErrorPrintf)("*** floating point error occurred ***\n");
395   PetscError(PETSC_COMM_SELF, 0, NULL, NULL, PETSC_ERR_FP, PETSC_ERROR_REPEAT, "floating point error");
396   PETSCABORT(MPI_COMM_WORLD, PETSC_ERR_FP);
397 }
398 
399 PetscErrorCode PetscSetFPTrap(PetscFPTrap flag) {
400   unsigned int cw;
401 
402   PetscFunctionBegin;
403   if (flag == PETSC_FP_TRAP_ON) {
404     /* cw = _EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW | _EM_UNDERFLOW; */
405     cw = _EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW;
406     PetscCheck(SIG_ERR != signal(SIGFPE, PetscDefaultFPTrap), PETSC_COMM_SELF, PETSC_ERR_LIB, "Can't set floating point handler");
407   } else {
408     cw = 0;
409     PetscCheck(SIG_ERR != signal(SIGFPE, SIG_DFL), PETSC_COMM_SELF, PETSC_ERR_LIB, "Can't clear floating point handler");
410   }
411   (void)_controlfp(0, cw);
412   _trapmode = flag;
413   PetscCall(PetscInfo(NULL, "Using PETSC_HAVE_WINDOWS_COMPILERS FPTRAP\n"));
414   PetscFunctionReturn(0);
415 }
416 
417 PetscErrorCode PetscDetermineInitialFPTrap(void) {
418   PetscFunctionBegin;
419   PetscCall(PetscInfo(NULL, "Unable to determine initial floating point trapping. Assuming it is off\n"));
420   PetscFunctionReturn(0);
421 }
422 
423 /* ------------------------------------------------------------*/
424 #elif defined(PETSC_HAVE_FENV_H) && !defined(__cplusplus)
425 /*
426    C99 style floating point environment.
427 
428    Note that C99 merely specifies how to save, restore, and clear the floating
429    point environment as well as defining an enumeration of exception codes.  In
430    particular, C99 does not specify how to make floating point exceptions raise
431    a signal.  Glibc offers this capability through FE_NOMASK_ENV (or with finer
432    granularity, feenableexcept()), xmmintrin.h offers _MM_SET_EXCEPTION_MASK().
433 */
434 #include <fenv.h>
435 typedef struct {
436   int         code;
437   const char *name;
438 } FPNode;
439 static const FPNode error_codes[] = {
440   {FE_DIVBYZERO, "divide by zero"                                 },
441   {FE_INEXACT,   "inexact floating point result"                  },
442   {FE_INVALID,   "invalid floating point arguments (domain error)"},
443   {FE_OVERFLOW,  "floating point overflow"                        },
444   {FE_UNDERFLOW, "floating point underflow"                       },
445   {0,            "unknown error"                                  }
446 };
447 
448 void PetscDefaultFPTrap(int sig) {
449   const FPNode *node;
450   int           code;
451   PetscBool     matched = PETSC_FALSE;
452 
453   PetscFunctionBegin;
454   /* Note: While it is possible for the exception state to be preserved by the
455    * kernel, this seems to be rare which makes the following flag testing almost
456    * useless.  But on a system where the flags can be preserved, it would provide
457    * more detail.
458    */
459   code = fetestexcept(FE_ALL_EXCEPT);
460   for (node = &error_codes[0]; node->code; node++) {
461     if (code & node->code) {
462       matched = PETSC_TRUE;
463       (*PetscErrorPrintf)("*** floating point error \"%s\" occurred ***\n", node->name);
464       code &= ~node->code; /* Unset this flag since it has been processed */
465     }
466   }
467   if (!matched || code) { /* If any remaining flags are set, or we didn't process any flags */
468     (*PetscErrorPrintf)("*** unknown floating point error occurred ***\n");
469     (*PetscErrorPrintf)("The specific exception can be determined by running in a debugger.  When the\n");
470     (*PetscErrorPrintf)("debugger traps the signal, the exception can be found with fetestexcept(0x%x)\n", FE_ALL_EXCEPT);
471     (*PetscErrorPrintf)("where the result is a bitwise OR of the following flags:\n");
472     (*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);
473   }
474 
475   (*PetscErrorPrintf)("Try option -start_in_debugger\n");
476 #if PetscDefined(USE_DEBUG)
477   (*PetscErrorPrintf)("likely location of problem given in stack below\n");
478   (*PetscErrorPrintf)("---------------------  Stack Frames ------------------------------------\n");
479   PetscStackView(PETSC_STDOUT);
480 #else
481   (*PetscErrorPrintf)("configure using --with-debugging=yes, recompile, link, and run \n");
482   (*PetscErrorPrintf)("with -start_in_debugger to get more information on the crash.\n");
483 #endif
484   PetscError(PETSC_COMM_SELF, 0, NULL, NULL, PETSC_ERR_FP, PETSC_ERROR_INITIAL, "trapped floating point error");
485   PETSCABORT(MPI_COMM_WORLD, PETSC_ERR_FP);
486 }
487 
488 PetscErrorCode PetscSetFPTrap(PetscFPTrap flag) {
489   PetscFunctionBegin;
490   if (flag == PETSC_FP_TRAP_ON) {
491     /* Clear any flags that are currently set so that activating trapping will not immediately call the signal handler. */
492     PetscCheck(!feclearexcept(FE_ALL_EXCEPT), PETSC_COMM_SELF, PETSC_ERR_LIB, "Cannot clear floating point exception flags");
493 #if defined(FE_NOMASK_ENV)
494     /* Could use fesetenv(FE_NOMASK_ENV), but that causes spurious exceptions (like gettimeofday() -> PetscLogDouble). */
495     /* PetscCheck(feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW) != -1,PETSC_COMM_SELF,PETSC_ERR_LIB,"Cannot activate floating point exceptions"); */
496     /* Doesn't work on AArch64 targets. There's a known hardware limitation. Need to detect hardware at configure time? */
497     PetscCheck(feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW) != -1, PETSC_COMM_SELF, PETSC_ERR_LIB, "Cannot activate floating point exceptions");
498     PetscCall(PetscInfo(NULL, "Using PETSC_HAVE_FENV_H FPTRAP with FE_NOMASK_ENV\n"));
499 #elif defined PETSC_HAVE_XMMINTRIN_H
500     _MM_SET_EXCEPTION_MASK(_MM_GET_EXCEPTION_MASK() & ~_MM_MASK_DIV_ZERO);
501     /* _MM_SET_EXCEPTION_MASK(_MM_GET_EXCEPTION_MASK() & ~_MM_MASK_UNDERFLOW); */
502     _MM_SET_EXCEPTION_MASK(_MM_GET_EXCEPTION_MASK() & ~_MM_MASK_OVERFLOW);
503     _MM_SET_EXCEPTION_MASK(_MM_GET_EXCEPTION_MASK() & ~_MM_MASK_INVALID);
504     PetscCall(PetscInfo(NULL, "Using PETSC_HAVE_FENV_H FPTRAP with PETSC_HAVE_XMMINTRIN_H\n"));
505 #else
506     /* C99 does not provide a way to modify the environment so there is no portable way to activate trapping. */
507     PetscCall(PetscInfo(NULL, "Using PETSC_HAVE_FENV_H FPTRAP\n"));
508 #endif
509     PetscCheck(SIG_ERR != signal(SIGFPE, PetscDefaultFPTrap), PETSC_COMM_SELF, PETSC_ERR_LIB, "Can't set floating point handler");
510   } else {
511     PetscCheck(!fesetenv(FE_DFL_ENV), PETSC_COMM_SELF, PETSC_ERR_LIB, "Cannot disable floating point exceptions");
512     /* can use _MM_SET_EXCEPTION_MASK(_MM_GET_EXCEPTION_MASK() | _MM_MASK_UNDERFLOW); if PETSC_HAVE_XMMINTRIN_H exists */
513     PetscCheck(SIG_ERR != signal(SIGFPE, SIG_DFL), PETSC_COMM_SELF, PETSC_ERR_LIB, "Can't clear floating point handler");
514   }
515   _trapmode = flag;
516   PetscFunctionReturn(0);
517 }
518 
519 PetscErrorCode PetscDetermineInitialFPTrap(void) {
520 #if defined(FE_NOMASK_ENV) || defined PETSC_HAVE_XMMINTRIN_H
521   unsigned int flags;
522 #endif
523 
524   PetscFunctionBegin;
525 #if defined(FE_NOMASK_ENV)
526   flags = fegetexcept();
527   if (flags & FE_DIVBYZERO) {
528 #elif defined PETSC_HAVE_XMMINTRIN_H
529   flags = _MM_GET_EXCEPTION_MASK();
530   if (!(flags & _MM_MASK_DIV_ZERO)) {
531 #else
532   PetscCall(PetscInfo(NULL, "Floating point trapping unknown, assuming off\n"));
533   PetscFunctionReturn(0);
534 #endif
535 #if defined(FE_NOMASK_ENV) || defined PETSC_HAVE_XMMINTRIN_H
536     _trapmode = PETSC_FP_TRAP_ON;
537     PetscCall(PetscInfo(NULL, "Floating point trapping is on by default %d\n", flags));
538   } else {
539     _trapmode = PETSC_FP_TRAP_OFF;
540     PetscCall(PetscInfo(NULL, "Floating point trapping is off by default %d\n", flags));
541   }
542   PetscFunctionReturn(0);
543 #endif
544 }
545 
546 /* ------------------------------------------------------------*/
547 #elif defined(PETSC_HAVE_IEEEFP_H)
548 #include <ieeefp.h>
549 void PetscDefaultFPTrap(int sig) {
550   PetscFunctionBegin;
551   (*PetscErrorPrintf)("*** floating point error occurred ***\n");
552   PetscError(PETSC_COMM_SELF, 0, NULL, NULL, PETSC_ERR_FP, PETSC_ERROR_REPEAT, "floating point error");
553   PETSCABORT(MPI_COMM_WORLD, PETSC_ERR_FP);
554 }
555 
556 PetscErrorCode PetscSetFPTrap(PetscFPTrap flag) {
557   PetscFunctionBegin;
558   if (flag == PETSC_FP_TRAP_ON) {
559 #if defined(PETSC_HAVE_FPPRESETSTICKY)
560     fpresetsticky(fpgetsticky());
561 #elif defined(PETSC_HAVE_FPSETSTICKY)
562     fpsetsticky(fpgetsticky());
563 #endif
564     fpsetmask(FP_X_INV | FP_X_DZ | FP_X_OFL | FP_X_OFL);
565     PetscCheck(SIG_ERR != signal(SIGFPE, PetscDefaultFPTrap), PETSC_COMM_SELF, PETSC_ERR_LIB, "Can't set floating point handler");
566   } else {
567 #if defined(PETSC_HAVE_FPPRESETSTICKY)
568     fpresetsticky(fpgetsticky());
569 #elif defined(PETSC_HAVE_FPSETSTICKY)
570     fpsetsticky(fpgetsticky());
571 #endif
572     fpsetmask(0);
573     PetscCheck(SIG_ERR != signal(SIGFPE, SIG_DFL), PETSC_COMM_SELF, PETSC_ERR_LIB, "Can't clear floating point handler");
574   }
575   _trapmode = flag;
576   PetscCall(PetscInfo(NULL, "Using PETSC_HAVE_IEEEFP_H FPTRAP\n"));
577   PetscFunctionReturn(0);
578 }
579 
580 PetscErrorCode PetscDetermineInitialFPTrap(void) {
581   PetscFunctionBegin;
582   PetscCall(PetscInfo(NULL, "Unable to determine initial floating point trapping. Assuming it is off\n"));
583   PetscFunctionReturn(0);
584 }
585 
586 /* -------------------------Default -----------------------------------*/
587 #else
588 
589 void PetscDefaultFPTrap(int sig) {
590   PetscFunctionBegin;
591   (*PetscErrorPrintf)("*** floating point error occurred ***\n");
592   PetscError(PETSC_COMM_SELF, 0, NULL, NULL, PETSC_ERR_FP, PETSC_ERROR_REPEAT, "floating point error");
593   PETSCABORT(MPI_COMM_WORLD, PETSC_ERR_FP);
594 }
595 
596 PetscErrorCode PetscSetFPTrap(PetscFPTrap flag) {
597   PetscFunctionBegin;
598   if (flag == PETSC_FP_TRAP_ON) {
599     if (SIG_ERR == signal(SIGFPE, PetscDefaultFPTrap)) (*PetscErrorPrintf)("Can't set floatingpoint handler\n");
600   } else if (SIG_ERR == signal(SIGFPE, SIG_DFL)) (*PetscErrorPrintf)("Can't clear floatingpoint handler\n");
601 
602   _trapmode = flag;
603   PetscCall(PetscInfo(NULL, "Using default FPTRAP\n"));
604   PetscFunctionReturn(0);
605 }
606 
607 PetscErrorCode PetscDetermineInitialFPTrap(void) {
608   PetscFunctionBegin;
609   PetscCall(PetscInfo(NULL, "Unable to determine initial floating point trapping. Assuming it is off\n"));
610   PetscFunctionReturn(0);
611 }
612 #endif
613