Version: SMASH-3.1
fpenvironment.cc
Go to the documentation of this file.
1 /*
2  *
3  * Copyright (c) 2015,2017-2019,2021
4  * SMASH Team
5  *
6  * GNU General Public License (GPLv3 or later)
7  *
8  */
9 
10 #include "smash/fpenvironment.h"
11 
12 #if defined __SSE__
13 #include <xmmintrin.h>
14 #endif
15 
16 #include <csignal>
17 
18 #include "smash/logging.h"
19 
20 namespace smash {
21 static constexpr int LFpe = LogArea::Fpe::id;
22 
23 #if !defined _GNU_SOURCE && defined __SSE__ && !defined __clang__
24 bool enable_float_traps(int femask) {
25  static_assert(FE_INVALID == 0x01,
26  "incorrect assumption that FE_INVALID == 0x01");
27  static_assert(FE_DIVBYZERO == 0x04,
28  "incorrect assumption that FE_DIVBYZERO == 0x04");
29  static_assert(FE_OVERFLOW == 0x08,
30  "incorrect assumption that FE_OVERFLOW == 0x08");
31  static_assert(FE_UNDERFLOW == 0x10,
32  "incorrect assumption that FE_UNDERFLOW == 0x10");
33  static_assert(FE_INEXACT == 0x20,
34  "incorrect assumption that FE_INEXACT == 0x20");
35 
36  // only accept supported values
37  femask &= FE_ALL_EXCEPT;
38 
39 #if defined __GNUC__ /*for inline asm*/
40  // get the current FPU control word
41  uint16_t fpucw;
42  asm volatile("fstcw %0" : "=m"(fpucw));
43 
44  // clear the bits where the FPU should trap
45  fpucw &= ~femask;
46 
47  // load the FPU control word back
48  asm volatile("fldcw %0" ::"m"(fpucw));
49 #endif
50 
51  // the SSE CSR has the bit positions 7 bit positions further to the left
52  femask <<= 7;
53 
54  /* Get the current CSR, mask of the relevant bits, and load it back into the
55  * SSE CSR.*/
56  _mm_setcsr(_mm_getcsr() & ~femask);
57 
58  // we did something, so return true
59  return true;
60 }
61 #endif
62 
64  if (!enable_float_traps(mask)) {
65  logg[LFpe].warn("Failed to setup traps on ", mask);
66  }
67 }
68 
70  {
71  // pole error occurred in a floating-point operation:
72  if (!enable_float_traps(FE_DIVBYZERO)) {
73  logg[LFpe].warn("Failed to setup trap on pole error.");
74  }
75 
76  // domain error occurred in an earlier floating-point operation:
77  if (!enable_float_traps(FE_INVALID)) {
78  logg[LFpe].warn("Failed to setup trap on domain error.");
79  }
80 
81  /* The result of the earlier floating-point operation was too large to be
82  * representable: */
83  if (!enable_float_traps(FE_OVERFLOW)) {
84  logg[LFpe].warn("Failed to setup trap on overflow.");
85  }
86 
87  /* There's also FE_UNDERFLOW, where the result of the earlier
88  * floating-point operation was subnormal with a loss of precision.
89  * We do not consider this an error by default.
90  * Furthermore, there's FE_INEXACT, but this traps if "rounding was
91  * necessary to store the result of an earlier floating-point
92  * operation". This is common and not really an error condition. */
93  }
94 
95 // Install the signal handler if we have the functionality.
96 #if (defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 199309L) || \
97  (defined _XOPEN_SOURCE && _XOPEN_SOURCE) || \
98  (defined _POSIX_SOURCE && _POSIX_SOURCE)
99 /* The missing-fields-initializers warning says that not all fields of this
100  * struct were explicitly initialized. That's exactly what is the intention
101  * here, because then they are default-initialized, which is zero. */
102 #pragma GCC diagnostic ignored "-Wmissing-field-initializers"
103  struct sigaction action = {};
104  action.sa_flags = SA_SIGINFO;
105  action.sa_sigaction = [](int signal, siginfo_t *info, void *) {
106  if (signal == SIGFPE) {
107  const char *msg = nullptr;
108  switch (info->si_code) {
109  case FPE_FLTDIV:
110  msg = "Division by Zero (NaN)";
111  break;
112  case FPE_FLTUND:
113  msg = "Underflow (result was subnormal with a loss of precision)";
114  break;
115  case FPE_FLTOVF:
116  msg = "Overflow (result was too large to be representable)";
117  break;
118  case FPE_FLTINV:
119  msg = "Invalid (domain error occurred)";
120  break;
121  case FPE_FLTRES:
122  msg =
123  "Inexact Result (rounding was necessary to store the result of "
124  "an earlier floating-point operation)";
125  break;
126  default:
127  msg = "unknown";
128  break;
129  }
131  "Floating point trap was raised: ", msg);
132  } else {
133  logg[LFpe].fatal(SMASH_SOURCE_LOCATION, "Unexpected Signal ", signal,
134  " received in the FPE signal handler. Aborting.");
135  }
136  std::abort();
137  };
138  sigaction(SIGFPE, &action, nullptr);
139 #endif
140 }
141 
142 } // namespace smash
void reenable_traps(int mask)
Reenables the given traps.
#define SMASH_SOURCE_LOCATION
Hackery that is required to output the location in the source code where the log statement occurs.
Definition: logging.h:153
std::array< einhard::Logger<>, std::tuple_size< LogArea::AreaTuple >::value > logg
An array that stores all pre-configured Logger objects.
Definition: logging.cc:39
Definition: action.h:24
bool enable_float_traps(int)
Fallback that fails to set the trap.
Definition: fpenvironment.h:40
static constexpr int LFpe
void setup_default_float_traps()
Setup the floating-point traps used throughout SMASH.