| 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(S0: "" , S1: "ieee" , Value: DenormalMode::IEEE) | 
| 192 |       .Case(S: "preserve-sign" , Value: DenormalMode::PreserveSign) | 
| 193 |       .Case(S: "positive-zero" , Value: DenormalMode::PositiveZero) | 
| 194 |       .Case(S: "dynamic" , Value: DenormalMode::Dynamic) | 
| 195 |       .Default(Value: 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(args&: OutputStr, args&: InputStr) = Str.split(Separator: ','); | 
| 219 |  | 
| 220 |   DenormalMode Mode; | 
| 221 |   Mode.Output = parseDenormalFPAttributeComponent(Str: 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(Str: InputStr); | 
| 227 |  | 
| 228 |   return Mode; | 
| 229 | } | 
| 230 |  | 
| 231 | void DenormalMode::print(raw_ostream &OS) const { | 
| 232 |   OS << denormalModeKindName(Mode: Output) << ',' << denormalModeKindName(Mode: 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 |  |