xref: /freebsd/contrib/llvm-project/llvm/include/llvm/ADT/FloatingPointMode.h (revision 700637cbb5e582861067a11aaca4d053546871d2)
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