1 //===- llvm/Support/FloatingPointMode.h -------------------------*- C++ -*-===//
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 /// \file
10 /// Utilities for dealing with flags related to floating point properties and
11 /// mode controls.
12 ///
13 //===----------------------------------------------------------------------===/
14
15 #ifndef LLVM_ADT_FLOATINGPOINTMODE_H
16 #define LLVM_ADT_FLOATINGPOINTMODE_H
17
18 #include "llvm/ADT/BitmaskEnum.h"
19 #include "llvm/ADT/StringSwitch.h"
20 #include "llvm/Support/Compiler.h"
21 #include "llvm/Support/raw_ostream.h"
22
23 namespace llvm {
24
25 /// Rounding mode.
26 ///
27 /// Enumerates supported rounding modes, as well as some special values. The set
28 /// of the modes must agree with IEEE-754, 4.3.1 and 4.3.2. The constants
29 /// assigned to the IEEE rounding modes must agree with the values used by
30 /// FLT_ROUNDS (C11, 5.2.4.2.2p8).
31 ///
32 /// This value is packed into bitfield in some cases, including \c FPOptions, so
33 /// the rounding mode values and the special value \c Dynamic must fit into the
34 /// the bit field (now - 3 bits). The value \c Invalid is used only in values
35 /// returned by intrinsics to indicate errors, it should never be stored as
36 /// rounding mode value, so it does not need to fit the bit fields.
37 ///
38 enum class RoundingMode : int8_t {
39 // Rounding mode defined in IEEE-754.
40 TowardZero = 0, ///< roundTowardZero.
41 NearestTiesToEven = 1, ///< roundTiesToEven.
42 TowardPositive = 2, ///< roundTowardPositive.
43 TowardNegative = 3, ///< roundTowardNegative.
44 NearestTiesToAway = 4, ///< roundTiesToAway.
45
46 // Special values.
47 Dynamic = 7, ///< Denotes mode unknown at compile time.
48 Invalid = -1 ///< Denotes invalid value.
49 };
50
51 /// Returns text representation of the given rounding mode.
spell(RoundingMode RM)52 inline StringRef spell(RoundingMode RM) {
53 switch (RM) {
54 case RoundingMode::TowardZero: return "towardzero";
55 case RoundingMode::NearestTiesToEven: return "tonearest";
56 case RoundingMode::TowardPositive: return "upward";
57 case RoundingMode::TowardNegative: return "downward";
58 case RoundingMode::NearestTiesToAway: return "tonearestaway";
59 case RoundingMode::Dynamic: return "dynamic";
60 default: return "invalid";
61 }
62 }
63
64 inline raw_ostream &operator << (raw_ostream &OS, RoundingMode RM) {
65 OS << spell(RM);
66 return OS;
67 }
68
69 /// Represent subnormal handling kind for floating point instruction inputs and
70 /// outputs.
71 struct DenormalMode {
72 /// Represent handled modes for denormal (aka subnormal) modes in the floating
73 /// point environment.
74 enum DenormalModeKind : int8_t {
75 Invalid = -1,
76
77 /// IEEE-754 denormal numbers preserved.
78 IEEE,
79
80 /// The sign of a flushed-to-zero number is preserved in the sign of 0
81 PreserveSign,
82
83 /// Denormals are flushed to positive zero.
84 PositiveZero,
85
86 /// Denormals have unknown treatment.
87 Dynamic
88 };
89
90 /// Denormal flushing mode for floating point instruction results in the
91 /// default floating point environment.
92 DenormalModeKind Output = DenormalModeKind::Invalid;
93
94 /// Denormal treatment kind for floating point instruction inputs in the
95 /// default floating-point environment. If this is not DenormalModeKind::IEEE,
96 /// floating-point instructions implicitly treat the input value as 0.
97 DenormalModeKind Input = DenormalModeKind::Invalid;
98
99 constexpr DenormalMode() = default;
100 constexpr DenormalMode(const DenormalMode &) = default;
DenormalModeDenormalMode101 constexpr DenormalMode(DenormalModeKind Out, DenormalModeKind In) :
102 Output(Out), Input(In) {}
103
104 DenormalMode &operator=(const DenormalMode &) = default;
105
getInvalidDenormalMode106 static constexpr DenormalMode getInvalid() {
107 return DenormalMode(DenormalModeKind::Invalid, DenormalModeKind::Invalid);
108 }
109
110 /// Return the assumed default mode for a function without denormal-fp-math.
getDefaultDenormalMode111 static constexpr DenormalMode getDefault() {
112 return getIEEE();
113 }
114
getIEEEDenormalMode115 static constexpr DenormalMode getIEEE() {
116 return DenormalMode(DenormalModeKind::IEEE, DenormalModeKind::IEEE);
117 }
118
getPreserveSignDenormalMode119 static constexpr DenormalMode getPreserveSign() {
120 return DenormalMode(DenormalModeKind::PreserveSign,
121 DenormalModeKind::PreserveSign);
122 }
123
getPositiveZeroDenormalMode124 static constexpr DenormalMode getPositiveZero() {
125 return DenormalMode(DenormalModeKind::PositiveZero,
126 DenormalModeKind::PositiveZero);
127 }
128
getDynamicDenormalMode129 static constexpr DenormalMode getDynamic() {
130 return DenormalMode(DenormalModeKind::Dynamic, DenormalModeKind::Dynamic);
131 }
132
133 bool operator==(DenormalMode Other) const {
134 return Output == Other.Output && Input == Other.Input;
135 }
136
137 bool operator!=(DenormalMode Other) const {
138 return !(*this == Other);
139 }
140
isSimpleDenormalMode141 bool isSimple() const {
142 return Input == Output;
143 }
144
isValidDenormalMode145 bool isValid() const {
146 return Output != DenormalModeKind::Invalid &&
147 Input != DenormalModeKind::Invalid;
148 }
149
150 /// Return true if input denormals must be implicitly treated as 0.
inputsAreZeroDenormalMode151 constexpr bool inputsAreZero() const {
152 return Input == DenormalModeKind::PreserveSign ||
153 Input == DenormalModeKind::PositiveZero;
154 }
155
156 /// Return true if output denormals should be flushed to 0.
outputsAreZeroDenormalMode157 constexpr bool outputsAreZero() const {
158 return Output == DenormalModeKind::PreserveSign ||
159 Output == DenormalModeKind::PositiveZero;
160 }
161
162 /// Get the effective denormal mode if the mode if this caller calls into a
163 /// function with \p Callee. This promotes dynamic modes to the mode of the
164 /// caller.
mergeCalleeModeDenormalMode165 DenormalMode mergeCalleeMode(DenormalMode Callee) const {
166 DenormalMode MergedMode = Callee;
167 if (Callee.Input == DenormalMode::Dynamic)
168 MergedMode.Input = Input;
169 if (Callee.Output == DenormalMode::Dynamic)
170 MergedMode.Output = Output;
171 return MergedMode;
172 }
173
174 inline void print(raw_ostream &OS) const;
175
strDenormalMode176 inline std::string str() const {
177 std::string storage;
178 raw_string_ostream OS(storage);
179 print(OS);
180 return storage;
181 }
182 };
183
184 inline raw_ostream& operator<<(raw_ostream &OS, DenormalMode Mode) {
185 Mode.print(OS);
186 return OS;
187 }
188
189 /// Parse the expected names from the denormal-fp-math attribute.
190 inline DenormalMode::DenormalModeKind
parseDenormalFPAttributeComponent(StringRef Str)191 parseDenormalFPAttributeComponent(StringRef Str) {
192 // Assume ieee on unspecified attribute.
193 return StringSwitch<DenormalMode::DenormalModeKind>(Str)
194 .Cases("", "ieee", DenormalMode::IEEE)
195 .Case("preserve-sign", DenormalMode::PreserveSign)
196 .Case("positive-zero", DenormalMode::PositiveZero)
197 .Case("dynamic", DenormalMode::Dynamic)
198 .Default(DenormalMode::Invalid);
199 }
200
201 /// Return the name used for the denormal handling mode used by the
202 /// expected names from the denormal-fp-math attribute.
denormalModeKindName(DenormalMode::DenormalModeKind Mode)203 inline StringRef denormalModeKindName(DenormalMode::DenormalModeKind Mode) {
204 switch (Mode) {
205 case DenormalMode::IEEE:
206 return "ieee";
207 case DenormalMode::PreserveSign:
208 return "preserve-sign";
209 case DenormalMode::PositiveZero:
210 return "positive-zero";
211 case DenormalMode::Dynamic:
212 return "dynamic";
213 default:
214 return "";
215 }
216 }
217
218 /// Returns the denormal mode to use for inputs and outputs.
parseDenormalFPAttribute(StringRef Str)219 inline DenormalMode parseDenormalFPAttribute(StringRef Str) {
220 StringRef OutputStr, InputStr;
221 std::tie(OutputStr, InputStr) = Str.split(',');
222
223 DenormalMode Mode;
224 Mode.Output = parseDenormalFPAttributeComponent(OutputStr);
225
226 // Maintain compatibility with old form of the attribute which only specified
227 // one component.
228 Mode.Input = InputStr.empty() ? Mode.Output :
229 parseDenormalFPAttributeComponent(InputStr);
230
231 return Mode;
232 }
233
print(raw_ostream & OS)234 void DenormalMode::print(raw_ostream &OS) const {
235 OS << denormalModeKindName(Output) << ',' << denormalModeKindName(Input);
236 }
237
238 /// Floating-point class tests, supported by 'is_fpclass' intrinsic. Actual
239 /// test may be an OR combination of basic tests.
240 enum FPClassTest : unsigned {
241 fcNone = 0,
242
243 fcSNan = 0x0001,
244 fcQNan = 0x0002,
245 fcNegInf = 0x0004,
246 fcNegNormal = 0x0008,
247 fcNegSubnormal = 0x0010,
248 fcNegZero = 0x0020,
249 fcPosZero = 0x0040,
250 fcPosSubnormal = 0x0080,
251 fcPosNormal = 0x0100,
252 fcPosInf = 0x0200,
253
254 fcNan = fcSNan | fcQNan,
255 fcInf = fcPosInf | fcNegInf,
256 fcNormal = fcPosNormal | fcNegNormal,
257 fcSubnormal = fcPosSubnormal | fcNegSubnormal,
258 fcZero = fcPosZero | fcNegZero,
259 fcPosFinite = fcPosNormal | fcPosSubnormal | fcPosZero,
260 fcNegFinite = fcNegNormal | fcNegSubnormal | fcNegZero,
261 fcFinite = fcPosFinite | fcNegFinite,
262 fcPositive = fcPosFinite | fcPosInf,
263 fcNegative = fcNegFinite | fcNegInf,
264
265 fcAllFlags = fcNan | fcInf | fcFinite,
266 };
267
268 LLVM_DECLARE_ENUM_AS_BITMASK(FPClassTest, /* LargestValue */ fcPosInf);
269
270 /// Return the test mask which returns true if the value's sign bit is flipped.
271 LLVM_ABI FPClassTest fneg(FPClassTest Mask);
272
273 /// Return the test mask which returns true after fabs is applied to the value.
274 LLVM_ABI FPClassTest inverse_fabs(FPClassTest Mask);
275
276 /// Return the test mask which returns true if the value could have the same set
277 /// of classes, but with a different sign.
278 LLVM_ABI FPClassTest unknown_sign(FPClassTest Mask);
279
280 /// Write a human readable form of \p Mask to \p OS
281 LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, FPClassTest Mask);
282
283 } // namespace llvm
284
285 #endif // LLVM_ADT_FLOATINGPOINTMODE_H
286