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