1480093f4SDimitry Andric //===- llvm/Support/FloatingPointMode.h -------------------------*- C++ -*-===//
2480093f4SDimitry Andric //
3480093f4SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4480093f4SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5480093f4SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6480093f4SDimitry Andric //
7480093f4SDimitry Andric //===----------------------------------------------------------------------===//
81fd87a68SDimitry Andric ///
91fd87a68SDimitry Andric /// \file
1081ad6265SDimitry Andric /// Utilities for dealing with flags related to floating point properties and
1181ad6265SDimitry Andric /// mode controls.
121fd87a68SDimitry Andric ///
13480093f4SDimitry Andric //===----------------------------------------------------------------------===/
14480093f4SDimitry Andric 
15fe6060f1SDimitry Andric #ifndef LLVM_ADT_FLOATINGPOINTMODE_H
16fe6060f1SDimitry Andric #define LLVM_ADT_FLOATINGPOINTMODE_H
17480093f4SDimitry Andric 
1806c3fb27SDimitry Andric #include "llvm/ADT/BitmaskEnum.h"
19480093f4SDimitry Andric #include "llvm/ADT/StringSwitch.h"
205ffd83dbSDimitry Andric #include "llvm/Support/raw_ostream.h"
21480093f4SDimitry Andric 
22480093f4SDimitry Andric namespace llvm {
23480093f4SDimitry Andric 
245ffd83dbSDimitry Andric /// Rounding mode.
255ffd83dbSDimitry Andric ///
265ffd83dbSDimitry Andric /// Enumerates supported rounding modes, as well as some special values. The set
275ffd83dbSDimitry Andric /// of the modes must agree with IEEE-754, 4.3.1 and 4.3.2. The constants
285ffd83dbSDimitry Andric /// assigned to the IEEE rounding modes must agree with the values used by
295ffd83dbSDimitry Andric /// FLT_ROUNDS (C11, 5.2.4.2.2p8).
305ffd83dbSDimitry Andric ///
315ffd83dbSDimitry Andric /// This value is packed into bitfield in some cases, including \c FPOptions, so
325ffd83dbSDimitry Andric /// the rounding mode values and the special value \c Dynamic must fit into the
335ffd83dbSDimitry Andric /// the bit field (now - 3 bits). The value \c Invalid is used only in values
345ffd83dbSDimitry Andric /// returned by intrinsics to indicate errors, it should never be stored as
355ffd83dbSDimitry Andric /// rounding mode value, so it does not need to fit the bit fields.
365ffd83dbSDimitry Andric ///
375ffd83dbSDimitry Andric enum class RoundingMode : int8_t {
385ffd83dbSDimitry Andric   // Rounding mode defined in IEEE-754.
395ffd83dbSDimitry Andric   TowardZero        = 0,    ///< roundTowardZero.
405ffd83dbSDimitry Andric   NearestTiesToEven = 1,    ///< roundTiesToEven.
415ffd83dbSDimitry Andric   TowardPositive    = 2,    ///< roundTowardPositive.
425ffd83dbSDimitry Andric   TowardNegative    = 3,    ///< roundTowardNegative.
435ffd83dbSDimitry Andric   NearestTiesToAway = 4,    ///< roundTiesToAway.
445ffd83dbSDimitry Andric 
455ffd83dbSDimitry Andric   // Special values.
465ffd83dbSDimitry Andric   Dynamic = 7,    ///< Denotes mode unknown at compile time.
475ffd83dbSDimitry Andric   Invalid = -1    ///< Denotes invalid value.
485ffd83dbSDimitry Andric };
495ffd83dbSDimitry Andric 
50e8d8bef9SDimitry Andric /// Returns text representation of the given rounding mode.
spell(RoundingMode RM)51e8d8bef9SDimitry Andric inline StringRef spell(RoundingMode RM) {
52e8d8bef9SDimitry Andric   switch (RM) {
53e8d8bef9SDimitry Andric   case RoundingMode::TowardZero: return "towardzero";
54e8d8bef9SDimitry Andric   case RoundingMode::NearestTiesToEven: return "tonearest";
55e8d8bef9SDimitry Andric   case RoundingMode::TowardPositive: return "upward";
56e8d8bef9SDimitry Andric   case RoundingMode::TowardNegative: return "downward";
57e8d8bef9SDimitry Andric   case RoundingMode::NearestTiesToAway: return "tonearestaway";
58e8d8bef9SDimitry Andric   case RoundingMode::Dynamic: return "dynamic";
59e8d8bef9SDimitry Andric   default: return "invalid";
60e8d8bef9SDimitry Andric   }
61e8d8bef9SDimitry Andric }
62e8d8bef9SDimitry Andric 
63e8d8bef9SDimitry Andric inline raw_ostream &operator << (raw_ostream &OS, RoundingMode RM) {
64e8d8bef9SDimitry Andric   OS << spell(RM);
65e8d8bef9SDimitry Andric   return OS;
66e8d8bef9SDimitry Andric }
67e8d8bef9SDimitry Andric 
685ffd83dbSDimitry Andric /// Represent subnormal handling kind for floating point instruction inputs and
695ffd83dbSDimitry Andric /// outputs.
705ffd83dbSDimitry Andric struct DenormalMode {
71480093f4SDimitry Andric   /// Represent handled modes for denormal (aka subnormal) modes in the floating
72480093f4SDimitry Andric   /// point environment.
735ffd83dbSDimitry Andric   enum DenormalModeKind : int8_t {
74480093f4SDimitry Andric     Invalid = -1,
75480093f4SDimitry Andric 
76480093f4SDimitry Andric     /// IEEE-754 denormal numbers preserved.
77480093f4SDimitry Andric     IEEE,
78480093f4SDimitry Andric 
79480093f4SDimitry Andric     /// The sign of a flushed-to-zero number is preserved in the sign of 0
80480093f4SDimitry Andric     PreserveSign,
81480093f4SDimitry Andric 
82480093f4SDimitry Andric     /// Denormals are flushed to positive zero.
8306c3fb27SDimitry Andric     PositiveZero,
8406c3fb27SDimitry Andric 
8506c3fb27SDimitry Andric     /// Denormals have unknown treatment.
8606c3fb27SDimitry Andric     Dynamic
87480093f4SDimitry Andric   };
88480093f4SDimitry Andric 
895ffd83dbSDimitry Andric   /// Denormal flushing mode for floating point instruction results in the
905ffd83dbSDimitry Andric   /// default floating point environment.
915ffd83dbSDimitry Andric   DenormalModeKind Output = DenormalModeKind::Invalid;
925ffd83dbSDimitry Andric 
935ffd83dbSDimitry Andric   /// Denormal treatment kind for floating point instruction inputs in the
945ffd83dbSDimitry Andric   /// default floating-point environment. If this is not DenormalModeKind::IEEE,
955ffd83dbSDimitry Andric   /// floating-point instructions implicitly treat the input value as 0.
965ffd83dbSDimitry Andric   DenormalModeKind Input = DenormalModeKind::Invalid;
975ffd83dbSDimitry Andric 
985ffd83dbSDimitry Andric   constexpr DenormalMode() = default;
99*5f757f3fSDimitry Andric   constexpr DenormalMode(const DenormalMode &) = default;
DenormalModeDenormalMode1005ffd83dbSDimitry Andric   constexpr DenormalMode(DenormalModeKind Out, DenormalModeKind In) :
1015ffd83dbSDimitry Andric     Output(Out), Input(In) {}
1025ffd83dbSDimitry Andric 
103*5f757f3fSDimitry Andric   DenormalMode &operator=(const DenormalMode &) = default;
1045ffd83dbSDimitry Andric 
getInvalidDenormalMode1055ffd83dbSDimitry Andric   static constexpr DenormalMode getInvalid() {
1065ffd83dbSDimitry Andric     return DenormalMode(DenormalModeKind::Invalid, DenormalModeKind::Invalid);
1075ffd83dbSDimitry Andric   }
1085ffd83dbSDimitry Andric 
10906c3fb27SDimitry Andric   /// Return the assumed default mode for a function without denormal-fp-math.
getDefaultDenormalMode11006c3fb27SDimitry Andric   static constexpr DenormalMode getDefault() {
11106c3fb27SDimitry Andric     return getIEEE();
11206c3fb27SDimitry Andric   }
11306c3fb27SDimitry Andric 
getIEEEDenormalMode1145ffd83dbSDimitry Andric   static constexpr DenormalMode getIEEE() {
1155ffd83dbSDimitry Andric     return DenormalMode(DenormalModeKind::IEEE, DenormalModeKind::IEEE);
1165ffd83dbSDimitry Andric   }
1175ffd83dbSDimitry Andric 
getPreserveSignDenormalMode1185ffd83dbSDimitry Andric   static constexpr DenormalMode getPreserveSign() {
1195ffd83dbSDimitry Andric     return DenormalMode(DenormalModeKind::PreserveSign,
1205ffd83dbSDimitry Andric                         DenormalModeKind::PreserveSign);
1215ffd83dbSDimitry Andric   }
1225ffd83dbSDimitry Andric 
getPositiveZeroDenormalMode1235ffd83dbSDimitry Andric   static constexpr DenormalMode getPositiveZero() {
1245ffd83dbSDimitry Andric     return DenormalMode(DenormalModeKind::PositiveZero,
1255ffd83dbSDimitry Andric                         DenormalModeKind::PositiveZero);
1265ffd83dbSDimitry Andric   }
1275ffd83dbSDimitry Andric 
getDynamicDenormalMode12806c3fb27SDimitry Andric   static constexpr DenormalMode getDynamic() {
12906c3fb27SDimitry Andric     return DenormalMode(DenormalModeKind::Dynamic, DenormalModeKind::Dynamic);
13006c3fb27SDimitry Andric   }
13106c3fb27SDimitry Andric 
1325ffd83dbSDimitry Andric   bool operator==(DenormalMode Other) const {
1335ffd83dbSDimitry Andric     return Output == Other.Output && Input == Other.Input;
1345ffd83dbSDimitry Andric   }
1355ffd83dbSDimitry Andric 
1365ffd83dbSDimitry Andric   bool operator!=(DenormalMode Other) const {
1375ffd83dbSDimitry Andric     return !(*this == Other);
1385ffd83dbSDimitry Andric   }
1395ffd83dbSDimitry Andric 
isSimpleDenormalMode1405ffd83dbSDimitry Andric   bool isSimple() const {
1415ffd83dbSDimitry Andric     return Input == Output;
1425ffd83dbSDimitry Andric   }
1435ffd83dbSDimitry Andric 
isValidDenormalMode1445ffd83dbSDimitry Andric   bool isValid() const {
1455ffd83dbSDimitry Andric     return Output != DenormalModeKind::Invalid &&
1465ffd83dbSDimitry Andric            Input != DenormalModeKind::Invalid;
1475ffd83dbSDimitry Andric   }
1485ffd83dbSDimitry Andric 
14906c3fb27SDimitry Andric   /// Return true if input denormals must be implicitly treated as 0.
inputsAreZeroDenormalMode15006c3fb27SDimitry Andric   constexpr bool inputsAreZero() const {
15106c3fb27SDimitry Andric     return Input == DenormalModeKind::PreserveSign ||
15206c3fb27SDimitry Andric            Input == DenormalModeKind::PositiveZero;
15306c3fb27SDimitry Andric   }
15406c3fb27SDimitry Andric 
15506c3fb27SDimitry Andric   /// Return true if output denormals should be flushed to 0.
outputsAreZeroDenormalMode15606c3fb27SDimitry Andric   constexpr bool outputsAreZero() const {
15706c3fb27SDimitry Andric     return Output == DenormalModeKind::PreserveSign ||
15806c3fb27SDimitry Andric            Output == DenormalModeKind::PositiveZero;
15906c3fb27SDimitry Andric   }
16006c3fb27SDimitry Andric 
16106c3fb27SDimitry Andric   /// Get the effective denormal mode if the mode if this caller calls into a
16206c3fb27SDimitry Andric   /// function with \p Callee. This promotes dynamic modes to the mode of the
16306c3fb27SDimitry Andric   /// caller.
mergeCalleeModeDenormalMode16406c3fb27SDimitry Andric   DenormalMode mergeCalleeMode(DenormalMode Callee) const {
16506c3fb27SDimitry Andric     DenormalMode MergedMode = Callee;
16606c3fb27SDimitry Andric     if (Callee.Input == DenormalMode::Dynamic)
16706c3fb27SDimitry Andric       MergedMode.Input = Input;
16806c3fb27SDimitry Andric     if (Callee.Output == DenormalMode::Dynamic)
16906c3fb27SDimitry Andric       MergedMode.Output = Output;
17006c3fb27SDimitry Andric     return MergedMode;
17106c3fb27SDimitry Andric   }
17206c3fb27SDimitry Andric 
1735ffd83dbSDimitry Andric   inline void print(raw_ostream &OS) const;
1745ffd83dbSDimitry Andric 
strDenormalMode1755ffd83dbSDimitry Andric   inline std::string str() const {
1765ffd83dbSDimitry Andric     std::string storage;
1775ffd83dbSDimitry Andric     raw_string_ostream OS(storage);
1785ffd83dbSDimitry Andric     print(OS);
1795ffd83dbSDimitry Andric     return OS.str();
1805ffd83dbSDimitry Andric   }
1815ffd83dbSDimitry Andric };
1825ffd83dbSDimitry Andric 
1835ffd83dbSDimitry Andric inline raw_ostream& operator<<(raw_ostream &OS, DenormalMode Mode) {
1845ffd83dbSDimitry Andric   Mode.print(OS);
1855ffd83dbSDimitry Andric   return OS;
1865ffd83dbSDimitry Andric }
1875ffd83dbSDimitry Andric 
188480093f4SDimitry Andric /// Parse the expected names from the denormal-fp-math attribute.
1895ffd83dbSDimitry Andric inline DenormalMode::DenormalModeKind
parseDenormalFPAttributeComponent(StringRef Str)1905ffd83dbSDimitry Andric parseDenormalFPAttributeComponent(StringRef Str) {
191480093f4SDimitry Andric   // Assume ieee on unspecified attribute.
1925ffd83dbSDimitry Andric   return StringSwitch<DenormalMode::DenormalModeKind>(Str)
193480093f4SDimitry Andric       .Cases("", "ieee", DenormalMode::IEEE)
194480093f4SDimitry Andric       .Case("preserve-sign", DenormalMode::PreserveSign)
195480093f4SDimitry Andric       .Case("positive-zero", DenormalMode::PositiveZero)
19606c3fb27SDimitry Andric       .Case("dynamic", DenormalMode::Dynamic)
197480093f4SDimitry Andric       .Default(DenormalMode::Invalid);
198480093f4SDimitry Andric }
199480093f4SDimitry Andric 
200*5f757f3fSDimitry Andric /// Return the name used for the denormal handling mode used by the
201480093f4SDimitry Andric /// expected names from the denormal-fp-math attribute.
denormalModeKindName(DenormalMode::DenormalModeKind Mode)2025ffd83dbSDimitry Andric inline StringRef denormalModeKindName(DenormalMode::DenormalModeKind Mode) {
203480093f4SDimitry Andric   switch (Mode) {
204480093f4SDimitry Andric   case DenormalMode::IEEE:
205480093f4SDimitry Andric     return "ieee";
206480093f4SDimitry Andric   case DenormalMode::PreserveSign:
207480093f4SDimitry Andric     return "preserve-sign";
208480093f4SDimitry Andric   case DenormalMode::PositiveZero:
209480093f4SDimitry Andric     return "positive-zero";
21006c3fb27SDimitry Andric   case DenormalMode::Dynamic:
21106c3fb27SDimitry Andric     return "dynamic";
212480093f4SDimitry Andric   default:
213480093f4SDimitry Andric     return "";
214480093f4SDimitry Andric   }
215480093f4SDimitry Andric }
216480093f4SDimitry Andric 
2175ffd83dbSDimitry Andric /// Returns the denormal mode to use for inputs and outputs.
parseDenormalFPAttribute(StringRef Str)2185ffd83dbSDimitry Andric inline DenormalMode parseDenormalFPAttribute(StringRef Str) {
2195ffd83dbSDimitry Andric   StringRef OutputStr, InputStr;
2205ffd83dbSDimitry Andric   std::tie(OutputStr, InputStr) = Str.split(',');
2215ffd83dbSDimitry Andric 
2225ffd83dbSDimitry Andric   DenormalMode Mode;
2235ffd83dbSDimitry Andric   Mode.Output = parseDenormalFPAttributeComponent(OutputStr);
2245ffd83dbSDimitry Andric 
225*5f757f3fSDimitry Andric   // Maintain compatibility with old form of the attribute which only specified
2265ffd83dbSDimitry Andric   // one component.
2275ffd83dbSDimitry Andric   Mode.Input = InputStr.empty() ? Mode.Output  :
2285ffd83dbSDimitry Andric                parseDenormalFPAttributeComponent(InputStr);
2295ffd83dbSDimitry Andric 
2305ffd83dbSDimitry Andric   return Mode;
2315ffd83dbSDimitry Andric }
2325ffd83dbSDimitry Andric 
print(raw_ostream & OS)2335ffd83dbSDimitry Andric void DenormalMode::print(raw_ostream &OS) const {
2345ffd83dbSDimitry Andric   OS << denormalModeKindName(Output) << ',' << denormalModeKindName(Input);
2355ffd83dbSDimitry Andric }
2365ffd83dbSDimitry Andric 
23781ad6265SDimitry Andric /// Floating-point class tests, supported by 'is_fpclass' intrinsic. Actual
23881ad6265SDimitry Andric /// test may be an OR combination of basic tests.
23906c3fb27SDimitry Andric enum FPClassTest : unsigned {
24006c3fb27SDimitry Andric   fcNone = 0,
24106c3fb27SDimitry Andric 
24281ad6265SDimitry Andric   fcSNan = 0x0001,
24381ad6265SDimitry Andric   fcQNan = 0x0002,
24481ad6265SDimitry Andric   fcNegInf = 0x0004,
24581ad6265SDimitry Andric   fcNegNormal = 0x0008,
24681ad6265SDimitry Andric   fcNegSubnormal = 0x0010,
24781ad6265SDimitry Andric   fcNegZero = 0x0020,
24881ad6265SDimitry Andric   fcPosZero = 0x0040,
24981ad6265SDimitry Andric   fcPosSubnormal = 0x0080,
25081ad6265SDimitry Andric   fcPosNormal = 0x0100,
25181ad6265SDimitry Andric   fcPosInf = 0x0200,
25281ad6265SDimitry Andric 
25381ad6265SDimitry Andric   fcNan = fcSNan | fcQNan,
25481ad6265SDimitry Andric   fcInf = fcPosInf | fcNegInf,
25581ad6265SDimitry Andric   fcNormal = fcPosNormal | fcNegNormal,
25681ad6265SDimitry Andric   fcSubnormal = fcPosSubnormal | fcNegSubnormal,
25781ad6265SDimitry Andric   fcZero = fcPosZero | fcNegZero,
25881ad6265SDimitry Andric   fcPosFinite = fcPosNormal | fcPosSubnormal | fcPosZero,
25981ad6265SDimitry Andric   fcNegFinite = fcNegNormal | fcNegSubnormal | fcNegZero,
26081ad6265SDimitry Andric   fcFinite = fcPosFinite | fcNegFinite,
26106c3fb27SDimitry Andric   fcPositive = fcPosFinite | fcPosInf,
26206c3fb27SDimitry Andric   fcNegative = fcNegFinite | fcNegInf,
26306c3fb27SDimitry Andric 
26406c3fb27SDimitry Andric   fcAllFlags = fcNan | fcInf | fcFinite,
26581ad6265SDimitry Andric };
26681ad6265SDimitry Andric 
26706c3fb27SDimitry Andric LLVM_DECLARE_ENUM_AS_BITMASK(FPClassTest, /* LargestValue */ fcPosInf);
26806c3fb27SDimitry Andric 
26906c3fb27SDimitry Andric /// Return the test mask which returns true if the value's sign bit is flipped.
27006c3fb27SDimitry Andric FPClassTest fneg(FPClassTest Mask);
27106c3fb27SDimitry Andric 
272*5f757f3fSDimitry Andric /// Return the test mask which returns true after fabs is applied to the value.
273*5f757f3fSDimitry Andric FPClassTest inverse_fabs(FPClassTest Mask);
274*5f757f3fSDimitry Andric 
275*5f757f3fSDimitry Andric /// Return the test mask which returns true if the value could have the same set
276*5f757f3fSDimitry Andric /// of classes, but with a different sign.
277*5f757f3fSDimitry Andric FPClassTest unknown_sign(FPClassTest Mask);
27806c3fb27SDimitry Andric 
27906c3fb27SDimitry Andric /// Write a human readable form of \p Mask to \p OS
28006c3fb27SDimitry Andric raw_ostream &operator<<(raw_ostream &OS, FPClassTest Mask);
28106c3fb27SDimitry Andric 
28206c3fb27SDimitry Andric } // namespace llvm
28306c3fb27SDimitry Andric 
284fe6060f1SDimitry Andric #endif // LLVM_ADT_FLOATINGPOINTMODE_H
285