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