13aba9264Svdonaldson //===-- runtime/exceptions.cpp --------------------------------------===// 23aba9264Svdonaldson // 33aba9264Svdonaldson // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 43aba9264Svdonaldson // See https://llvm.org/LICENSE.txt for license information. 53aba9264Svdonaldson // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 63aba9264Svdonaldson // 73aba9264Svdonaldson //===----------------------------------------------------------------------===// 83aba9264Svdonaldson 94cdc19b8Svdonaldson // Runtime exception support. 103aba9264Svdonaldson 113aba9264Svdonaldson #include "flang/Runtime/exceptions.h" 123aba9264Svdonaldson #include "terminator.h" 133aba9264Svdonaldson #include <cfenv> 14*9d8dc45dSvdonaldson #if defined(__aarch64__) && !defined(_WIN32) 15*9d8dc45dSvdonaldson #include <fpu_control.h> 16*9d8dc45dSvdonaldson #elif defined(__x86_64__) 176003be7eSvdonaldson #include <xmmintrin.h> 186003be7eSvdonaldson #endif 193aba9264Svdonaldson 20ff862d6dSvdonaldson // fenv.h may not define exception macros. 21b6686e76Sserge-sans-paille #ifndef FE_INVALID 22b6686e76Sserge-sans-paille #define FE_INVALID 0 23b6686e76Sserge-sans-paille #endif 24b6686e76Sserge-sans-paille #ifndef FE_DIVBYZERO 25b6686e76Sserge-sans-paille #define FE_DIVBYZERO 0 26b6686e76Sserge-sans-paille #endif 27b6686e76Sserge-sans-paille #ifndef FE_OVERFLOW 28b6686e76Sserge-sans-paille #define FE_OVERFLOW 0 29b6686e76Sserge-sans-paille #endif 30b6686e76Sserge-sans-paille #ifndef FE_UNDERFLOW 31b6686e76Sserge-sans-paille #define FE_UNDERFLOW 0 32b6686e76Sserge-sans-paille #endif 33b6686e76Sserge-sans-paille #ifndef FE_INEXACT 34b6686e76Sserge-sans-paille #define FE_INEXACT 0 35b6686e76Sserge-sans-paille #endif 363aba9264Svdonaldson 373aba9264Svdonaldson namespace Fortran::runtime { 383aba9264Svdonaldson 393aba9264Svdonaldson extern "C" { 403aba9264Svdonaldson 414cdc19b8Svdonaldson // Map a set of Fortran ieee_arithmetic module exceptions to a libm fenv.h 424cdc19b8Svdonaldson // excepts value. 434cdc19b8Svdonaldson uint32_t RTNAME(MapException)(uint32_t excepts) { 443aba9264Svdonaldson Terminator terminator{__FILE__, __LINE__}; 453aba9264Svdonaldson 464cdc19b8Svdonaldson static constexpr uint32_t v{FE_INVALID}; 47ff862d6dSvdonaldson #if __x86_64__ 48ff862d6dSvdonaldson static constexpr uint32_t s{__FE_DENORM}; // nonstandard, not a #define 49ff862d6dSvdonaldson #else 50ff862d6dSvdonaldson static constexpr uint32_t s{0}; 51ff862d6dSvdonaldson #endif 524cdc19b8Svdonaldson static constexpr uint32_t z{FE_DIVBYZERO}; 534cdc19b8Svdonaldson static constexpr uint32_t o{FE_OVERFLOW}; 544cdc19b8Svdonaldson static constexpr uint32_t u{FE_UNDERFLOW}; 554cdc19b8Svdonaldson static constexpr uint32_t x{FE_INEXACT}; 563aba9264Svdonaldson 574cdc19b8Svdonaldson #define vm(p) p, p | v 584cdc19b8Svdonaldson #define sm(p) vm(p), vm(p | s) 594cdc19b8Svdonaldson #define zm(p) sm(p), sm(p | z) 604cdc19b8Svdonaldson #define om(p) zm(p), zm(p | o) 614cdc19b8Svdonaldson #define um(p) om(p), om(p | u) 624cdc19b8Svdonaldson #define xm um(0), um(x) 633aba9264Svdonaldson 644cdc19b8Svdonaldson static constexpr uint32_t map[]{xm}; 654cdc19b8Svdonaldson static constexpr uint32_t mapSize{sizeof(map) / sizeof(uint32_t)}; 664cdc19b8Svdonaldson static_assert(mapSize == 64); 67ff862d6dSvdonaldson if (excepts >= mapSize) { 684cdc19b8Svdonaldson terminator.Crash("Invalid excepts value: %d", excepts); 693aba9264Svdonaldson } 70b6686e76Sserge-sans-paille uint32_t except_value = map[excepts]; 71b6686e76Sserge-sans-paille return except_value; 723aba9264Svdonaldson } 733aba9264Svdonaldson 74c28a7c1eSvdonaldson // Check if the processor has the ability to control whether to halt or 75c28a7c1eSvdonaldson // continue execution when a given exception is raised. 76c28a7c1eSvdonaldson bool RTNAME(SupportHalting)([[maybe_unused]] uint32_t except) { 77df129836Svdonaldson #ifdef __USE_GNU 78c28a7c1eSvdonaldson except = RTNAME(MapException)(except); 79c28a7c1eSvdonaldson int currentSet = fegetexcept(), flipSet, ok; 80c28a7c1eSvdonaldson if (currentSet & except) { 81c28a7c1eSvdonaldson ok = fedisableexcept(except); 82c28a7c1eSvdonaldson flipSet = fegetexcept(); 83c28a7c1eSvdonaldson ok |= feenableexcept(except); 84c28a7c1eSvdonaldson } else { 85c28a7c1eSvdonaldson ok = feenableexcept(except); 86c28a7c1eSvdonaldson flipSet = fegetexcept(); 87c28a7c1eSvdonaldson ok |= fedisableexcept(except); 88c28a7c1eSvdonaldson } 89c28a7c1eSvdonaldson return ok != -1 && currentSet != flipSet; 90dcb7f44cSvdonaldson #else 91dcb7f44cSvdonaldson return false; 92c28a7c1eSvdonaldson #endif 93c28a7c1eSvdonaldson } 94c28a7c1eSvdonaldson 95*9d8dc45dSvdonaldson // A hardware FZ (flush to zero) bit is the negation of the 96*9d8dc45dSvdonaldson // ieee_[get|set]_underflow_mode GRADUAL argument. 97*9d8dc45dSvdonaldson #if defined(_MM_FLUSH_ZERO_MASK) 98*9d8dc45dSvdonaldson // The x86_64 MXCSR FZ bit affects computations of real kinds 3, 4, and 8. 99*9d8dc45dSvdonaldson #elif defined(_FPU_GETCW) 100*9d8dc45dSvdonaldson // The aarch64 FPCR FZ bit affects computations of real kinds 3, 4, and 8. 101*9d8dc45dSvdonaldson // bit 24: FZ -- single, double precision flush to zero bit 102*9d8dc45dSvdonaldson // bit 19: FZ16 -- half precision flush to zero bit [not currently relevant] 103*9d8dc45dSvdonaldson #define _FPU_FPCR_FZ_MASK_ 0x01080000 104*9d8dc45dSvdonaldson #endif 105*9d8dc45dSvdonaldson 1066003be7eSvdonaldson bool RTNAME(GetUnderflowMode)(void) { 107*9d8dc45dSvdonaldson #if defined(_MM_FLUSH_ZERO_MASK) 1086003be7eSvdonaldson return _MM_GET_FLUSH_ZERO_MODE() == _MM_FLUSH_ZERO_OFF; 109*9d8dc45dSvdonaldson #elif defined(_FPU_GETCW) 110*9d8dc45dSvdonaldson uint64_t fpcr; 111*9d8dc45dSvdonaldson _FPU_GETCW(fpcr); 112*9d8dc45dSvdonaldson return (fpcr & _FPU_FPCR_FZ_MASK_) == 0; 1136003be7eSvdonaldson #else 1146003be7eSvdonaldson return false; 1156003be7eSvdonaldson #endif 1166003be7eSvdonaldson } 1176003be7eSvdonaldson void RTNAME(SetUnderflowMode)(bool flag) { 118*9d8dc45dSvdonaldson #if defined(_MM_FLUSH_ZERO_MASK) 1196003be7eSvdonaldson _MM_SET_FLUSH_ZERO_MODE(flag ? _MM_FLUSH_ZERO_OFF : _MM_FLUSH_ZERO_ON); 120*9d8dc45dSvdonaldson #elif defined(_FPU_GETCW) 121*9d8dc45dSvdonaldson uint64_t fpcr; 122*9d8dc45dSvdonaldson _FPU_GETCW(fpcr); 123*9d8dc45dSvdonaldson if (flag) { 124*9d8dc45dSvdonaldson fpcr &= ~_FPU_FPCR_FZ_MASK_; 125*9d8dc45dSvdonaldson } else { 126*9d8dc45dSvdonaldson fpcr |= _FPU_FPCR_FZ_MASK_; 127*9d8dc45dSvdonaldson } 128*9d8dc45dSvdonaldson _FPU_SETCW(fpcr); 1296003be7eSvdonaldson #endif 1306003be7eSvdonaldson } 1316003be7eSvdonaldson 132ff862d6dSvdonaldson size_t RTNAME(GetModesTypeSize)(void) { 133ff862d6dSvdonaldson #ifdef __GLIBC_USE_IEC_60559_BFP_EXT 134ff862d6dSvdonaldson return sizeof(femode_t); // byte size of ieee_modes_type data 135ff862d6dSvdonaldson #else 136ff862d6dSvdonaldson return 8; // femode_t is not defined 137ff862d6dSvdonaldson #endif 138ff862d6dSvdonaldson } 139ff862d6dSvdonaldson size_t RTNAME(GetStatusTypeSize)(void) { 140ff862d6dSvdonaldson return sizeof(fenv_t); // byte size of ieee_status_type data 141ff862d6dSvdonaldson } 142ff862d6dSvdonaldson 1433aba9264Svdonaldson } // extern "C" 1443aba9264Svdonaldson } // namespace Fortran::runtime 145