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(const DenormalMode &) = default; 100 constexpr DenormalMode(DenormalModeKind Out, DenormalModeKind In) : 101 Output(Out), Input(In) {} 102 103 DenormalMode &operator=(const DenormalMode &) = default; 104 105 static constexpr DenormalMode getInvalid() { 106 return DenormalMode(DenormalModeKind::Invalid, DenormalModeKind::Invalid); 107 } 108 109 /// Return the assumed default mode for a function without denormal-fp-math. 110 static constexpr DenormalMode getDefault() { 111 return getIEEE(); 112 } 113 114 static constexpr DenormalMode getIEEE() { 115 return DenormalMode(DenormalModeKind::IEEE, DenormalModeKind::IEEE); 116 } 117 118 static constexpr DenormalMode getPreserveSign() { 119 return DenormalMode(DenormalModeKind::PreserveSign, 120 DenormalModeKind::PreserveSign); 121 } 122 123 static constexpr DenormalMode getPositiveZero() { 124 return DenormalMode(DenormalModeKind::PositiveZero, 125 DenormalModeKind::PositiveZero); 126 } 127 128 static constexpr DenormalMode getDynamic() { 129 return DenormalMode(DenormalModeKind::Dynamic, DenormalModeKind::Dynamic); 130 } 131 132 bool operator==(DenormalMode Other) const { 133 return Output == Other.Output && Input == Other.Input; 134 } 135 136 bool operator!=(DenormalMode Other) const { 137 return !(*this == Other); 138 } 139 140 bool isSimple() const { 141 return Input == Output; 142 } 143 144 bool isValid() const { 145 return Output != DenormalModeKind::Invalid && 146 Input != DenormalModeKind::Invalid; 147 } 148 149 /// Return true if input denormals must be implicitly treated as 0. 150 constexpr bool inputsAreZero() const { 151 return Input == DenormalModeKind::PreserveSign || 152 Input == DenormalModeKind::PositiveZero; 153 } 154 155 /// Return true if output denormals should be flushed to 0. 156 constexpr bool outputsAreZero() const { 157 return Output == DenormalModeKind::PreserveSign || 158 Output == DenormalModeKind::PositiveZero; 159 } 160 161 /// Get the effective denormal mode if the mode if this caller calls into a 162 /// function with \p Callee. This promotes dynamic modes to the mode of the 163 /// caller. 164 DenormalMode mergeCalleeMode(DenormalMode Callee) const { 165 DenormalMode MergedMode = Callee; 166 if (Callee.Input == DenormalMode::Dynamic) 167 MergedMode.Input = Input; 168 if (Callee.Output == DenormalMode::Dynamic) 169 MergedMode.Output = Output; 170 return MergedMode; 171 } 172 173 inline void print(raw_ostream &OS) const; 174 175 inline std::string str() const { 176 std::string storage; 177 raw_string_ostream OS(storage); 178 print(OS); 179 return storage; 180 } 181 }; 182 183 inline raw_ostream& operator<<(raw_ostream &OS, DenormalMode Mode) { 184 Mode.print(OS); 185 return OS; 186 } 187 188 /// Parse the expected names from the denormal-fp-math attribute. 189 inline DenormalMode::DenormalModeKind 190 parseDenormalFPAttributeComponent(StringRef Str) { 191 // Assume ieee on unspecified attribute. 192 return StringSwitch<DenormalMode::DenormalModeKind>(Str) 193 .Cases("", "ieee", DenormalMode::IEEE) 194 .Case("preserve-sign", DenormalMode::PreserveSign) 195 .Case("positive-zero", DenormalMode::PositiveZero) 196 .Case("dynamic", DenormalMode::Dynamic) 197 .Default(DenormalMode::Invalid); 198 } 199 200 /// Return the name used for the denormal handling mode used by the 201 /// expected names from the denormal-fp-math attribute. 202 inline StringRef denormalModeKindName(DenormalMode::DenormalModeKind Mode) { 203 switch (Mode) { 204 case DenormalMode::IEEE: 205 return "ieee"; 206 case DenormalMode::PreserveSign: 207 return "preserve-sign"; 208 case DenormalMode::PositiveZero: 209 return "positive-zero"; 210 case DenormalMode::Dynamic: 211 return "dynamic"; 212 default: 213 return ""; 214 } 215 } 216 217 /// Returns the denormal mode to use for inputs and outputs. 218 inline DenormalMode parseDenormalFPAttribute(StringRef Str) { 219 StringRef OutputStr, InputStr; 220 std::tie(OutputStr, InputStr) = Str.split(','); 221 222 DenormalMode Mode; 223 Mode.Output = parseDenormalFPAttributeComponent(OutputStr); 224 225 // Maintain compatibility with old form of the attribute which only specified 226 // one component. 227 Mode.Input = InputStr.empty() ? Mode.Output : 228 parseDenormalFPAttributeComponent(InputStr); 229 230 return Mode; 231 } 232 233 void DenormalMode::print(raw_ostream &OS) const { 234 OS << denormalModeKindName(Output) << ',' << denormalModeKindName(Input); 235 } 236 237 /// Floating-point class tests, supported by 'is_fpclass' intrinsic. Actual 238 /// test may be an OR combination of basic tests. 239 enum FPClassTest : unsigned { 240 fcNone = 0, 241 242 fcSNan = 0x0001, 243 fcQNan = 0x0002, 244 fcNegInf = 0x0004, 245 fcNegNormal = 0x0008, 246 fcNegSubnormal = 0x0010, 247 fcNegZero = 0x0020, 248 fcPosZero = 0x0040, 249 fcPosSubnormal = 0x0080, 250 fcPosNormal = 0x0100, 251 fcPosInf = 0x0200, 252 253 fcNan = fcSNan | fcQNan, 254 fcInf = fcPosInf | fcNegInf, 255 fcNormal = fcPosNormal | fcNegNormal, 256 fcSubnormal = fcPosSubnormal | fcNegSubnormal, 257 fcZero = fcPosZero | fcNegZero, 258 fcPosFinite = fcPosNormal | fcPosSubnormal | fcPosZero, 259 fcNegFinite = fcNegNormal | fcNegSubnormal | fcNegZero, 260 fcFinite = fcPosFinite | fcNegFinite, 261 fcPositive = fcPosFinite | fcPosInf, 262 fcNegative = fcNegFinite | fcNegInf, 263 264 fcAllFlags = fcNan | fcInf | fcFinite, 265 }; 266 267 LLVM_DECLARE_ENUM_AS_BITMASK(FPClassTest, /* LargestValue */ fcPosInf); 268 269 /// Return the test mask which returns true if the value's sign bit is flipped. 270 FPClassTest fneg(FPClassTest Mask); 271 272 /// Return the test mask which returns true after fabs is applied to the value. 273 FPClassTest inverse_fabs(FPClassTest Mask); 274 275 /// Return the test mask which returns true if the value could have the same set 276 /// of classes, but with a different sign. 277 FPClassTest unknown_sign(FPClassTest Mask); 278 279 /// Write a human readable form of \p Mask to \p OS 280 raw_ostream &operator<<(raw_ostream &OS, FPClassTest Mask); 281 282 } // namespace llvm 283 284 #endif // LLVM_ADT_FLOATINGPOINTMODE_H 285