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