Skip to content

[Analysis][NFC] Extract KnownFPClass #133457

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 28, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
334 changes: 27 additions & 307 deletions llvm/include/llvm/Analysis/ValueTracking.h
Original file line number Diff line number Diff line change
@@ -34,6 +34,7 @@ class DominatorTree;
class GEPOperator;
class WithOverflowInst;
struct KnownBits;
struct KnownFPClass;
class Loop;
class LoopInfo;
class MDNode;
@@ -255,244 +256,6 @@ std::tuple<Value *, FPClassTest, FPClassTest>
fcmpImpliesClass(CmpInst::Predicate Pred, const Function &F, Value *LHS,
const APFloat &RHS, bool LookThroughSrc = true);

struct KnownFPClass {
/// Floating-point classes the value could be one of.
FPClassTest KnownFPClasses = fcAllFlags;

/// 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> SignBit;

bool operator==(KnownFPClass Other) const {
return KnownFPClasses == Other.KnownFPClasses && SignBit == Other.SignBit;
}

/// Return true if it's known this can never be one of the mask entries.
bool isKnownNever(FPClassTest Mask) const {
return (KnownFPClasses & Mask) == fcNone;
}

bool isKnownAlways(FPClassTest Mask) const { return isKnownNever(~Mask); }

bool isUnknown() const {
return KnownFPClasses == fcAllFlags && !SignBit;
}

/// Return true if it's known this can never be a nan.
bool isKnownNeverNaN() const {
return isKnownNever(fcNan);
}

/// Return true if it's known this must always be a nan.
bool isKnownAlwaysNaN() const { return isKnownAlways(fcNan); }

/// Return true if it's known this can never be an infinity.
bool isKnownNeverInfinity() const {
return isKnownNever(fcInf);
}

/// Return true if it's known this can never be +infinity.
bool isKnownNeverPosInfinity() const {
return isKnownNever(fcPosInf);
}

/// Return true if it's known this can never be -infinity.
bool isKnownNeverNegInfinity() const {
return isKnownNever(fcNegInf);
}

/// Return true if it's known this can never be a subnormal
bool isKnownNeverSubnormal() const {
return isKnownNever(fcSubnormal);
}

/// Return true if it's known this can never be a positive subnormal
bool isKnownNeverPosSubnormal() const {
return isKnownNever(fcPosSubnormal);
}

/// Return true if it's known this can never be a negative subnormal
bool isKnownNeverNegSubnormal() const {
return isKnownNever(fcNegSubnormal);
}

/// Return true if it's known this can never be a zero. This means a literal
/// [+-]0, and does not include denormal inputs implicitly treated as [+-]0.
bool isKnownNeverZero() const {
return isKnownNever(fcZero);
}

/// Return true if it's known this can never be a literal positive zero.
bool isKnownNeverPosZero() const {
return isKnownNever(fcPosZero);
}

/// Return true if it's known this can never be a negative zero. This means a
/// literal -0 and does not include denormal inputs implicitly treated as -0.
bool isKnownNeverNegZero() const {
return isKnownNever(fcNegZero);
}

/// Return true if it's know this can never be interpreted as a zero. This
/// extends isKnownNeverZero to cover the case where the assumed
/// floating-point mode for the function interprets denormals as zero.
bool isKnownNeverLogicalZero(const Function &F, Type *Ty) const;

/// Return true if it's know this can never be interpreted as a negative zero.
bool isKnownNeverLogicalNegZero(const Function &F, Type *Ty) const;

/// Return true if it's know this can never be interpreted as a positive zero.
bool isKnownNeverLogicalPosZero(const Function &F, Type *Ty) const;

static constexpr FPClassTest OrderedLessThanZeroMask =
fcNegSubnormal | fcNegNormal | fcNegInf;
static constexpr FPClassTest OrderedGreaterThanZeroMask =
fcPosSubnormal | fcPosNormal | fcPosInf;

/// Return true if we can prove that the analyzed floating-point value is
/// either NaN or never less than -0.0.
///
/// NaN --> true
/// +0 --> true
/// -0 --> true
/// x > +0 --> true
/// x < -0 --> false
bool cannotBeOrderedLessThanZero() const {
return isKnownNever(OrderedLessThanZeroMask);
}

/// Return true if we can prove that the analyzed floating-point value is
/// either NaN or never greater than -0.0.
/// NaN --> true
/// +0 --> true
/// -0 --> true
/// x > +0 --> false
/// x < -0 --> true
bool cannotBeOrderedGreaterThanZero() const {
return isKnownNever(OrderedGreaterThanZeroMask);
}

KnownFPClass &operator|=(const KnownFPClass &RHS) {
KnownFPClasses = KnownFPClasses | RHS.KnownFPClasses;

if (SignBit != RHS.SignBit)
SignBit = std::nullopt;
return *this;
}

void knownNot(FPClassTest RuleOut) {
KnownFPClasses = KnownFPClasses & ~RuleOut;
if (isKnownNever(fcNan) && !SignBit) {
if (isKnownNever(fcNegative))
SignBit = false;
else if (isKnownNever(fcPositive))
SignBit = true;
}
}

void fneg() {
KnownFPClasses = llvm::fneg(KnownFPClasses);
if (SignBit)
SignBit = !*SignBit;
}

void fabs() {
if (KnownFPClasses & fcNegZero)
KnownFPClasses |= fcPosZero;

if (KnownFPClasses & fcNegInf)
KnownFPClasses |= fcPosInf;

if (KnownFPClasses & fcNegSubnormal)
KnownFPClasses |= fcPosSubnormal;

if (KnownFPClasses & fcNegNormal)
KnownFPClasses |= fcPosNormal;

signBitMustBeZero();
}

/// Return true if the sign bit must be 0, ignoring the sign of nans.
bool signBitIsZeroOrNaN() const {
return isKnownNever(fcNegative);
}

/// Assume the sign bit is zero.
void signBitMustBeZero() {
KnownFPClasses &= (fcPositive | fcNan);
SignBit = false;
}

/// Assume the sign bit is one.
void signBitMustBeOne() {
KnownFPClasses &= (fcNegative | fcNan);
SignBit = true;
}

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 (KnownFPClasses & fcZero)
KnownFPClasses |= fcZero;
if (KnownFPClasses & fcSubnormal)
KnownFPClasses |= fcSubnormal;
if (KnownFPClasses & fcNormal)
KnownFPClasses |= fcNormal;
if (KnownFPClasses & fcInf)
KnownFPClasses |= fcInf;

// Sign bit is exactly preserved even for nans.
SignBit = Sign.SignBit;

// Clear sign bits based on the input sign mask.
if (Sign.isKnownNever(fcPositive | fcNan) || (SignBit && *SignBit))
KnownFPClasses &= (fcNegative | fcNan);
if (Sign.isKnownNever(fcNegative | fcNan) || (SignBit && !*SignBit))
KnownFPClasses &= (fcPositive | fcNan);
}

