[llvm] [KnownFPClass] Refactor NaN sign encoding for KnownFPClass (PR #220606)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Sep 10 21:54:30 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-support
Author: Eric Ross (ZERICO2005)
<details>
<summary>Changes</summary>
Part of https://github.com/llvm/llvm-project/issues/217072
Replaces the `struct` layout for `KnownFPClass`
```c++
struct KnownFPClass {
FPClassTest KnownFPClasses;
std::optional<bool> SignBit;
};
struct KnownFPClass {
FPClassMask KnownFPMask;
};
```
I also added conversion functions between `FPClassTest` and `FPClassMask`.
---
Full diff: https://github.com/llvm/llvm-project/pull/220606.diff
2 Files Affected:
- (modified) llvm/include/llvm/Support/KnownFPClass.h (+262-81)
- (modified) llvm/lib/Support/KnownFPClass.cpp (+5-5)
``````````diff
diff --git a/llvm/include/llvm/Support/KnownFPClass.h b/llvm/include/llvm/Support/KnownFPClass.h
index 22097a70a361b..5e67c05da87fb 100644
--- a/llvm/include/llvm/Support/KnownFPClass.h
+++ b/llvm/include/llvm/Support/KnownFPClass.h
@@ -24,41 +24,219 @@ class APInt;
struct fltSemantics;
struct KnownBits;
+enum FPClassMask : unsigned {
+ kfcNone = 0,
+
+ kfcNegQNan = 0x0001,
+ kfcNegSNan = 0x0002,
+ kfcNegInf = 0x0004,
+ kfcNegNormal = 0x0008,
+ kfcNegSubnormal = 0x0010,
+ kfcNegZero = 0x0020,
+ kfcPosZero = 0x0040,
+ kfcPosSubnormal = 0x0080,
+ kfcPosNormal = 0x0100,
+ kfcPosInf = 0x0200,
+ kfcPosSNan = 0x0400,
+ kfcPosQNan = 0x0800,
+
+ kfcSNan = kfcPosSNan | kfcNegSNan,
+ kfcQNan = kfcPosQNan | kfcNegQNan,
+ kfcPosNan = kfcPosSNan | kfcPosQNan,
+ kfcNegNan = kfcNegSNan | kfcNegQNan,
+ kfcNan = kfcSNan | kfcQNan,
+ kfcInf = kfcPosInf | kfcNegInf,
+ kfcNormal = kfcPosNormal | kfcNegNormal,
+ kfcSubnormal = kfcPosSubnormal | kfcNegSubnormal,
+ kfcZero = kfcPosZero | kfcNegZero,
+ kfcPosFinite = kfcPosNormal | kfcPosSubnormal | kfcPosZero,
+ kfcNegFinite = kfcNegNormal | kfcNegSubnormal | kfcNegZero,
+ kfcFinite = kfcPosFinite | kfcNegFinite,
+ kfcPositive = kfcPosFinite | kfcPosInf,
+ kfcNegative = kfcNegFinite | kfcNegInf,
+ kfcPosSignBit = kfcPositive | kfcPosNan,
+ kfcNegSignBit = kfcNegative | kfcNegNan,
+
+ kfcAllFlags = kfcNan | kfcInf | kfcFinite,
+};
+
+LLVM_DECLARE_ENUM_AS_BITMASK(FPClassMask, /* LargestValue */ kfcPosQNan);
+
+constexpr FPClassMask toFPClassMask(FPClassTest Classes) {
+ FPClassMask Mask = kfcNone;
+
+ if (Classes & fcQNan)
+ Mask |= kfcQNan;
+ if (Classes & fcSNan)
+ Mask |= kfcSNan;
+ if (Classes & fcNegInf)
+ Mask |= kfcNegInf;
+ if (Classes & fcNegNormal)
+ Mask |= kfcNegNormal;
+ if (Classes & fcNegSubnormal)
+ Mask |= kfcNegSubnormal;
+ if (Classes & fcNegZero)
+ Mask |= kfcNegZero;
+ if (Classes & fcPosZero)
+ Mask |= kfcPosZero;
+ if (Classes & fcPosSubnormal)
+ Mask |= kfcPosSubnormal;
+ if (Classes & fcPosNormal)
+ Mask |= kfcPosNormal;
+ if (Classes & fcPosInf)
+ Mask |= kfcPosInf;
+
+ return Mask;
+}
+
+constexpr FPClassMask toFPClassMask(FPClassTest Classes,
+ std::optional<bool> SignBit) {
+ FPClassMask Mask = toFPClassMask(Classes);
+
+ // This is the only way to generate a NaN with a specific sign from
+ // FPClassTest. SignBit must agree with the input classes.
+ if (SignBit) {
+ if (!*SignBit) {
+ Mask &= ~kfcNegNan;
+ // If the SignBit is false, then we should not have any negative classes.
+ if (!(Mask & kfcNegSignBit))
+ return Mask;
+ } else {
+ Mask &= ~kfcPosNan;
+ // If the SignBit is true, then we should not have any positive classes.
+ if (!(Mask & kfcPosSignBit))
+ return Mask;
+ }
+ }
+
+ // SignBit is unknown or inconsistent with the input classes. Expand the
+ // possible set to its opposite sign pair.
+ if (Classes & fcQNan)
+ Mask |= kfcQNan;
+ if (Classes & fcSNan)
+ Mask |= kfcSNan;
+ if (Classes & fcInf)
+ Mask |= kfcInf;
+ if (Classes & fcNormal)
+ Mask |= kfcNormal;
+ if (Classes & fcSubnormal)
+ Mask |= kfcSubnormal;
+ if (Classes & fcZero)
+ Mask |= kfcZero;
+
+ return Mask;
+}
+
+constexpr FPClassTest toFPClassTest(FPClassMask Mask) {
+ FPClassTest Classes = fcNone;
+
+ // Sign of qNaN and sNaN are lost in the conversion.
+ if (Mask & kfcQNan)
+ Classes |= fcQNan;
+ if (Mask & kfcSNan)
+ Classes |= fcSNan;
+
+ if (Mask & kfcNegInf)
+ Classes |= fcNegInf;
+ if (Mask & kfcNegNormal)
+ Classes |= fcNegNormal;
+ if (Mask & kfcNegSubnormal)
+ Classes |= fcNegSubnormal;
+ if (Mask & kfcNegZero)
+ Classes |= fcNegZero;
+ if (Mask & kfcPosZero)
+ Classes |= fcPosZero;
+ if (Mask & kfcPosSubnormal)
+ Classes |= fcPosSubnormal;
+ if (Mask & kfcPosNormal)
+ Classes |= fcPosNormal;
+ if (Mask & kfcPosInf)
+ Classes |= fcPosInf;
+
+ return Classes;
+}
+
struct KnownFPClass {
- FPClassTest KnownFPClassesValue = fcAllFlags;
- std::optional<bool> SignBitValue;
+ FPClassMask KnownFPMask = kfcAllFlags;
/// Floating-point classes the value could be one of.
- FPClassTest getKnownFPClasses() const { return KnownFPClassesValue; }
+ FPClassTest getKnownFPClasses() const { return toFPClassTest(KnownFPMask); }
- void setKnownFPClasses(FPClassTest Classes) { KnownFPClassesValue = Classes; }
+ void setKnownFPClasses(FPClassTest Classes) {
+ KnownFPMask = toFPClassMask(Classes);
+ }
+
+ void setKnownFPClasses(FPClassTest Classes, std::optional<bool> Sign) {
+ KnownFPMask = toFPClassMask(Classes, Sign);
+ }
/// std::nullopt if the sign bit is unknown, true if the sign bit is
/// definitely set or false if the sign bit is definitely unset.
- std::optional<bool> getSignBit() const { return SignBitValue; }
+ /// By convention, returns false for kfcNone/poison.
+ std::optional<bool> getSignBit() const {
+ if (KnownFPMask == kfcNone)
+ return false;
- void setSignBit(std::optional<bool> Sign) { SignBitValue = Sign; }
+ if ((KnownFPMask & kfcPosSignBit) == KnownFPMask)
+ return false;
+ if ((KnownFPMask & kfcNegSignBit) == KnownFPMask)
+ return true;
- KnownFPClass(FPClassTest Known = fcAllFlags, std::optional<bool> Sign = {})
- : KnownFPClassesValue(Known), SignBitValue(Sign) {}
+ return std::nullopt;
+ }
+
+ void setSignBit(std::optional<bool> Sign) {
+ if (Sign && !*Sign) {
+ KnownFPMask &= kfcPosSignBit;
+ return;
+ }
+ if (Sign && *Sign) {
+ KnownFPMask &= kfcNegSignBit;
+ return;
+ }
+ // Set sign to unknown.
+ if (KnownFPMask & kfcQNan)
+ KnownFPMask |= kfcQNan;
+ if (KnownFPMask & kfcSNan)
+ KnownFPMask |= kfcSNan;
+ if (KnownFPMask & kfcInf)
+ KnownFPMask |= kfcInf;
+ if (KnownFPMask & kfcNormal)
+ KnownFPMask |= kfcNormal;
+ if (KnownFPMask & kfcSubnormal)
+ KnownFPMask |= kfcSubnormal;
+ if (KnownFPMask & kfcZero)
+ KnownFPMask |= kfcZero;
+ }
+
+ KnownFPClass(FPClassMask Known = kfcAllFlags) : KnownFPMask(Known) {}
+ KnownFPClass(FPClassTest Known) : KnownFPMask(toFPClassMask(Known)) {}
+ KnownFPClass(FPClassTest Known, std::optional<bool> Sign)
+ : KnownFPMask(toFPClassMask(Known, Sign)) {}
LLVM_ABI KnownFPClass(const APFloat &C);
bool operator==(KnownFPClass Other) const {
- return getKnownFPClasses() == Other.getKnownFPClasses() &&
- getSignBit() == Other.getSignBit();
+ return KnownFPMask == Other.KnownFPMask;
+ }
+
+ /// Return true if it's known this can never be one of the mask entries.
+ bool isKnownNever(FPClassMask Mask) const {
+ return (KnownFPMask & Mask) == kfcNone;
}
/// Return true if it's known this can never be one of the mask entries.
bool isKnownNever(FPClassTest Mask) const {
- return (getKnownFPClasses() & Mask) == fcNone;
+ return isKnownNever(toFPClassMask(Mask));
}
- bool isKnownAlways(FPClassTest Mask) const { return isKnownNever(~Mask); }
+ bool isKnownAlways(FPClassMask Mask) const { return isKnownNever(~Mask); }
- bool isUnknown() const {
- return getKnownFPClasses() == fcAllFlags && !getSignBit();
+ bool isKnownAlways(FPClassTest Mask) const {
+ return isKnownAlways(toFPClassMask(Mask));
}
+ bool isUnknown() const { return KnownFPMask == kfcAllFlags; }
+
/// Return true if it's known this can never be a nan.
bool isKnownNeverNaN() const { return isKnownNever(fcNan); }
@@ -161,44 +339,51 @@ struct KnownFPClass {
}
KnownFPClass intersectWith(const KnownFPClass &RHS) const {
- return KnownFPClass(getKnownFPClasses() | RHS.getKnownFPClasses(),
- getSignBit() == RHS.getSignBit() ? getSignBit()
- : std::nullopt);
+ return KnownFPClass(KnownFPMask | RHS.KnownFPMask);
}
KnownFPClass unionWith(const KnownFPClass &RHS) const {
- std::optional<bool> MergedSignBit;
- if (getSignBit() && !RHS.getSignBit())
- MergedSignBit = getSignBit();
- else if (!getSignBit() && RHS.getSignBit())
- MergedSignBit = RHS.getSignBit();
-
- return KnownFPClass(getKnownFPClasses() & RHS.getKnownFPClasses(),
- MergedSignBit);
+ return KnownFPClass(KnownFPMask & RHS.KnownFPMask);
}
KnownFPClass &operator|=(const KnownFPClass &RHS) {
- setKnownFPClasses(getKnownFPClasses() | RHS.getKnownFPClasses());
-
- if (getSignBit() != RHS.getSignBit())
- setSignBit(std::nullopt);
+ KnownFPMask |= RHS.KnownFPMask;
return *this;
}
- void knownNot(FPClassTest RuleOut) {
- setKnownFPClasses(getKnownFPClasses() & ~RuleOut);
- if (isKnownNever(fcNan) && !getSignBit()) {
- if (isKnownNever(fcNegative))
- setSignBit(false);
- else if (isKnownNever(fcPositive))
- setSignBit(true);
- }
- }
+ void knownNot(FPClassMask RuleOut) { KnownFPMask &= ~RuleOut; }
+
+ void knownNot(FPClassTest RuleOut) { knownNot(toFPClassMask(RuleOut)); }
void fneg() {
- setKnownFPClasses(llvm::fneg(getKnownFPClasses()));
- if (std::optional<bool> Sign = getSignBit())
- setSignBit(!*Sign);
+ FPClassMask Known = kfcNone;
+
+ if (KnownFPMask & kfcNegQNan)
+ Known |= kfcPosQNan;
+ if (KnownFPMask & kfcNegSNan)
+ Known |= kfcPosSNan;
+ if (KnownFPMask & kfcNegInf)
+ Known |= kfcPosInf;
+ if (KnownFPMask & kfcNegNormal)
+ Known |= kfcPosNormal;
+ if (KnownFPMask & kfcNegSubnormal)
+ Known |= kfcPosSubnormal;
+ if (KnownFPMask & kfcNegZero)
+ Known |= kfcPosZero;
+ if (KnownFPMask & kfcPosZero)
+ Known |= kfcNegZero;
+ if (KnownFPMask & kfcPosSubnormal)
+ Known |= kfcNegSubnormal;
+ if (KnownFPMask & kfcPosNormal)
+ Known |= kfcNegNormal;
+ if (KnownFPMask & kfcPosInf)
+ Known |= kfcNegInf;
+ if (KnownFPMask & kfcPosSNan)
+ Known |= kfcNegSNan;
+ if (KnownFPMask & kfcPosQNan)
+ Known |= kfcNegQNan;
+
+ KnownFPMask = Known;
}
static KnownFPClass fneg(const KnownFPClass &Src) {
@@ -208,19 +393,22 @@ struct KnownFPClass {
}
void fabs() {
- if (getKnownFPClasses() & fcNegZero)
- setKnownFPClasses(getKnownFPClasses() | fcPosZero);
-
- if (getKnownFPClasses() & fcNegInf)
- setKnownFPClasses(getKnownFPClasses() | fcPosInf);
-
- if (getKnownFPClasses() & fcNegSubnormal)
- setKnownFPClasses(getKnownFPClasses() | fcPosSubnormal);
-
- if (getKnownFPClasses() & fcNegNormal)
- setKnownFPClasses(getKnownFPClasses() | fcPosNormal);
-
- signBitMustBeZero();
+ FPClassMask Known = kfcNone;
+
+ if (KnownFPMask & kfcQNan)
+ Known |= kfcPosQNan;
+ if (KnownFPMask & kfcSNan)
+ Known |= kfcPosSNan;
+ if (KnownFPMask & kfcInf)
+ Known |= kfcPosInf;
+ if (KnownFPMask & kfcNormal)
+ Known |= kfcPosNormal;
+ if (KnownFPMask & kfcSubnormal)
+ Known |= kfcPosSubnormal;
+ if (KnownFPMask & kfcZero)
+ Known |= kfcPosZero;
+
+ KnownFPMask = Known;
}
static KnownFPClass fabs(const KnownFPClass &Src) {
@@ -369,39 +557,32 @@ struct KnownFPClass {
bool signBitIsZeroOrNaN() const { return isKnownNever(fcNegative); }
/// Assume the sign bit is zero.
- void signBitMustBeZero() {
- setKnownFPClasses(getKnownFPClasses() & (fcPositive | fcNan));
- setSignBit(false);
- }
+ void signBitMustBeZero() { KnownFPMask &= kfcPosSignBit; }
/// Assume the sign bit is one.
- void signBitMustBeOne() {
- setKnownFPClasses(getKnownFPClasses() & (fcNegative | fcNan));
- setSignBit(true);
- }
+ void signBitMustBeOne() { KnownFPMask &= kfcNegSignBit; }
void copysign(const KnownFPClass &Sign) {
// Don't know anything about the sign of the source. Expand the possible set
// to its opposite sign pair.
- if (getKnownFPClasses() & fcZero)
- setKnownFPClasses(getKnownFPClasses() | fcZero);
- if (getKnownFPClasses() & fcSubnormal)
- setKnownFPClasses(getKnownFPClasses() | fcSubnormal);
- if (getKnownFPClasses() & fcNormal)
- setKnownFPClasses(getKnownFPClasses() | fcNormal);
- if (getKnownFPClasses() & fcInf)
- setKnownFPClasses(getKnownFPClasses() | fcInf);
-
- // Sign bit is exactly preserved even for nans.
- setSignBit(Sign.getSignBit());
-
- // Clear sign bits based on the input sign mask.
- if (Sign.isKnownNever(fcPositive | fcNan) ||
- (getSignBit() && *getSignBit()))
- setKnownFPClasses(getKnownFPClasses() & (fcNegative | fcNan));
- if (Sign.isKnownNever(fcNegative | fcNan) ||
- (getSignBit() && !*getSignBit()))
- setKnownFPClasses(getKnownFPClasses() & (fcPositive | fcNan));
+
+ if (KnownFPMask & kfcQNan)
+ KnownFPMask |= kfcQNan;
+ if (KnownFPMask & kfcSNan)
+ KnownFPMask |= kfcSNan;
+ if (KnownFPMask & kfcInf)
+ KnownFPMask |= kfcInf;
+ if (KnownFPMask & kfcNormal)
+ KnownFPMask |= kfcNormal;
+ if (KnownFPMask & kfcSubnormal)
+ KnownFPMask |= kfcSubnormal;
+ if (KnownFPMask & kfcZero)
+ KnownFPMask |= kfcZero;
+
+ if (Sign.getSignBit() && !*Sign.getSignBit())
+ KnownFPMask &= kfcPosSignBit;
+ if (Sign.getSignBit() && *Sign.getSignBit())
+ KnownFPMask &= kfcNegSignBit;
}
static KnownFPClass copysign(const KnownFPClass &KnownMag,
diff --git a/llvm/lib/Support/KnownFPClass.cpp b/llvm/lib/Support/KnownFPClass.cpp
index 1f596edd74100..f9492e1580d0d 100644
--- a/llvm/lib/Support/KnownFPClass.cpp
+++ b/llvm/lib/Support/KnownFPClass.cpp
@@ -19,9 +19,7 @@
using namespace llvm;
KnownFPClass::KnownFPClass(const APFloat &C)
- : KnownFPClassesValue(C.classify()) {
- setSignBit(C.isNegative());
-}
+ : KnownFPClass(C.classify(), C.isNegative()) {}
/// Return true if it's possible to assume IEEE treatment of input denormals in
/// \p F for \p Val.
@@ -822,8 +820,10 @@ KnownFPClass KnownFPClass::fpext(const KnownFPClass &KnownSrc,
}
// Sign bit of a nan isn't guaranteed.
- if (!Known.isKnownNeverNaN())
- Known.setSignBit(std::nullopt);
+ if (Known.KnownFPMask & kfcSNan)
+ Known.KnownFPMask |= kfcSNan;
+ if (Known.KnownFPMask & kfcQNan)
+ Known.KnownFPMask |= kfcQNan;
return Known;
}
``````````
</details>
https://github.com/llvm/llvm-project/pull/220606
More information about the llvm-commits
mailing list