blob: dfd3b812e22a13d6901fe5d4ec0ea8146242dcae [file] [log] [blame]
//===-- runtime/exceptions.cpp --------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Map Fortran ieee_arithmetic module exceptions to fenv.h exceptions.
#include "flang/Runtime/exceptions.h"
#include "terminator.h"
#include "flang/Runtime/magic-numbers.h"
#include <cfenv>
#ifndef __FE_DENORM
#define __FE_DENORM 0 // denorm is nonstandard
#endif
namespace Fortran::runtime {
extern "C" {
std::int32_t RTNAME(MapException)(int32_t except) {
Terminator terminator{__FILE__, __LINE__};
static constexpr int32_t mask{_FORTRAN_RUNTIME_IEEE_INVALID |
_FORTRAN_RUNTIME_IEEE_DENORM | _FORTRAN_RUNTIME_IEEE_DIVIDE_BY_ZERO |
_FORTRAN_RUNTIME_IEEE_OVERFLOW | _FORTRAN_RUNTIME_IEEE_UNDERFLOW |
_FORTRAN_RUNTIME_IEEE_INEXACT};
if (except == 0 || except != (except & mask)) {
terminator.Crash("Invalid exception value: %d", except);
}
// Fortran and fenv.h values are identical; return the value.
if constexpr (_FORTRAN_RUNTIME_IEEE_INVALID == FE_INVALID &&
_FORTRAN_RUNTIME_IEEE_DENORM == __FE_DENORM &&
_FORTRAN_RUNTIME_IEEE_DIVIDE_BY_ZERO == FE_DIVBYZERO &&
_FORTRAN_RUNTIME_IEEE_OVERFLOW == FE_OVERFLOW &&
_FORTRAN_RUNTIME_IEEE_UNDERFLOW == FE_UNDERFLOW &&
_FORTRAN_RUNTIME_IEEE_INEXACT == FE_INEXACT) {
return except;
}
// fenv.h calls that take exception arguments are able to process multiple
// exceptions in one call, such as FE_OVERFLOW | FE_DIVBYZERO | FE_INVALID.
// And intrinsic module procedures that manage exceptions are elemental
// procedures that may specify multiple exceptions, such as ieee_all.
// However, general elemental call processing places single scalar arguments
// in a loop. As a consequence, argument 'except' here will be a power of
// two, corresponding to a single exception. If code generation were
// modified to bypass normal elemental call processing for calls with
// ieee_usual, ieee_all, or user-specified array arguments, this switch
// could be extended to support that.
// Fortran and fenv.h values differ.
switch (except) {
case _FORTRAN_RUNTIME_IEEE_INVALID:
return FE_INVALID;
case _FORTRAN_RUNTIME_IEEE_DENORM:
if (__FE_DENORM) {
return __FE_DENORM;
}
break;
case _FORTRAN_RUNTIME_IEEE_DIVIDE_BY_ZERO:
return FE_DIVBYZERO;
case _FORTRAN_RUNTIME_IEEE_OVERFLOW:
return FE_OVERFLOW;
case _FORTRAN_RUNTIME_IEEE_UNDERFLOW:
return FE_UNDERFLOW;
case _FORTRAN_RUNTIME_IEEE_INEXACT:
return FE_INEXACT;
}
terminator.Crash("Invalid exception set: %d", except);
}
// Verify that the size of ieee_modes_type and ieee_status_type objects from
// intrinsic module file __fortran_ieee_exceptions.f90 are large enough to
// hold fenv_t object.
// TODO: fenv_t can be way larger than
// sizeof(int) * _FORTRAN_RUNTIME_IEEE_FENV_T_EXTENT
// on some systems, e.g. Solaris, so omit object size comparison for now.
// TODO: consider femode_t object size comparison once its more mature.
} // extern "C"
} // namespace Fortran::runtime