xref: /llvm-project/flang/runtime/exceptions.cpp (revision 9d8dc45d17088300e9e2086594ca581b119193c8)
1 //===-- runtime/exceptions.cpp --------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 // Runtime exception support.
10 
11 #include "flang/Runtime/exceptions.h"
12 #include "terminator.h"
13 #include <cfenv>
14 #if defined(__aarch64__) && !defined(_WIN32)
15 #include <fpu_control.h>
16 #elif defined(__x86_64__)
17 #include <xmmintrin.h>
18 #endif
19 
20 // fenv.h may not define exception macros.
21 #ifndef FE_INVALID
22 #define FE_INVALID 0
23 #endif
24 #ifndef FE_DIVBYZERO
25 #define FE_DIVBYZERO 0
26 #endif
27 #ifndef FE_OVERFLOW
28 #define FE_OVERFLOW 0
29 #endif
30 #ifndef FE_UNDERFLOW
31 #define FE_UNDERFLOW 0
32 #endif
33 #ifndef FE_INEXACT
34 #define FE_INEXACT 0
35 #endif
36 
37 namespace Fortran::runtime {
38 
39 extern "C" {
40 
41 // Map a set of Fortran ieee_arithmetic module exceptions to a libm fenv.h
42 // excepts value.
43 uint32_t RTNAME(MapException)(uint32_t excepts) {
44   Terminator terminator{__FILE__, __LINE__};
45 
46   static constexpr uint32_t v{FE_INVALID};
47 #if __x86_64__
48   static constexpr uint32_t s{__FE_DENORM}; // nonstandard, not a #define
49 #else
50   static constexpr uint32_t s{0};
51 #endif
52   static constexpr uint32_t z{FE_DIVBYZERO};
53   static constexpr uint32_t o{FE_OVERFLOW};
54   static constexpr uint32_t u{FE_UNDERFLOW};
55   static constexpr uint32_t x{FE_INEXACT};
56 
57 #define vm(p) p, p | v
58 #define sm(p) vm(p), vm(p | s)
59 #define zm(p) sm(p), sm(p | z)
60 #define om(p) zm(p), zm(p | o)
61 #define um(p) om(p), om(p | u)
62 #define xm um(0), um(x)
63 
64   static constexpr uint32_t map[]{xm};
65   static constexpr uint32_t mapSize{sizeof(map) / sizeof(uint32_t)};
66   static_assert(mapSize == 64);
67   if (excepts >= mapSize) {
68     terminator.Crash("Invalid excepts value: %d", excepts);
69   }
70   uint32_t except_value = map[excepts];
71   return except_value;
72 }
73 
74 // Check if the processor has the ability to control whether to halt or
75 // continue execution when a given exception is raised.
76 bool RTNAME(SupportHalting)([[maybe_unused]] uint32_t except) {
77 #ifdef __USE_GNU
78   except = RTNAME(MapException)(except);
79   int currentSet = fegetexcept(), flipSet, ok;
80   if (currentSet & except) {
81     ok = fedisableexcept(except);
82     flipSet = fegetexcept();
83     ok |= feenableexcept(except);
84   } else {
85     ok = feenableexcept(except);
86     flipSet = fegetexcept();
87     ok |= fedisableexcept(except);
88   }
89   return ok != -1 && currentSet != flipSet;
90 #else
91   return false;
92 #endif
93 }
94 
95 // A hardware FZ (flush to zero) bit is the negation of the
96 // ieee_[get|set]_underflow_mode GRADUAL argument.
97 #if defined(_MM_FLUSH_ZERO_MASK)
98 // The x86_64 MXCSR FZ bit affects computations of real kinds 3, 4, and 8.
99 #elif defined(_FPU_GETCW)
100 // The aarch64 FPCR FZ bit affects computations of real kinds 3, 4, and 8.
101 // bit 24: FZ   -- single, double precision flush to zero bit
102 // bit 19: FZ16 -- half precision flush to zero bit [not currently relevant]
103 #define _FPU_FPCR_FZ_MASK_ 0x01080000
104 #endif
105 
106 bool RTNAME(GetUnderflowMode)(void) {
107 #if defined(_MM_FLUSH_ZERO_MASK)
108   return _MM_GET_FLUSH_ZERO_MODE() == _MM_FLUSH_ZERO_OFF;
109 #elif defined(_FPU_GETCW)
110   uint64_t fpcr;
111   _FPU_GETCW(fpcr);
112   return (fpcr & _FPU_FPCR_FZ_MASK_) == 0;
113 #else
114   return false;
115 #endif
116 }
117 void RTNAME(SetUnderflowMode)(bool flag) {
118 #if defined(_MM_FLUSH_ZERO_MASK)
119   _MM_SET_FLUSH_ZERO_MODE(flag ? _MM_FLUSH_ZERO_OFF : _MM_FLUSH_ZERO_ON);
120 #elif defined(_FPU_GETCW)
121   uint64_t fpcr;
122   _FPU_GETCW(fpcr);
123   if (flag) {
124     fpcr &= ~_FPU_FPCR_FZ_MASK_;
125   } else {
126     fpcr |= _FPU_FPCR_FZ_MASK_;
127   }
128   _FPU_SETCW(fpcr);
129 #endif
130 }
131 
132 size_t RTNAME(GetModesTypeSize)(void) {
133 #ifdef __GLIBC_USE_IEC_60559_BFP_EXT
134   return sizeof(femode_t); // byte size of ieee_modes_type data
135 #else
136   return 8; // femode_t is not defined
137 #endif
138 }
139 size_t RTNAME(GetStatusTypeSize)(void) {
140   return sizeof(fenv_t); // byte size of ieee_status_type data
141 }
142 
143 } // extern "C"
144 } // namespace Fortran::runtime
145