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
20namespace Fortran::runtime {
21
22extern "C" {
23
24std::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

source code of flang/runtime/exceptions.cpp