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