Files
gps/tools/local/circt/include/llvm/Support/KnownFPClass.h
T
2026-07-12 11:01:51 +03:00

476 lines
16 KiB
C++

//===- llvm/Support/KnownFPClass.h - Stores known fpclass -------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file contains a class for representing known fpclasses used by
// computeKnownFPClass.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_SUPPORT_KNOWNFPCLASS_H
#define LLVM_SUPPORT_KNOWNFPCLASS_H
#include "llvm/ADT/FloatingPointMode.h"
#include "llvm/Support/Compiler.h"
#include <optional>
namespace llvm {
class APFloat;
struct fltSemantics;
struct KnownBits;
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;
KnownFPClass(FPClassTest Known = fcAllFlags, std::optional<bool> Sign = {})
: KnownFPClasses(Known), SignBit(Sign) {}
LLVM_ABI KnownFPClass(const APFloat &C);
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 an infinity or nan
bool isKnownNeverInfOrNaN() const { return isKnownNever(fcInf | fcNan); }
/// 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 known 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.
LLVM_ABI bool isKnownNeverLogicalZero(DenormalMode Mode) const;
/// Return true if it's known this can never be interpreted as a negative
/// zero.
LLVM_ABI bool isKnownNeverLogicalNegZero(DenormalMode Mode) const;
/// Return true if it's known this can never be interpreted as a positive
/// zero.
LLVM_ABI bool isKnownNeverLogicalPosZero(DenormalMode Mode) 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);
}
/// Return true if it's known this can never be a positive value or a logical
/// 0.
///
/// NaN --> true
/// x <= +0 --> false
/// psub --> true if mode is ieee, false otherwise.
/// x > +0 --> true
bool cannotBeOrderedLessEqZero(DenormalMode Mode) const {
return isKnownNever(fcNegative) && isKnownNeverLogicalPosZero(Mode);
}
/// Return true if it's know this can never be a negative value or a logical
/// 0.
///
/// NaN --> true
/// x >= -0 --> false
/// nsub --> true if mode is ieee, false otherwise.
/// x < -0 --> true
bool cannotBeOrderedGreaterEqZero(DenormalMode Mode) const {
return isKnownNever(fcPositive) && isKnownNeverLogicalNegZero(Mode);
}
KnownFPClass intersectWith(const KnownFPClass &RHS) const {
return KnownFPClass(KnownFPClasses | RHS.KnownFPClasses,
SignBit == RHS.SignBit ? SignBit : std::nullopt);
}
KnownFPClass unionWith(const KnownFPClass &RHS) const {
std::optional<bool> MergedSignBit;
if (SignBit && !RHS.SignBit)
MergedSignBit = SignBit;
else if (!SignBit && RHS.SignBit)
MergedSignBit = RHS.SignBit;
return KnownFPClass(KnownFPClasses & RHS.KnownFPClasses, MergedSignBit);
}
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;
}
static KnownFPClass fneg(const KnownFPClass &Src) {
KnownFPClass Known = Src;
Known.fneg();
return Known;
}
void fabs() {
if (KnownFPClasses & fcNegZero)
KnownFPClasses |= fcPosZero;
if (KnownFPClasses & fcNegInf)
KnownFPClasses |= fcPosInf;
if (KnownFPClasses & fcNegSubnormal)
KnownFPClasses |= fcPosSubnormal;
if (KnownFPClasses & fcNegNormal)
KnownFPClasses |= fcPosNormal;
signBitMustBeZero();
}
static KnownFPClass fabs(const KnownFPClass &Src) {
KnownFPClass Known = Src;
Known.fabs();
return Known;
}
// Enum of min/max intrinsics to avoid dependency on IR.
enum class MinMaxKind {
minimum,
maximum,
minimumnum,
maximumnum,
minnum,
maxnum
};
LLVM_ABI static KnownFPClass
minMaxLike(const KnownFPClass &LHS, const KnownFPClass &RHS, MinMaxKind Kind,
DenormalMode DenormMode = DenormalMode::getDynamic());
/// Apply the canonicalize intrinsic to this value. This is essentially a
/// stronger form of propagateCanonicalizingSrc.
LLVM_ABI static KnownFPClass
canonicalize(const KnownFPClass &Src,
DenormalMode DenormMode = DenormalMode::getDynamic());
/// Report known values for a bitcast into a float with provided semantics.
LLVM_ABI static KnownFPClass bitcast(const fltSemantics &FltSemantics,
const KnownBits &Bits);
/// Report known values for fadd
LLVM_ABI static KnownFPClass
fadd(const KnownFPClass &LHS, const KnownFPClass &RHS,
DenormalMode Mode = DenormalMode::getDynamic());
/// Report known values for fadd x, x
LLVM_ABI static KnownFPClass
fadd_self(const KnownFPClass &Src,
DenormalMode Mode = DenormalMode::getDynamic());
/// Report known values for fsub
LLVM_ABI static KnownFPClass
fsub(const KnownFPClass &LHS, const KnownFPClass &RHS,
DenormalMode Mode = DenormalMode::getDynamic());
/// Report known values for fmul
LLVM_ABI static KnownFPClass
fmul(const KnownFPClass &LHS, const KnownFPClass &RHS,
DenormalMode Mode = DenormalMode::getDynamic());
// Special case of fmul x, x.
static KnownFPClass square(const KnownFPClass &Src,
DenormalMode Mode = DenormalMode::getDynamic()) {
KnownFPClass Known = fmul(Src, Src, Mode);
// X * X is always non-negative or a NaN.
Known.knownNot(fcNegative);
Known.propagateNaN(Src);
return Known;
}
LLVM_ABI static KnownFPClass
fmul(const KnownFPClass &LHS, const APFloat &RHS,
DenormalMode Mode = DenormalMode::getDynamic());
/// Report known values for fdiv
LLVM_ABI static KnownFPClass
fdiv(const KnownFPClass &LHS, const KnownFPClass &RHS,
DenormalMode Mode = DenormalMode::getDynamic());
/// Report known values for fdiv x, x
LLVM_ABI static KnownFPClass
fdiv_self(const KnownFPClass &Src,
DenormalMode Mode = DenormalMode::getDynamic());
/// Report known values for frem
LLVM_ABI static KnownFPClass
frem_self(const KnownFPClass &Src,
DenormalMode Mode = DenormalMode::getDynamic());
/// Report known values for fma
LLVM_ABI static KnownFPClass
fma(const KnownFPClass &LHS, const KnownFPClass &RHS,
const KnownFPClass &Addend,
DenormalMode Mode = DenormalMode::getDynamic());
/// Report known values for fma squared, squared, addend
LLVM_ABI static KnownFPClass
fma_square(const KnownFPClass &Squared, const KnownFPClass &Addend,
DenormalMode Mode = DenormalMode::getDynamic());
/// Report known values for exp, exp2 and exp10.
LLVM_ABI static KnownFPClass exp(const KnownFPClass &Src);
/// Report known values for sin
LLVM_ABI static KnownFPClass sin(const KnownFPClass &Src);
/// Report known values for cos
LLVM_ABI static KnownFPClass cos(const KnownFPClass &Src);
/// Report known values for tan
LLVM_ABI static KnownFPClass tan(const KnownFPClass &Src);
/// Report known values for sinh
LLVM_ABI static KnownFPClass sinh(const KnownFPClass &Src);
/// Report known values for cosh
LLVM_ABI static KnownFPClass cosh(const KnownFPClass &Src);
/// Report known values for tanh
LLVM_ABI static KnownFPClass tanh(const KnownFPClass &Src);
/// Report known values for asin
LLVM_ABI static KnownFPClass asin(const KnownFPClass &Src);
/// Report known values for acos
LLVM_ABI static KnownFPClass acos(const KnownFPClass &Src);
/// Report known values for atan
LLVM_ABI static KnownFPClass atan(const KnownFPClass &Src);
/// Report known values for atan2
LLVM_ABI static KnownFPClass atan2(const KnownFPClass &LHS,
const KnownFPClass &RHS);
/// 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);
}
static KnownFPClass copysign(const KnownFPClass &KnownMag,
const KnownFPClass &KnownSign) {
KnownFPClass Known = KnownMag;
Known.copysign(KnownSign);
return Known;
}
// 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 for operations whose result sign is the xor of the
// operand signs, such as multiply and divide. This only rules out possible
// non-NaN sign classes. NaNs do not have a constrained sign class here.
void propagateXorSign(const KnownFPClass &LHS, const KnownFPClass &RHS) {
if ((LHS.isKnownNever(fcNegative) && RHS.isKnownNever(fcNegative)) ||
(LHS.isKnownNever(fcPositive) && RHS.isKnownNever(fcPositive)))
knownNot(fcNegative);
if ((LHS.isKnownNever(fcPositive) && RHS.isKnownNever(fcNegative)) ||
(LHS.isKnownNever(fcNegative) && RHS.isKnownNever(fcPositive)))
knownNot(fcPositive);
}
/// 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.
LLVM_ABI void propagateDenormal(const KnownFPClass &Src, DenormalMode Mode);
/// 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.
LLVM_ABI void propagateCanonicalizingSrc(const KnownFPClass &Src,
DenormalMode Mode);
/// Propagate known class for log/log2/log10
static LLVM_ABI KnownFPClass
log(const KnownFPClass &Src, DenormalMode Mode = DenormalMode::getDynamic());
/// Propagate known class for sqrt
static LLVM_ABI KnownFPClass
sqrt(const KnownFPClass &Src, DenormalMode Mode = DenormalMode::getDynamic());
/// Propagate known class for fpext.
static LLVM_ABI KnownFPClass fpext(const KnownFPClass &KnownSrc,
const fltSemantics &DstTy,
const fltSemantics &SrcTy);
/// Propagate known class for fptrunc.
static LLVM_ABI KnownFPClass fptrunc(const KnownFPClass &KnownSrc);
/// Propagate known class for rounding intrinsics (trunc, floor, ceil, rint,
/// nearbyint, round, roundeven). This is trunc if \p IsTrunc. \p
/// IsMultiUnitFPType if this is for a multi-unit floating-point type.
static LLVM_ABI KnownFPClass roundToIntegral(const KnownFPClass &Src,
bool IsTrunc,
bool IsMultiUnitFPType);
/// Propagate known class for mantissa component of frexp
static LLVM_ABI KnownFPClass frexp_mant(
const KnownFPClass &Src, DenormalMode Mode = DenormalMode::getDynamic());
/// Propagate known class for ldexp
static LLVM_ABI KnownFPClass
ldexp(const KnownFPClass &Src, const KnownBits &N, const fltSemantics &Flt,
DenormalMode Mode = DenormalMode::getDynamic());
/// Propagate known class for powi
static LLVM_ABI KnownFPClass powi(const KnownFPClass &Src,
const KnownBits &N);
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);
}
} // namespace llvm
#endif