Files
clang-p2996/flang/runtime/exceptions.cpp
vdonaldson ff862d6de9 [flang] Modifications to ieee floating point environment procedures (#121949)
Intrinsic module procedures ieee_get_modes, ieee_set_modes,
ieee_get_status, and ieee_set_status store and retrieve opaque data
values whose size varies by machine and OS environment. These data
values are usually, but not always small. Their sizes are not directly
known in a cross compilation environment. Address this issue by
implementing two mechanisms for processing these data values.
Environments that use typical small data sizes can access storage
defined at compile time. When this is not valid, data storage of any
size can be allocated at runtime.
2025-01-15 10:55:09 -05:00

123 lines
3.4 KiB
C++

//===-- 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
//
//===----------------------------------------------------------------------===//
// Runtime exception support.
#include "flang/Runtime/exceptions.h"
#include "terminator.h"
#include <cfenv>
#if __x86_64__
#include <xmmintrin.h>
#endif
// fenv.h may not define exception macros.
#ifndef FE_INVALID
#define FE_INVALID 0
#endif
#ifndef FE_DIVBYZERO
#define FE_DIVBYZERO 0
#endif
#ifndef FE_OVERFLOW
#define FE_OVERFLOW 0
#endif
#ifndef FE_UNDERFLOW
#define FE_UNDERFLOW 0
#endif
#ifndef FE_INEXACT
#define FE_INEXACT 0
#endif
namespace Fortran::runtime {
extern "C" {
// Map a set of Fortran ieee_arithmetic module exceptions to a libm fenv.h
// excepts value.
uint32_t RTNAME(MapException)(uint32_t excepts) {
Terminator terminator{__FILE__, __LINE__};
static constexpr uint32_t v{FE_INVALID};
#if __x86_64__
static constexpr uint32_t s{__FE_DENORM}; // nonstandard, not a #define
#else
static constexpr uint32_t s{0};
#endif
static constexpr uint32_t z{FE_DIVBYZERO};
static constexpr uint32_t o{FE_OVERFLOW};
static constexpr uint32_t u{FE_UNDERFLOW};
static constexpr uint32_t x{FE_INEXACT};
#define vm(p) p, p | v
#define sm(p) vm(p), vm(p | s)
#define zm(p) sm(p), sm(p | z)
#define om(p) zm(p), zm(p | o)
#define um(p) om(p), om(p | u)
#define xm um(0), um(x)
static constexpr uint32_t map[]{xm};
static constexpr uint32_t mapSize{sizeof(map) / sizeof(uint32_t)};
static_assert(mapSize == 64);
if (excepts >= mapSize) {
terminator.Crash("Invalid excepts value: %d", excepts);
}
uint32_t except_value = map[excepts];
return except_value;
}
// Check if the processor has the ability to control whether to halt or
// continue execution when a given exception is raised.
bool RTNAME(SupportHalting)([[maybe_unused]] uint32_t except) {
#ifdef __USE_GNU
except = RTNAME(MapException)(except);
int currentSet = fegetexcept(), flipSet, ok;
if (currentSet & except) {
ok = fedisableexcept(except);
flipSet = fegetexcept();
ok |= feenableexcept(except);
} else {
ok = feenableexcept(except);
flipSet = fegetexcept();
ok |= fedisableexcept(except);
}
return ok != -1 && currentSet != flipSet;
#else
return false;
#endif
}
bool RTNAME(GetUnderflowMode)(void) {
#if _MM_FLUSH_ZERO_MASK
// The MXCSR Flush to Zero flag is the negation of the ieee_get_underflow_mode
// GRADUAL argument. It affects real computations of kinds 3, 4, and 8.
return _MM_GET_FLUSH_ZERO_MODE() == _MM_FLUSH_ZERO_OFF;
#else
return false;
#endif
}
void RTNAME(SetUnderflowMode)(bool flag) {
#if _MM_FLUSH_ZERO_MASK
// The MXCSR Flush to Zero flag is the negation of the ieee_set_underflow_mode
// GRADUAL argument. It affects real computations of kinds 3, 4, and 8.
_MM_SET_FLUSH_ZERO_MODE(flag ? _MM_FLUSH_ZERO_OFF : _MM_FLUSH_ZERO_ON);
#endif
}
size_t RTNAME(GetModesTypeSize)(void) {
#ifdef __GLIBC_USE_IEC_60559_BFP_EXT
return sizeof(femode_t); // byte size of ieee_modes_type data
#else
return 8; // femode_t is not defined
#endif
}
size_t RTNAME(GetStatusTypeSize)(void) {
return sizeof(fenv_t); // byte size of ieee_status_type data
}
} // extern "C"
} // namespace Fortran::runtime