host.cpp
4.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
//===-- lib/Evaluate/host.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
//
//===----------------------------------------------------------------------===//
#include "host.h"
#include "flang/Common/idioms.h"
#include "llvm/Support/Errno.h"
#include <cfenv>
#if __x86_64__
#include <xmmintrin.h>
#endif
namespace Fortran::evaluate::host {
using namespace Fortran::parser::literals;
void HostFloatingPointEnvironment::SetUpHostFloatingPointEnvironment(
FoldingContext &context) {
errno = 0;
std::fenv_t currentFenv;
if (feholdexcept(&originalFenv_) != 0) {
common::die("Folding with host runtime: feholdexcept() failed: %s",
llvm::sys::StrError(errno).c_str());
return;
}
if (fegetenv(¤tFenv) != 0) {
common::die("Folding with host runtime: fegetenv() failed: %s",
llvm::sys::StrError(errno).c_str());
return;
}
#if __x86_64__
hasSubnormalFlushingHardwareControl_ = true;
originalMxcsr = _mm_getcsr();
unsigned int currentMxcsr{originalMxcsr};
if (context.flushSubnormalsToZero()) {
currentMxcsr |= 0x8000;
currentMxcsr |= 0x0040;
} else {
currentMxcsr &= ~0x8000;
currentMxcsr &= ~0x0040;
}
#elif defined(__aarch64__)
#if defined(__GNU_LIBRARY__)
hasSubnormalFlushingHardwareControl_ = true;
if (context.flushSubnormalsToZero()) {
currentFenv.__fpcr |= (1U << 24); // control register
} else {
currentFenv.__fpcr &= ~(1U << 24); // control register
}
#elif defined(__BIONIC__)
hasSubnormalFlushingHardwareControl_ = true;
if (context.flushSubnormalsToZero()) {
currentFenv.__control |= (1U << 24); // control register
} else {
currentFenv.__control &= ~(1U << 24); // control register
}
#else
// If F18 is built with other C libraries on AArch64, software flushing will
// be performed around host library calls if subnormal flushing is requested
#endif
#else
// If F18 is not built on one of the above host architecture, software
// flushing will be performed around host library calls if needed.
#endif
#ifdef __clang__
// clang does not ensure that floating point environment flags are meaningful.
// It may perform optimizations that will impact the floating point
// environment. For instance, libc++ complex float tan and tanh compilation
// with clang -O2 introduces a division by zero on X86 in unused slots of xmm
// registers. Therefore, fetestexcept should not be used.
hardwareFlagsAreReliable_ = false;
#endif
errno = 0;
if (fesetenv(¤tFenv) != 0) {
common::die("Folding with host runtime: fesetenv() failed: %s",
llvm::sys::StrError(errno).c_str());
return;
}
#if __x86_64__
_mm_setcsr(currentMxcsr);
#endif
switch (context.rounding().mode) {
case common::RoundingMode::TiesToEven:
fesetround(FE_TONEAREST);
break;
case common::RoundingMode::ToZero:
fesetround(FE_TOWARDZERO);
break;
case common::RoundingMode::Up:
fesetround(FE_UPWARD);
break;
case common::RoundingMode::Down:
fesetround(FE_DOWNWARD);
break;
case common::RoundingMode::TiesAwayFromZero:
fesetround(FE_TONEAREST);
context.messages().Say(
"TiesAwayFromZero rounding mode is not available when folding constants"
" with host runtime; using TiesToEven instead"_en_US);
break;
}
flags_.clear();
errno = 0;
}
void HostFloatingPointEnvironment::CheckAndRestoreFloatingPointEnvironment(
FoldingContext &context) {
int errnoCapture{errno};
if (hardwareFlagsAreReliable()) {
int exceptions{fetestexcept(FE_ALL_EXCEPT)};
if (exceptions & FE_INVALID) {
flags_.set(RealFlag::InvalidArgument);
}
if (exceptions & FE_DIVBYZERO) {
flags_.set(RealFlag::DivideByZero);
}
if (exceptions & FE_OVERFLOW) {
flags_.set(RealFlag::Overflow);
}
if (exceptions & FE_UNDERFLOW) {
flags_.set(RealFlag::Underflow);
}
if (exceptions & FE_INEXACT) {
flags_.set(RealFlag::Inexact);
}
}
if (flags_.empty()) {
if (errnoCapture == EDOM) {
flags_.set(RealFlag::InvalidArgument);
}
if (errnoCapture == ERANGE) {
// can't distinguish over/underflow from errno
flags_.set(RealFlag::Overflow);
}
}
if (!flags_.empty()) {
RealFlagWarnings(context, flags_, "intrinsic function");
}
errno = 0;
if (fesetenv(&originalFenv_) != 0) {
std::fprintf(
stderr, "fesetenv() failed: %s\n", llvm::sys::StrError(errno).c_str());
common::die(
"Folding with host runtime: fesetenv() failed while restoring fenv: %s",
llvm::sys::StrError(errno).c_str());
}
#if __x86_64__
_mm_setcsr(originalMxcsr);
#endif
errno = 0;
}
} // namespace Fortran::evaluate::host