// Propagate knowledge that a non-NaN source implies the result can also not
// be a NaN. For unconstrained operations, signaling nans are not guaranteed
// to be quieted but cannot be introduced.
void propagateNaN(const KnownFPClass &Src, bool PreserveSign = false) {
if (Src.isKnownNever(fcNan)) {
knownNot(fcNan);
if (PreserveSign)
SignBit = Src.SignBit;
} else if (Src.isKnownNever(fcSNan))
knownNot(fcSNan);
}

/// Propagate knowledge from a source value that could be a denormal or
/// zero. We have to be conservative since output flushing is not guaranteed,
/// so known-never-zero may not hold.
///
/// This assumes a copy-like operation and will replace any currently known
/// information.
void propagateDenormal(const KnownFPClass &Src, const Function &F, Type *Ty);

/// Report known classes if \p Src is evaluated through a potentially
/// canonicalizing operation. We can assume signaling nans will not be
/// introduced, but cannot assume a denormal will be flushed under FTZ/DAZ.
///
/// This assumes a copy-like operation and will replace any currently known
/// information.
void propagateCanonicalizingSrc(const KnownFPClass &Src, const Function &F,
Type *Ty);

void resetAll() { *this = KnownFPClass(); }
};

inline KnownFPClass operator|(KnownFPClass LHS, const KnownFPClass &RHS) {
LHS |= RHS;
return LHS;
}

inline KnownFPClass operator|(const KnownFPClass &LHS, KnownFPClass &&RHS) {
RHS |= LHS;
return std::move(RHS);
}

