| //===- llvm/Support/KnownFPClass.h - Stores known fplcass -------*- 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. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/Support/KnownFPClass.h" |
| #include "llvm/ADT/APFloat.h" |
| #include "llvm/Support/ErrorHandling.h" |
| #include "llvm/Support/KnownBits.h" |
| |
| using namespace llvm; |
| |
| KnownFPClass::KnownFPClass(const APFloat &C) |
| : KnownFPClassesValue(C.classify()) { |
| setSignBit(C.isNegative()); |
| } |
| |
| /// Return true if it's possible to assume IEEE treatment of input denormals in |
| /// \p F for \p Val. |
| static bool inputDenormalIsIEEE(DenormalMode Mode) { |
| return Mode.Input == DenormalMode::IEEE; |
| } |
| |
| static bool inputDenormalIsIEEEOrPosZero(DenormalMode Mode) { |
| return Mode.Input == DenormalMode::IEEE || |
| Mode.Input == DenormalMode::PositiveZero; |
| } |
| |
| bool KnownFPClass::isKnownNeverLogicalZero(DenormalMode Mode) const { |
| return isKnownNeverZero() && |
| (isKnownNeverSubnormal() || inputDenormalIsIEEE(Mode)); |
| } |
| |
| bool KnownFPClass::isKnownNeverLogicalNegZero(DenormalMode Mode) const { |
| return isKnownNeverNegZero() && |
| (isKnownNeverNegSubnormal() || inputDenormalIsIEEEOrPosZero(Mode)); |
| } |
| |
| bool KnownFPClass::isKnownNeverLogicalPosZero(DenormalMode Mode) const { |
| if (!isKnownNeverPosZero()) |
| return false; |
| |
| // If we know there are no denormals, nothing can be flushed to zero. |
| if (isKnownNeverSubnormal()) |
| return true; |
| |
| switch (Mode.Input) { |
| case DenormalMode::IEEE: |
| return true; |
| case DenormalMode::PreserveSign: |
| // Negative subnormal won't flush to +0 |
| return isKnownNeverPosSubnormal(); |
| case DenormalMode::PositiveZero: |
| default: |
| // Both positive and negative subnormal could flush to +0 |
| return false; |
| } |
| |
| llvm_unreachable("covered switch over denormal mode"); |
| } |
| |
| void KnownFPClass::propagateDenormal(const KnownFPClass &Src, |
| DenormalMode Mode) { |
| setKnownFPClasses(Src.getKnownFPClasses()); |
| // If we aren't assuming the source can't be a zero, we don't have to check if |
| // a denormal input could be flushed. |
| if (!Src.isKnownNeverPosZero() && !Src.isKnownNeverNegZero()) |
| return; |
| |
| // If we know the input can't be a denormal, it can't be flushed to 0. |
| if (Src.isKnownNeverSubnormal()) |
| return; |
| |
| if (!Src.isKnownNeverPosSubnormal() && Mode != DenormalMode::getIEEE()) |
| setKnownFPClasses(getKnownFPClasses() | fcPosZero); |
| |
| if (!Src.isKnownNeverNegSubnormal() && Mode != DenormalMode::getIEEE()) { |
| if (Mode != DenormalMode::getPositiveZero()) |
| setKnownFPClasses(getKnownFPClasses() | fcNegZero); |
| |
| if (Mode.Input == DenormalMode::PositiveZero || |
| Mode.Output == DenormalMode::PositiveZero || |
| Mode.Input == DenormalMode::Dynamic || |
| Mode.Output == DenormalMode::Dynamic) |
| setKnownFPClasses(getKnownFPClasses() | fcPosZero); |
| } |
| } |
| |
| KnownFPClass KnownFPClass::minMaxLike(const KnownFPClass &LHS_, |
| const KnownFPClass &RHS_, MinMaxKind Kind, |
| DenormalMode Mode) { |
| KnownFPClass KnownLHS = LHS_; |
| KnownFPClass KnownRHS = RHS_; |
| |
| bool NeverNaN = KnownLHS.isKnownNeverNaN() || KnownRHS.isKnownNeverNaN(); |
| KnownFPClass Known = KnownLHS | KnownRHS; |
| |
| // If either operand is not NaN, the result is not NaN. |
| if (NeverNaN && |
| (Kind == MinMaxKind::minnum || Kind == MinMaxKind::maxnum || |
| Kind == MinMaxKind::minimumnum || Kind == MinMaxKind::maximumnum)) |
| Known.knownNot(fcNan); |
| |
| if (Kind == MinMaxKind::maxnum || Kind == MinMaxKind::maximumnum) { |
| if (KnownLHS.isKnownNeverNaN()) |
| Known.knownNot(orderedStrictlyLess(KnownLHS.getKnownFPClasses())); |
| if (KnownRHS.isKnownNeverNaN()) |
| Known.knownNot(orderedStrictlyLess(KnownRHS.getKnownFPClasses())); |
| } else if (Kind == MinMaxKind::maximum) { |
| Known.knownNot(orderedStrictlyLess(KnownLHS.getKnownFPClasses()) | |
| orderedStrictlyLess(KnownRHS.getKnownFPClasses())); |
| } else if (Kind == MinMaxKind::minnum || Kind == MinMaxKind::minimumnum) { |
| if (KnownLHS.isKnownNeverNaN()) |
| Known.knownNot(orderedStrictlyGreater(KnownLHS.getKnownFPClasses())); |
| if (KnownRHS.isKnownNeverNaN()) |
| Known.knownNot(orderedStrictlyGreater(KnownRHS.getKnownFPClasses())); |
| } else if (Kind == MinMaxKind::minimum) { |
| Known.knownNot(orderedStrictlyGreater(KnownLHS.getKnownFPClasses()) | |
| orderedStrictlyGreater(KnownRHS.getKnownFPClasses())); |
| } else |
| llvm_unreachable("unhandled intrinsic"); |
| |
| // Fixup zero handling if denormals could be returned as a zero. |
| // |
| // As there's no spec for denormal flushing, be conservative with the |
| // treatment of denormals that could be flushed to zero. For older |
| // subtargets on AMDGPU the min/max instructions would not flush the |
| // output and return the original value. |
| // |
| if ((Known.getKnownFPClasses() & fcZero) != fcNone && |
| !Known.isKnownNeverSubnormal()) { |
| if (Mode != DenormalMode::getIEEE()) |
| Known.setKnownFPClasses(Known.getKnownFPClasses() | fcZero); |
| } |
| |
| if (Known.isKnownNeverNaN()) { |
| if (KnownLHS.getSignBit() && KnownRHS.getSignBit() && |
| *KnownLHS.getSignBit() == *KnownRHS.getSignBit()) { |
| if (*KnownLHS.getSignBit()) |
| Known.signBitMustBeOne(); |
| else |
| Known.signBitMustBeZero(); |
| } else if ((Kind == MinMaxKind::maximum || Kind == MinMaxKind::minimum || |
| Kind == MinMaxKind::maximumnum || |
| Kind == MinMaxKind::minimumnum) || |
| // FIXME: Should be using logical zero versions |
| ((KnownLHS.isKnownNeverNegZero() || |
| KnownRHS.isKnownNeverPosZero()) && |
| (KnownLHS.isKnownNeverPosZero() || |
| KnownRHS.isKnownNeverNegZero()))) { |
| // Don't take sign bit from NaN operands. |
| if (!KnownLHS.isKnownNeverNaN()) |
| KnownLHS.setSignBit(std::nullopt); |
| if (!KnownRHS.isKnownNeverNaN()) |
| KnownRHS.setSignBit(std::nullopt); |
| if ((Kind == MinMaxKind::maximum || Kind == MinMaxKind::maximumnum || |
| Kind == MinMaxKind::maxnum) && |
| (KnownLHS.getSignBit() == false || KnownRHS.getSignBit() == false)) |
| Known.signBitMustBeZero(); |
| else if ((Kind == MinMaxKind::minimum || Kind == MinMaxKind::minimumnum || |
| Kind == MinMaxKind::minnum) && |
| (KnownLHS.getSignBit() == true || KnownRHS.getSignBit() == true)) |
| Known.signBitMustBeOne(); |
| } |
| } |
| |
| return Known; |
| } |
| |
| KnownFPClass KnownFPClass::canonicalize(const KnownFPClass &KnownSrc, |
| DenormalMode DenormMode) { |
| KnownFPClass Known; |
| |
| // This is essentially a stronger form of |
| // propagateCanonicalizingSrc. Other "canonicalizing" operations don't |
| // actually have an IR canonicalization guarantee. |
| |
| // Canonicalize may flush denormals to zero, so we have to consider the |
| // denormal mode to preserve known-not-0 knowledge. |
| Known.setKnownFPClasses(KnownSrc.getKnownFPClasses() | fcZero | fcQNan); |
| |
| // Stronger version of propagateNaN |
| // Canonicalize is guaranteed to quiet signaling nans. |
| if (KnownSrc.isKnownNeverNaN()) |
| Known.knownNot(fcNan); |
| else |
| Known.knownNot(fcSNan); |
| |
| // FIXME: Missing check of IEEE like types. |
| |
| // If the parent function flushes denormals, the canonical output cannot be a |
| // denormal. |
| if (DenormMode == DenormalMode::getIEEE()) { |
| if (KnownSrc.isKnownNever(fcPosZero)) |
| Known.knownNot(fcPosZero); |
| if (KnownSrc.isKnownNever(fcNegZero)) |
| Known.knownNot(fcNegZero); |
| return Known; |
| } |
| |
| if (DenormMode.inputsAreZero() || DenormMode.outputsAreZero()) |
| Known.knownNot(fcSubnormal); |
| |
| if (DenormMode == DenormalMode::getPreserveSign()) { |
| if (KnownSrc.isKnownNever(fcPosZero | fcPosSubnormal)) |
| Known.knownNot(fcPosZero); |
| if (KnownSrc.isKnownNever(fcNegZero | fcNegSubnormal)) |
| Known.knownNot(fcNegZero); |
| return Known; |
| } |
| |
| if (DenormMode.Input == DenormalMode::PositiveZero || |
| (DenormMode.Output == DenormalMode::PositiveZero && |
| DenormMode.Input == DenormalMode::IEEE)) { |
| // -0.0 is not a subnormal and should not be flushed. |
| if (KnownSrc.isKnownNever(fcNegZero)) |
| Known.knownNot(fcNegZero); |
| |
| if (KnownSrc.isKnownNever(fcPosZero | fcSubnormal)) |
| Known.knownNot(fcPosZero); |
| } |
| |
| return Known; |
| } |
| |
| KnownFPClass KnownFPClass::bitcast(const fltSemantics &FltSemantics, |
| const KnownBits &Bits) { |
| assert(FltSemantics.sizeInBits == Bits.getBitWidth() && |
| "Bitcast operand has incorrect bit width"); |
| KnownFPClass Known; |
| |
| // Conflicting known bits do not describe a concrete value. Return unknown. |
| if (Bits.hasConflict()) |
| return Known; |
| |
| // Transfer information from the sign bit. |
| if (Bits.isNonNegative()) |
| Known.signBitMustBeZero(); |
| else if (Bits.isNegative()) |
| Known.signBitMustBeOne(); |
| |
| if (APFloat::isIEEELikeFP(FltSemantics)) { |
| const unsigned MantissaBits = FltSemantics.precision - 1; |
| const APInt ExponentMask = APInt::getBitsSet( |
| FltSemantics.sizeInBits, MantissaBits, FltSemantics.sizeInBits - 1); |
| const APInt MantissaMask = |
| APInt::getLowBitsSet(FltSemantics.sizeInBits, MantissaBits); |
| |
| const bool ExponentKnownAllZeros = |
| (Bits.Zero & ExponentMask) == ExponentMask; |
| const bool ExponentKnownAllOnes = (Bits.One & ExponentMask) == ExponentMask; |
| const bool ExponentKnownNotAllZeros = !(Bits.One & ExponentMask).isZero(); |
| const bool ExponentKnownNotAllOnes = !(Bits.Zero & ExponentMask).isZero(); |
| |
| const bool MantissaKnownAllZeros = |
| (Bits.Zero & MantissaMask) == MantissaMask; |
| const bool MantissaKnownNotAllZeros = !(Bits.One & MantissaMask).isZero(); |
| |
| // Zero and subnormal require an exponent with all zero bits. |
| if (ExponentKnownNotAllZeros) |
| Known.knownNot(fcZero | fcSubnormal); |
| |
| // Infinity and NaN require an exponent with all one bits. |
| if (ExponentKnownNotAllOnes) |
| Known.knownNot(fcInf | fcNan); |
| |
| // Normal values have an exponent that is not all zeros or all ones. |
| if (ExponentKnownAllZeros || ExponentKnownAllOnes) |
| Known.knownNot(fcNormal); |
| |
| // Zero and infinity require a mantissa with all zero bits. |
| if (MantissaKnownNotAllZeros) |
| Known.knownNot(fcZero | fcInf); |
| |
| // Subnormal and NaN require a non-zero mantissa. |
| if (MantissaKnownAllZeros) |
| Known.knownNot(fcSubnormal | fcNan); |
| |
| const bool QuietBitKnownSet = Bits.One[MantissaBits - 1]; |
| const bool QuietBitKnownClear = Bits.Zero[MantissaBits - 1]; |
| |
| if (QuietBitKnownSet) |
| Known.knownNot(fcSNan); |
| else if (QuietBitKnownClear) |
| Known.knownNot(fcQNan); |
| } |
| |
| return Known; |
| } |
| |
| KnownBits KnownFPClass::toKnownBits(const fltSemantics &FltSemantics) const { |
| KnownBits Known(FltSemantics.sizeInBits); |
| const FPClassTest FPClasses = getKnownFPClasses(); |
| |
| // Return unknown if poison. |
| if (FPClasses == fcNone) |
| return Known; |
| |
| if (isKnownNever(fcNormal | fcSubnormal | fcNan)) { |
| Known.setAllConflict(); |
| |
| if (FPClasses & fcInf) |
| Known = Known.intersectWith(KnownBits::makeConstant( |
| APFloat::getInf(FltSemantics).bitcastToAPInt())); |
| |
| if (FPClasses & fcZero) |
| Known = Known.intersectWith( |
| KnownBits::makeConstant(APInt::getZero(FltSemantics.sizeInBits))); |
| |
| Known.Zero.clearSignBit(); |
| Known.One.clearSignBit(); |
| } |
| |
| if (std::optional<bool> Sign = getSignBit()) { |
| if (*Sign) |
| Known.makeNegative(); |
| else |
| Known.makeNonNegative(); |
| } |
| |
| return Known; |
| } |
| |
| // Handle known sign bit and nan cases for fadd. |
| static KnownFPClass fadd_impl(const KnownFPClass &KnownLHS, |
| const KnownFPClass &KnownRHS, DenormalMode Mode) { |
| KnownFPClass Known; |
| |
| // Adding positive and negative infinity produces NaN, but only if both |
| // opposite-sign infinity combinations are possible. |
| if (KnownLHS.isKnownNeverNaN() && KnownRHS.isKnownNeverNaN() && |
| (KnownLHS.isKnownNever(fcPosInf) || KnownRHS.isKnownNever(fcNegInf)) && |
| (KnownLHS.isKnownNever(fcNegInf) || KnownRHS.isKnownNever(fcPosInf))) |
| Known.knownNot(fcNan); |
| |
| if (KnownLHS.cannotBeOrderedLessThanZero() && |
| KnownRHS.cannotBeOrderedLessThanZero()) { |
| Known.knownNot(KnownFPClass::OrderedLessThanZeroMask); |
| |
| // This can't underflow if one of the operands is known normal. |
| if (KnownLHS.isKnownNever(fcZero | fcPosSubnormal) || |
| KnownRHS.isKnownNever(fcZero | fcPosSubnormal)) |
| Known.knownNot(fcZero | fcPosSubnormal); |
| } |
| |
| if (KnownLHS.cannotBeOrderedGreaterThanZero() && |
| KnownRHS.cannotBeOrderedGreaterThanZero()) { |
| Known.knownNot(KnownFPClass::OrderedGreaterThanZeroMask); |
| |
| // This can't underflow if one of the operands is known normal. |
| if (KnownLHS.isKnownNever(fcZero | fcNegSubnormal) || |
| KnownRHS.isKnownNever(fcZero | fcNegSubnormal)) |
| Known.knownNot(fcZero | fcNegSubnormal); |
| } |
| |
| return Known; |
| } |
| |
| KnownFPClass KnownFPClass::fadd(const KnownFPClass &KnownLHS, |
| const KnownFPClass &KnownRHS, |
| DenormalMode Mode) { |
| KnownFPClass Known = fadd_impl(KnownLHS, KnownRHS, Mode); |
| |
| // (fadd x, 0.0) is guaranteed to return +0.0, not -0.0. |
| if ((KnownLHS.isKnownNeverLogicalNegZero(Mode) || |
| KnownRHS.isKnownNeverLogicalNegZero(Mode)) && |
| // Make sure output negative denormal can't flush to -0 |
| (Mode.Output == DenormalMode::IEEE || |
| Mode.Output == DenormalMode::PositiveZero)) |
| Known.knownNot(fcNegZero); |
| |
| return Known; |
| } |
| |
| KnownFPClass KnownFPClass::fadd_self(const KnownFPClass &KnownSrc, |
| DenormalMode Mode) { |
| KnownFPClass Known = fadd(KnownSrc, KnownSrc, Mode); |
| |
| // Doubling 0 will give the same 0. |
| if (KnownSrc.isKnownNeverLogicalPosZero(Mode) && |
| (Mode.Output == DenormalMode::IEEE || |
| (Mode.Output == DenormalMode::PreserveSign && |
| KnownSrc.isKnownNeverPosSubnormal()) || |
| (Mode.Output == DenormalMode::PositiveZero && |
| KnownSrc.isKnownNeverSubnormal()))) |
| Known.knownNot(fcPosZero); |
| |
| return Known; |
| } |
| |
| KnownFPClass KnownFPClass::fsub(const KnownFPClass &KnownLHS, |
| const KnownFPClass &KnownRHS, |
| DenormalMode Mode) { |
| return fadd(KnownLHS, fneg(KnownRHS), Mode); |
| } |
| |
| KnownFPClass KnownFPClass::fmul(const KnownFPClass &KnownLHS, |
| const KnownFPClass &KnownRHS, |
| DenormalMode Mode) { |
| KnownFPClass Known; |
| |
| // +X * +Y or -X * -Y => +Q |
| // +X * -Y or -X * +Y => -Q |
| Known.propagateXorSign(KnownLHS, KnownRHS); |
| |
| // Inf * Y => Inf or NaN |
| if (KnownLHS.isKnownAlways(fcInf | fcNan) || |
| KnownRHS.isKnownAlways(fcInf | fcNan)) |
| Known.knownNot(fcNormal | fcSubnormal | fcZero); |
| |
| // 0 * Y => 0 or NaN |
| if (KnownRHS.isKnownAlways(fcZero | fcNan) || |
| KnownLHS.isKnownAlways(fcZero | fcNan)) |
| Known.knownNot(fcNormal | fcSubnormal | fcInf); |
| |
| if (!KnownLHS.isKnownNeverNaN() || !KnownRHS.isKnownNeverNaN()) |
| return Known; |
| |
| // 0 * +/-inf => NaN |
| if ((KnownRHS.isKnownNeverInfinity() || |
| KnownLHS.isKnownNeverLogicalZero(Mode)) && |
| (KnownLHS.isKnownNeverInfinity() || |
| KnownRHS.isKnownNeverLogicalZero(Mode))) |
| Known.knownNot(fcNan); |
| |
| return Known; |
| } |
| |
| // TODO: This generalizes to known ranges |
| KnownFPClass KnownFPClass::fmul(const KnownFPClass &KnownLHS, |
| const APFloat &CRHS, DenormalMode Mode) { |
| // Match denormal scaling pattern, similar to the case in ldexp. If the |
| // constant's exponent is sufficiently large, the result cannot be subnormal. |
| |
| const fltSemantics &Flt = CRHS.getSemantics(); |
| unsigned Precision = APFloat::semanticsPrecision(Flt); |
| const int MantissaBits = Precision - 1; |
| |
| int MinKnownExponent = ilogb(CRHS); |
| bool CannotBeSubnormal = (MinKnownExponent >= MantissaBits); |
| |
| KnownFPClass Known = KnownFPClass::fmul(KnownLHS, KnownFPClass(CRHS), Mode); |
| if (CannotBeSubnormal) |
| Known.knownNot(fcSubnormal); |
| |
| // Multiply of values <= 1 cannot introduce overflow. |
| if (KnownLHS.isKnownNever(fcInf)) { |
| if (MinKnownExponent < 0) |
| Known.knownNot(fcInf); |
| else if (MinKnownExponent == 0 && CRHS.compareAbsoluteValue(APFloat::getOne( |
| Flt)) == APFloat::cmpEqual) |
| Known.knownNot(fcInf); |
| } |
| |
| return Known; |
| } |
| |
| KnownFPClass KnownFPClass::fdiv(const KnownFPClass &KnownLHS, |
| const KnownFPClass &KnownRHS, |
| DenormalMode Mode) { |
| KnownFPClass Known; |
| |
| // Only 0/0, Inf/Inf produce NaN. |
| if (KnownLHS.isKnownNeverNaN() && KnownRHS.isKnownNeverNaN() && |
| (KnownLHS.isKnownNeverInfinity() || KnownRHS.isKnownNeverInfinity()) && |
| (KnownLHS.isKnownNeverLogicalZero(Mode) || |
| KnownRHS.isKnownNeverLogicalZero(Mode))) { |
| Known.knownNot(fcNan); |
| } |
| |
| // X / -0.0 => -Inf (or NaN) |
| // +X / +Y or -X / -Y => +Q |
| // +X / -Y or -X / +Y => -Q |
| Known.propagateXorSign(KnownLHS, KnownRHS); |
| |
| // Normal and subnormal results require two non-zero finite operands. |
| if ((KnownLHS.isKnownNever(fcNegNormal | fcNegSubnormal) && |
| KnownRHS.isKnownNever(fcNegNormal | fcNegSubnormal)) || |
| (KnownLHS.isKnownNever(fcPosNormal | fcPosSubnormal) && |
| KnownRHS.isKnownNever(fcPosNormal | fcPosSubnormal))) |
| Known.knownNot(fcNegNormal | fcNegSubnormal); |
| if ((KnownLHS.isKnownNever(fcNegNormal | fcNegSubnormal) && |
| KnownRHS.isKnownNever(fcPosNormal | fcPosSubnormal)) || |
| (KnownLHS.isKnownNever(fcPosNormal | fcPosSubnormal) && |
| KnownRHS.isKnownNever(fcNegNormal | fcNegSubnormal))) |
| Known.knownNot(fcPosNormal | fcPosSubnormal); |
| |
| // 0 / X => 0 or NaN |
| if (KnownLHS.isKnownAlways(fcZero)) |
| Known.knownNot(fcSubnormal | fcNormal | fcInf); |
| |
| // X / 0 => NaN or Inf |
| if (KnownRHS.isKnownAlways(fcZero)) |
| Known.knownNot(fcFinite); |
| |
| return Known; |
| } |
| |
| KnownFPClass KnownFPClass::fdiv_self(const KnownFPClass &KnownSrc, |
| DenormalMode Mode) { |
| // X / X is always exactly 1.0 or a NaN. |
| KnownFPClass Known(fcNan | fcPosNormal); |
| |
| if (KnownSrc.isKnownNeverInfOrNaN() && KnownSrc.isKnownNeverLogicalZero(Mode)) |
| Known.knownNot(fcNan); |
| else if (KnownSrc.isKnownNever(fcSNan)) |
| Known.knownNot(fcSNan); |
| |
| return Known; |
| } |
| |
| KnownFPClass KnownFPClass::frem(const KnownFPClass &KnownLHS, |
| const KnownFPClass &KnownRHS, |
| DenormalMode Mode) { |
| KnownFPClass Known; |
| |
| Known.knownNot(fcInf); |
| |
| // Inf REM x and x REM 0 produce NaN. |
| if (KnownLHS.isKnownNeverNaN() && KnownRHS.isKnownNeverNaN() && |
| KnownLHS.isKnownNeverInfinity() && |
| KnownRHS.isKnownNeverLogicalZero(Mode)) { |
| Known.knownNot(fcNan); |
| } |
| |
| // The sign for frem is the same as the first operand. |
| if (KnownLHS.cannotBeOrderedLessThanZero()) |
| Known.knownNot(KnownFPClass::OrderedLessThanZeroMask); |
| if (KnownLHS.cannotBeOrderedGreaterThanZero()) |
| Known.knownNot(KnownFPClass::OrderedGreaterThanZeroMask); |
| |
| // See if we can be more aggressive about the sign of 0. |
| if (KnownLHS.isKnownNever(fcNegative)) |
| Known.knownNot(fcNegative); |
| if (KnownLHS.isKnownNever(fcPositive)) |
| Known.knownNot(fcPositive); |
| |
| return Known; |
| } |
| |
| KnownFPClass KnownFPClass::frem_self(const KnownFPClass &KnownSrc, |
| DenormalMode Mode) { |
| // X % X is always exactly [+-]0.0 or a NaN. |
| KnownFPClass Known(fcNan | fcZero); |
| |
| if (KnownSrc.isKnownNeverInfOrNaN() && KnownSrc.isKnownNeverLogicalZero(Mode)) |
| Known.knownNot(fcNan); |
| else if (KnownSrc.isKnownNever(fcSNan)) |
| Known.knownNot(fcSNan); |
| |
| return Known; |
| } |
| |
| KnownFPClass KnownFPClass::fma(const KnownFPClass &KnownLHS, |
| const KnownFPClass &KnownRHS, |
| const KnownFPClass &KnownAddend, |
| DenormalMode Mode) { |
| KnownFPClass Mul = fmul(KnownLHS, KnownRHS, Mode); |
| |
| // FMA differs from the base fmul + fadd handling only in the treatment of -0 |
| // results. |
| // |
| // If the multiply is a -0 due to rounding, the final -0 + 0 will be -0, |
| // unlike for a separate fadd. |
| return fadd_impl(Mul, KnownAddend, Mode); |
| } |
| |
| KnownFPClass KnownFPClass::fma_square(const KnownFPClass &KnownSquared, |
| const KnownFPClass &KnownAddend, |
| DenormalMode Mode) { |
| KnownFPClass Squared = square(KnownSquared, Mode); |
| KnownFPClass Known = fadd_impl(Squared, KnownAddend, Mode); |
| |
| // Since we know the squared input must be positive, the add of opposite sign |
| // infinities nan hazard only applies for negative inf. |
| // |
| // TODO: Alternatively to proving addend is not -inf, we could know Squared is |
| // not pinf. Other than the degenerate always-subnormal input case, we can't |
| // prove that without a known range. |
| if (KnownAddend.isKnownNever(fcNegInf | fcNan) && Squared.isKnownNever(fcNan)) |
| Known.knownNot(fcNan); |
| |
| return Known; |
| } |
| |
| KnownFPClass KnownFPClass::exp(const KnownFPClass &KnownSrc) { |
| KnownFPClass Known; |
| Known.knownNot(fcNegative); |
| |
| Known.propagateNonNaN(KnownSrc); |
| |
| if (KnownSrc.cannotBeOrderedLessThanZero()) { |
| // If the source is positive this cannot underflow. |
| Known.knownNot(fcPosZero); |
| |
| // Cannot introduce denormal values. |
| Known.knownNot(fcPosSubnormal); |
| } |
| |
| // If the source is negative, this cannot overflow to infinity. |
| if (KnownSrc.cannotBeOrderedGreaterThanZero()) |
| Known.knownNot(fcPosInf); |
| |
| return Known; |
| } |
| |
| void KnownFPClass::propagateCanonicalizingSrc(const KnownFPClass &Src, |
| DenormalMode Mode) { |
| propagateDenormal(Src, Mode); |
| propagateNonNaN(Src); |
| } |
| |
| KnownFPClass KnownFPClass::log(const KnownFPClass &KnownSrc, |
| DenormalMode Mode) { |
| KnownFPClass Known; |
| Known.knownNot(fcNegZero | fcSubnormal); |
| |
| Known.propagateNonSNaN(KnownSrc); |
| |
| if (KnownSrc.isKnownNeverPosInfinity()) |
| Known.knownNot(fcPosInf); |
| |
| if (KnownSrc.isKnownNeverNaN() && KnownSrc.cannotBeOrderedLessThanZero()) |
| Known.knownNot(fcNan); |
| |
| if (KnownSrc.isKnownNeverLogicalZero(Mode)) |
| Known.knownNot(fcNegInf); |
| |
| return Known; |
| } |
| |
| KnownFPClass KnownFPClass::sqrt(const KnownFPClass &KnownSrc, |
| DenormalMode Mode) { |
| KnownFPClass Known; |
| Known.knownNot(fcPosSubnormal); |
| |
| if (KnownSrc.isKnownNeverPosInfinity()) |
| Known.knownNot(fcPosInf); |
| |
| Known.propagateNonSNaN(KnownSrc); |
| |
| // Any negative value besides -0 returns a nan. |
| if (KnownSrc.isKnownNeverNaN() && KnownSrc.cannotBeOrderedLessThanZero()) |
| Known.knownNot(fcNan); |
| |
| // The only negative value that can be returned is -0 for -0 inputs. |
| Known.knownNot(fcNegInf | fcNegSubnormal | fcNegNormal); |
| |
| // If the input denormal mode could be PreserveSign, a negative |
| // subnormal input could produce a negative zero output. |
| if (KnownSrc.isKnownNeverLogicalNegZero(Mode)) |
| Known.knownNot(fcNegZero); |
| |
| return Known; |
| } |
| |
| KnownFPClass KnownFPClass::sin(const KnownFPClass &KnownSrc) { |
| KnownFPClass Known; |
| |
| // Return NaN on infinite inputs. |
| Known.knownNot(fcInf); |
| if (KnownSrc.isKnownNeverNaN() && KnownSrc.isKnownNeverInfinity()) |
| Known.knownNot(fcNan); |
| |
| return Known; |
| } |
| |
| KnownFPClass KnownFPClass::cos(const KnownFPClass &KnownSrc) { |
| return sin(KnownSrc); |
| } |
| |
| KnownFPClass KnownFPClass::tan(const KnownFPClass &KnownSrc) { |
| KnownFPClass Known; |
| |
| // tan never returns Inf (tan(+-Inf) = NaN; tan(finite) = finite). |
| Known.knownNot(fcInf); |
| |
| // NaN propagates. tan(+-Inf) is NaN. |
| if (KnownSrc.isKnownNeverNaN() && KnownSrc.isKnownNeverInfinity()) |
| Known.knownNot(fcNan); |
| |
| return Known; |
| } |
| |
| KnownFPClass KnownFPClass::sinh(const KnownFPClass &KnownSrc) { |
| KnownFPClass Known; |
| |
| // sinh is sign-preserving: sinh(x) < 0 iff x < 0. |
| if (KnownSrc.isKnownNever(fcNegative)) |
| Known.knownNot(fcNegative); |
| |
| Known.propagateNonNaN(KnownSrc); |
| |
| return Known; |
| } |
| |
| KnownFPClass KnownFPClass::cosh(const KnownFPClass &KnownSrc) { |
| KnownFPClass Known; |
| |
| // cosh(x) >= 1 for all real x; cosh(+-Inf) = +Inf. Never negative, |
| // zero, or subnormal. |
| Known.knownNot(fcNegative | fcZero | fcSubnormal); |
| |
| Known.propagateNonNaN(KnownSrc); |
| |
| return Known; |
| } |
| |
| KnownFPClass KnownFPClass::tanh(const KnownFPClass &KnownSrc) { |
| KnownFPClass Known; |
| |
| // tanh is bounded to (-1, 1), never Inf. |
| Known.knownNot(fcInf); |
| |
| // tanh is sign-preserving: tanh(x) < 0 iff x < 0. |
| if (KnownSrc.isKnownNever(fcNegative)) |
| Known.knownNot(fcNegative); |
| |
| Known.propagateNonNaN(KnownSrc); |
| |
| return Known; |
| } |
| |
| KnownFPClass KnownFPClass::asin(const KnownFPClass &KnownSrc) { |
| KnownFPClass Known; |
| |
| // asin is bounded to [-pi/2, pi/2], never Inf. |
| Known.knownNot(fcInf); |
| |
| Known.propagateNonSNaN(KnownSrc); |
| |
| // asin is sign-preserving for finite arguments. |
| if (KnownSrc.isKnownNever(fcNegFinite)) |
| Known.knownNot(fcNegFinite); |
| |
| // NaN propagates. asin(x) is also NaN for |x| > 1, so we cannot rule |
| // out NaN without knowing the source is in [-1, 1]. |
| return Known; |
| } |
| |
| KnownFPClass KnownFPClass::acos(const KnownFPClass &KnownSrc) { |
| KnownFPClass Known; |
| |
| // acos(x) is bounded to [0, pi] for -1 <= x <= 1, and is never negative, |
| // infinite, or subnormal. The smallest non-zero value occurs when x is |
| // close to 1.0, where acos(x) can be approximated by sqrt(2 * (1 - x)). |
| // Since sqrt cannot produce a subnormal result, we can conclude that |
| // acos(x) will also never produce a subnormal result. |
| Known.knownNot(fcNegative | fcInf | fcSubnormal); |
| |
| // acos(x) == +0.0 iff x == +1.0 |
| if (KnownSrc.isKnownNever(fcPosNormal)) |
| Known.knownNot(fcZero); |
| |
| Known.propagateNonSNaN(KnownSrc); |
| |
| // NaN propagates. acos(x) is also NaN for |x| > 1, so we cannot rule |
| // out NaN without knowing the source is in [-1, 1]. |
| return Known; |
| } |
| |
| KnownFPClass KnownFPClass::atan(const KnownFPClass &KnownSrc) { |
| KnownFPClass Known; |
| |
| // atan is bounded to (-pi/2, pi/2), never Inf. atan(+-Inf) = +-pi/2 (finite). |
| Known.knownNot(fcInf); |
| |
| // atan is sign-preserving: atan(x) < 0 iff x < 0. |
| if (KnownSrc.isKnownNever(fcNegative)) |
| Known.knownNot(fcNegative); |
| |
| Known.propagateNonNaN(KnownSrc); |
| |
| return Known; |
| } |
| |
| KnownFPClass KnownFPClass::atan2(const KnownFPClass &KnownY, |
| const KnownFPClass &KnownX, |
| DenormalMode Mode) { |
| KnownFPClass Known; |
| |
| // Even though these deductions are correct, we are ignoring the following |
| // potentially erroneous cases: |
| // * atan2(y, inf) is not subnormal |
| // * atan2(inf, x) is not zero or subnormal |
| |
| // atan2 result is in (-pi, pi], never Inf. |
| Known.knownNot(fcInf); |
| |
| Known.propagateNonNaN(KnownY, KnownX); |
| |
| // Negative subnormals could be treated like positive zero. |
| const bool XCannotHavePositiveValue = KnownX.isKnownNever(fcPositive) && |
| KnownX.isKnownNeverLogicalPosZero(Mode); |
| |
| // If x <= -0.0, then |atan2(y, x)| >= pi/2 |
| if (XCannotHavePositiveValue) |
| Known.knownNot(fcZero | fcSubnormal); |
| |
| return Known; |
| } |
| |
| KnownFPClass KnownFPClass::fpext(const KnownFPClass &KnownSrc, |
| const fltSemantics &DstTy, |
| const fltSemantics &SrcTy) { |
| // Infinity, nan and zero propagate from source. |
| KnownFPClass Known = KnownSrc; |
| |
| // All subnormal inputs should be in the normal range in the result type. |
| if (APFloat::isRepresentableAsNormalIn(SrcTy, DstTy)) { |
| if (Known.getKnownFPClasses() & fcPosSubnormal) |
| Known.setKnownFPClasses(Known.getKnownFPClasses() | fcPosNormal); |
| if (Known.getKnownFPClasses() & fcNegSubnormal) |
| Known.setKnownFPClasses(Known.getKnownFPClasses() | fcNegNormal); |
| Known.knownNot(fcSubnormal); |
| } |
| |
| // Sign bit of a nan isn't guaranteed. |
| if (!Known.isKnownNeverNaN()) |
| Known.setSignBit(std::nullopt); |
| |
| return Known; |
| } |
| |
| KnownFPClass KnownFPClass::fptrunc(const KnownFPClass &KnownSrc) { |
| KnownFPClass Known; |
| |
| // Sign should be preserved |
| // TODO: Handle cannot be ordered greater than zero |
| if (KnownSrc.cannotBeOrderedLessThanZero()) |
| Known.knownNot(KnownFPClass::OrderedLessThanZeroMask); |
| |
| Known.propagateNonNaN(KnownSrc); |
| |
| // Infinity needs a range check. |
| return Known; |
| } |
| |
| KnownFPClass KnownFPClass::roundToIntegral(const KnownFPClass &KnownSrc, |
| bool IsTrunc, |
| bool IsMultiUnitFPType) { |
| KnownFPClass Known; |
| |
| // Integer results cannot be subnormal. |
| Known.knownNot(fcSubnormal); |
| |
| Known.propagateNonNaN(KnownSrc); |
| |
| // Pass through infinities, except PPC_FP128 is a special case for |
| // intrinsics other than trunc. |
| if (IsTrunc || !IsMultiUnitFPType) { |
| if (KnownSrc.isKnownNeverPosInfinity()) |
| Known.knownNot(fcPosInf); |
| if (KnownSrc.isKnownNeverNegInfinity()) |
| Known.knownNot(fcNegInf); |
| } |
| |
| // Negative round ups to 0 produce -0 |
| if (KnownSrc.isKnownNever(fcPosFinite)) |
| Known.knownNot(fcPosFinite); |
| if (KnownSrc.isKnownNever(fcNegFinite)) |
| Known.knownNot(fcNegFinite); |
| |
| return Known; |
| } |
| |
| KnownFPClass KnownFPClass::frexp_mant(const KnownFPClass &KnownSrc, |
| DenormalMode Mode) { |
| KnownFPClass Known; |
| Known.knownNot(fcSubnormal); |
| |
| if (KnownSrc.isKnownNever(fcNegative)) |
| Known.knownNot(fcNegative); |
| else { |
| if (KnownSrc.isKnownNeverLogicalNegZero(Mode)) |
| Known.knownNot(fcNegZero); |
| if (KnownSrc.isKnownNever(fcNegInf)) |
| Known.knownNot(fcNegInf); |
| } |
| |
| if (KnownSrc.isKnownNever(fcPositive)) |
| Known.knownNot(fcPositive); |
| else { |
| if (KnownSrc.isKnownNeverLogicalPosZero(Mode)) |
| Known.knownNot(fcPosZero); |
| if (KnownSrc.isKnownNever(fcPosInf)) |
| Known.knownNot(fcPosInf); |
| } |
| |
| Known.propagateNonNaN(KnownSrc); |
| return Known; |
| } |
| |
| KnownFPClass KnownFPClass::ldexp(const KnownFPClass &KnownSrc, |
| const APInt &ConstantRangeExpMin, |
| const APInt &ConstantRangeExpMax, |
| const fltSemantics &Flt, DenormalMode Mode) { |
| KnownFPClass Known; |
| Known.propagateNonNaN(KnownSrc); |
| |
| // Sign is preserved, but underflows may produce zeroes. |
| if (KnownSrc.isKnownNever(fcNegative)) |
| Known.knownNot(fcNegative); |
| else if (KnownSrc.cannotBeOrderedLessThanZero()) |
| Known.knownNot(OrderedLessThanZeroMask); |
| |
| if (KnownSrc.isKnownNever(fcPositive)) |
| Known.knownNot(fcPositive); |
| else if (KnownSrc.cannotBeOrderedGreaterThanZero()) |
| Known.knownNot(OrderedGreaterThanZeroMask); |
| |
| unsigned Precision = APFloat::semanticsPrecision(Flt); |
| const int MantissaBits = Precision - 1; |
| if (ConstantRangeExpMin.sge(MantissaBits)) |
| Known.knownNot(fcSubnormal); |
| |
| if (ConstantRangeExpMin.isZero() && ConstantRangeExpMax.isZero()) { |
| // ldexp(x, 0) -> x, so propagate everything. |
| Known.propagateCanonicalizingSrc(KnownSrc, Mode); |
| } else if (ConstantRangeExpMax.isNonPositive()) { |
| // If we know the power is <= 0, can't introduce inf |
| if (KnownSrc.isKnownNeverPosInfinity()) |
| Known.knownNot(fcPosInf); |
| if (KnownSrc.isKnownNeverNegInfinity()) |
| Known.knownNot(fcNegInf); |
| } else if (ConstantRangeExpMin.isNonNegative()) { |
| // If we know the power is >= 0, can't introduce subnormal or zero |
| if (KnownSrc.isKnownNeverPosSubnormal()) |
| Known.knownNot(fcPosSubnormal); |
| if (KnownSrc.isKnownNeverNegSubnormal()) |
| Known.knownNot(fcNegSubnormal); |
| if (KnownSrc.isKnownNeverLogicalPosZero(Mode)) |
| Known.knownNot(fcPosZero); |
| if (KnownSrc.isKnownNeverLogicalNegZero(Mode)) |
| Known.knownNot(fcNegZero); |
| } |
| |
| return Known; |
| } |
| |
| KnownFPClass KnownFPClass::ldexp(const KnownFPClass &KnownSrc, |
| const KnownBits &ExpBits, |
| const fltSemantics &Flt, DenormalMode Mode) { |
| return ldexp(KnownSrc, ExpBits.getSignedMinValue(), |
| ExpBits.getSignedMaxValue(), Flt, Mode); |
| } |
| |
| KnownFPClass KnownFPClass::pow(const KnownFPClass &KnownLHS, |
| const KnownFPClass &KnownRHS) { |
| KnownFPClass Known; |
| |
| Known.propagateNonSNaN(KnownLHS, KnownRHS); |
| |
| // pow may return NaN if one of the arguments is NaN. NaN may be produced from |
| // a non-zero-finite-negative base and a non-integer exponent. |
| if (KnownLHS.isKnownNever(fcNan | fcNegNormal | fcNegSubnormal) && |
| KnownRHS.isKnownNeverNaN()) |
| Known.knownNot(fcNan); |
| |
| // We could rule out negative and subnormal results when exponent is known to |
| // never be a normal value, but having either argument being known to never be |
| // normal is unlikely and not worth considering. |
| |
| // Only a negative base raised to an odd power returns a negative value. |
| if (KnownLHS.isKnownNever(fcNegative)) { |
| Known.knownNot(fcNegative); |
| } else if (KnownLHS.isKnownNever(fcNegNormal | fcNegSubnormal)) { |
| Known.knownNot(fcNegNormal | fcNegSubnormal); |
| // See if we can also rule out -0.0 or -inf. |
| // Here at least one of -0.0 or -inf is a possible base. |
| |
| // pow(-0.0, odd-positive) = -0.0 |
| // pow(-inf, odd-negative) = -0.0 |
| if ((KnownLHS.isKnownNever(fcNegZero) || |
| KnownRHS.isKnownNever(fcPosNormal)) && |
| (KnownLHS.isKnownNever(fcNegInf) || KnownRHS.isKnownNever(fcNegNormal))) |
| Known.knownNot(fcNegZero); |
| |
| // pow(-0.0, odd-negative) = -inf |
| // pow(-inf, odd-positive) = -inf |
| if ((KnownLHS.isKnownNever(fcNegZero) || |
| KnownRHS.isKnownNever(fcNegNormal)) && |
| (KnownLHS.isKnownNever(fcNegInf) || KnownRHS.isKnownNever(fcPosNormal))) |
| Known.knownNot(fcNegInf); |
| } |
| |
| return Known; |
| } |
| |
| KnownFPClass KnownFPClass::powi(const KnownFPClass &KnownSrc, |
| const KnownBits &ExponentKnownBits) { |
| KnownFPClass Known; |
| Known.propagateNonNaN(KnownSrc); |
| |
| if (ExponentKnownBits.isZero()) { |
| // powi(QNaN, 0) returns 1.0, and powi(SNaN, 0) may non-deterministically |
| // return 1.0 or a NaN. |
| if (KnownSrc.isKnownNever(fcSNan)) { |
| Known.knownNot(~fcPosNormal); |
| return Known; |
| } |
| |
| Known.knownNot(~(fcPosNormal | fcNan)); |
| return Known; |
| } |
| |
| // Given that exp is an integer, here are the |
| // ways that powi can return a negative value: |
| // |
| // powi(x, exp) --> negative if exp is odd and x is negative. |
| // powi(-0, exp) --> -inf if exp is negative odd. |
| // powi(-0, exp) --> -0 if exp is positive odd. |
| // powi(-inf, exp) --> -0 if exp is negative odd. |
| // powi(-inf, exp) --> -inf if exp is positive odd. |
| if (KnownSrc.isKnownNever(fcNegative) || ExponentKnownBits.isEven()) { |
| Known.knownNot(fcNegative); |
| } else if (KnownSrc.isKnownNever(fcNegNormal | fcNegSubnormal)) { |
| Known.knownNot(fcNegNormal | fcNegSubnormal); |
| // See if we can also rule out -0.0 or -inf. |
| // Here at least one of -0.0 or -inf is a possible base. |
| |
| // We already know that ExponentKnownBits.isEven() is false here. |
| const bool IsKnownNeverOddPositive = ExponentKnownBits.isNegative(); |
| const bool IsKnownNeverOddNegative = ExponentKnownBits.isNonNegative(); |
| |
| // powi(-0.0, odd-positive) = -0.0 |
| // powi(-inf, odd-negative) = -0.0 |
| if ((KnownSrc.isKnownNever(fcNegZero) || IsKnownNeverOddPositive) && |
| (KnownSrc.isKnownNever(fcNegInf) || IsKnownNeverOddNegative)) |
| Known.knownNot(fcNegZero); |
| |
| // powi(-0.0, odd-negative) = -inf |
| // powi(-inf, odd-positive) = -inf |
| if ((KnownSrc.isKnownNever(fcNegZero) || IsKnownNeverOddNegative) && |
| (KnownSrc.isKnownNever(fcNegInf) || IsKnownNeverOddPositive)) |
| Known.knownNot(fcNegInf); |
| } |
| |
| // powi(x, exp) --> inf |
| // when: |
| // * powi(inf, exp), exp > 0 |
| // * powi(+/-0, exp), exp < 0 |
| // * powi(finite, exp), |exp| > 1 |
| // * powi(subnormal, -1) |
| // TODO: |
| // 1. This simple all or nothing approach. We can do better |
| // and cover sign/parity and exp > 1 vs exp < -1 separately. |
| // 2. powi(0/nan, exp), exp > 0 can be refinable |
| // to fcNan | fcZero | fcPosNormal. |
| { |
| APInt MinExp = ExponentKnownBits.getSignedMinValue(); |
| APInt MaxExp = ExponentKnownBits.getSignedMaxValue(); |
| |
| // powi(inf, exp), exp > 0 |
| bool MayInfSrc = |
| !KnownSrc.isKnownNever(fcInf) && MaxExp.isStrictlyPositive(); |
| |
| // powi(+/-0, exp), exp < 0 |
| bool MayDivByZero = !KnownSrc.isKnownNever(fcZero) && MinExp.isNegative(); |
| |
| // powi(finite, exp), |exp| > 1 |
| bool MayFinite = !KnownSrc.isKnownNever(fcNormal | fcSubnormal); |
| bool MayAbsExpGT1 = MinExp.slt(-1) || MaxExp.sgt(1); |
| bool MayFiniteOverflow = MayFinite && MayAbsExpGT1; |
| |
| // powi(subnormal, -1) |
| bool MayBeNegOne = ExponentKnownBits.Zero.isZero(); |
| bool MaySubnormInv = !KnownSrc.isKnownNever(fcSubnormal) && MayBeNegOne; |
| |
| if (!MayInfSrc && !MayDivByZero && !MayFiniteOverflow && !MaySubnormInv) |
| Known.knownNot(fcInf); |
| } |
| |
| return Known; |
| } |