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