/// Determine which floating-point classes are valid for \p V, and return them
/// in KnownFPClass bit sets.
///
@@ -510,56 +273,30 @@ KnownFPClass computeKnownFPClass(const Value *V, const APInt &DemandedElts,
KnownFPClass computeKnownFPClass(const Value *V, FPClassTest InterestedClasses,
unsigned Depth, const SimplifyQuery &SQ);

inline KnownFPClass computeKnownFPClass(
const Value *V, const DataLayout &DL,
FPClassTest InterestedClasses = fcAllFlags, unsigned Depth = 0,
const TargetLibraryInfo *TLI = nullptr, AssumptionCache *AC = nullptr,
const Instruction *CxtI = nullptr, const DominatorTree *DT = nullptr,
bool UseInstrInfo = true) {
return computeKnownFPClass(
V, InterestedClasses, Depth,
SimplifyQuery(DL, TLI, DT, AC, CxtI, UseInstrInfo));
}
KnownFPClass computeKnownFPClass(const Value *V, const DataLayout &DL,
FPClassTest InterestedClasses = fcAllFlags,
unsigned Depth = 0,
const TargetLibraryInfo *TLI = nullptr,
AssumptionCache *AC = nullptr,
const Instruction *CxtI = nullptr,
const DominatorTree *DT = nullptr,
bool UseInstrInfo = true);

/// Wrapper to account for known fast math flags at the use instruction.
inline KnownFPClass
computeKnownFPClass(const Value *V, const APInt &DemandedElts,
FastMathFlags FMF, FPClassTest InterestedClasses,
unsigned Depth, const SimplifyQuery &SQ) {
if (FMF.noNaNs())
InterestedClasses &= ~fcNan;
if (FMF.noInfs())
InterestedClasses &= ~fcInf;

KnownFPClass Result =
computeKnownFPClass(V, DemandedElts, InterestedClasses, Depth, SQ);

if (FMF.noNaNs())
Result.KnownFPClasses &= ~fcNan;
if (FMF.noInfs())
Result.KnownFPClasses &= ~fcInf;
return Result;
}
KnownFPClass computeKnownFPClass(const Value *V, const APInt &DemandedElts,
FastMathFlags FMF,
FPClassTest InterestedClasses, unsigned Depth,
const SimplifyQuery &SQ);

inline KnownFPClass computeKnownFPClass(const Value *V, FastMathFlags FMF,
FPClassTest InterestedClasses,
unsigned Depth,
const SimplifyQuery &SQ) {
auto *FVTy = dyn_cast<FixedVectorType>(V->getType());
APInt DemandedElts =
FVTy ? APInt::getAllOnes(FVTy->getNumElements()) : APInt(1, 1);
return computeKnownFPClass(V, DemandedElts, FMF, InterestedClasses, Depth,
SQ);
}
KnownFPClass computeKnownFPClass(const Value *V, FastMathFlags FMF,
FPClassTest InterestedClasses, unsigned Depth,
const SimplifyQuery &SQ);

/// Return true if we can prove that the specified FP value is never equal to
/// -0.0. Users should use caution when considering PreserveSign
/// denormal-fp-math.
inline bool cannotBeNegativeZero(const Value *V, unsigned Depth,
const SimplifyQuery &SQ) {
KnownFPClass Known = computeKnownFPClass(V, fcNegZero, Depth, SQ);
return Known.isKnownNeverNegZero();
}
bool cannotBeNegativeZero(const Value *V, unsigned Depth,
const SimplifyQuery &SQ);

/// Return true if we can prove that the specified FP value is either NaN or
/// never less than -0.0.
@@ -569,46 +306,29 @@ inline bool cannotBeNegativeZero(const Value *V, unsigned Depth,
/// -0 --> true
/// x > +0 --> true
/// x < -0 --> false
inline bool cannotBeOrderedLessThanZero(const Value *V, unsigned Depth,
const SimplifyQuery &SQ) {
KnownFPClass Known =
computeKnownFPClass(V, KnownFPClass::OrderedLessThanZeroMask, Depth, SQ);
return Known.cannotBeOrderedLessThanZero();
}
bool cannotBeOrderedLessThanZero(const Value *V, unsigned Depth,
const SimplifyQuery &SQ);

/// Return true if the floating-point scalar value is not an infinity or if
/// the floating-point vector value has no infinities. Return false if a value
/// could ever be infinity.
inline bool isKnownNeverInfinity(const Value *V, unsigned Depth,
const SimplifyQuery &SQ) {
KnownFPClass Known = computeKnownFPClass(V, fcInf, Depth, SQ);
return Known.isKnownNeverInfinity();
}
bool isKnownNeverInfinity(const Value *V, unsigned Depth,
const SimplifyQuery &SQ);

/// Return true if the floating-point value can never contain a NaN or infinity.
inline bool isKnownNeverInfOrNaN(const Value *V, unsigned Depth,
const SimplifyQuery &SQ) {
KnownFPClass Known = computeKnownFPClass(V, fcInf | fcNan, Depth, SQ);
return Known.isKnownNeverNaN() && Known.isKnownNeverInfinity();
}
bool isKnownNeverInfOrNaN(const Value *V, unsigned Depth,
const SimplifyQuery &SQ);

/// Return true if the floating-point scalar value is not a NaN or if the
/// floating-point vector value has no NaN elements. Return false if a value
/// could ever be NaN.
inline bool isKnownNeverNaN(const Value *V, unsigned Depth,
const SimplifyQuery &SQ) {
KnownFPClass Known = computeKnownFPClass(V, fcNan, Depth, SQ);
return Known.isKnownNeverNaN();
}
bool isKnownNeverNaN(const Value *V, unsigned Depth, const SimplifyQuery &SQ);

/// Return false if we can prove that the specified FP value's sign bit is 0.
/// Return true if we can prove that the specified FP value's sign bit is 1.
/// Otherwise return std::nullopt.
inline std::optional<bool> computeKnownFPSignBit(const Value *V, unsigned Depth,
const SimplifyQuery &SQ) {
KnownFPClass Known = computeKnownFPClass(V, fcAllFlags, Depth, SQ);
return Known.SignBit;
}
std::optional<bool> computeKnownFPSignBit(const Value *V, unsigned Depth,
const SimplifyQuery &SQ);

/// If the specified value can be set by repeating the same byte in memory,
/// return the i8 value that it is represented with. This is true for all i8
Loading
Oops, something went wrong.