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 | // Map Fortran ieee_arithmetic module exceptions to fenv.h exceptions. |
10 | |
11 | #include "flang/Runtime/exceptions.h" |
12 | #include "terminator.h" |
13 | #include "flang/Runtime/magic-numbers.h" |
14 | #include <cfenv> |
15 | |
16 | #ifndef __FE_DENORM |
17 | #define __FE_DENORM 0 // denorm is nonstandard |
18 | #endif |
19 | |
20 | namespace Fortran::runtime { |
21 | |
22 | extern "C" { |
23 | |
24 | std::int32_t RTNAME(MapException)(int32_t except) { |
25 | Terminator terminator{__FILE__, __LINE__}; |
26 | |
27 | static constexpr int32_t mask{_FORTRAN_RUNTIME_IEEE_INVALID | |
28 | _FORTRAN_RUNTIME_IEEE_DENORM | _FORTRAN_RUNTIME_IEEE_DIVIDE_BY_ZERO | |
29 | _FORTRAN_RUNTIME_IEEE_OVERFLOW | _FORTRAN_RUNTIME_IEEE_UNDERFLOW | |
30 | _FORTRAN_RUNTIME_IEEE_INEXACT}; |
31 | if (except == 0 || except != (except & mask)) { |
32 | terminator.Crash("Invalid exception value: %d" , except); |
33 | } |
34 | |
35 | // Fortran and fenv.h values are identical; return the value. |
36 | if constexpr (_FORTRAN_RUNTIME_IEEE_INVALID == FE_INVALID && |
37 | _FORTRAN_RUNTIME_IEEE_DENORM == __FE_DENORM && |
38 | _FORTRAN_RUNTIME_IEEE_DIVIDE_BY_ZERO == FE_DIVBYZERO && |
39 | _FORTRAN_RUNTIME_IEEE_OVERFLOW == FE_OVERFLOW && |
40 | _FORTRAN_RUNTIME_IEEE_UNDERFLOW == FE_UNDERFLOW && |
41 | _FORTRAN_RUNTIME_IEEE_INEXACT == FE_INEXACT) { |
42 | return except; |
43 | } |
44 | |
45 | // fenv.h calls that take exception arguments are able to process multiple |
46 | // exceptions in one call, such as FE_OVERFLOW | FE_DIVBYZERO | FE_INVALID. |
47 | // And intrinsic module procedures that manage exceptions are elemental |
48 | // procedures that may specify multiple exceptions, such as ieee_all. |
49 | // However, general elemental call processing places single scalar arguments |
50 | // in a loop. As a consequence, argument 'except' here will be a power of |
51 | // two, corresponding to a single exception. If code generation were |
52 | // modified to bypass normal elemental call processing for calls with |
53 | // ieee_usual, ieee_all, or user-specified array arguments, this switch |
54 | // could be extended to support that. |
55 | |
56 | // Fortran and fenv.h values differ. |
57 | switch (except) { |
58 | case _FORTRAN_RUNTIME_IEEE_INVALID: |
59 | return FE_INVALID; |
60 | case _FORTRAN_RUNTIME_IEEE_DENORM: |
61 | if (__FE_DENORM) { |
62 | return __FE_DENORM; |
63 | } |
64 | break; |
65 | case _FORTRAN_RUNTIME_IEEE_DIVIDE_BY_ZERO: |
66 | return FE_DIVBYZERO; |
67 | case _FORTRAN_RUNTIME_IEEE_OVERFLOW: |
68 | return FE_OVERFLOW; |
69 | case _FORTRAN_RUNTIME_IEEE_UNDERFLOW: |
70 | return FE_UNDERFLOW; |
71 | case _FORTRAN_RUNTIME_IEEE_INEXACT: |
72 | return FE_INEXACT; |
73 | } |
74 | |
75 | terminator.Crash("Invalid exception set: %d" , except); |
76 | } |
77 | |
78 | // Verify that the size of ieee_modes_type and ieee_status_type objects from |
79 | // intrinsic module file __fortran_ieee_exceptions.f90 are large enough to |
80 | // hold fenv_t object. |
81 | // TODO: fenv_t can be way larger than |
82 | // sizeof(int) * _FORTRAN_RUNTIME_IEEE_FENV_T_EXTENT |
83 | // on some systems, e.g. Solaris, so omit object size comparison for now. |
84 | // TODO: consider femode_t object size comparison once its more mature. |
85 | |
86 | } // extern "C" |
87 | } // namespace Fortran::runtime |
88 | |