476 lines
16 KiB
C++
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
|