xref: /llvm-project/flang/runtime/exceptions.cpp (revision 9d8dc45d17088300e9e2086594ca581b119193c8)
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