| //===- 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" |
| |
| using namespace llvm; |
| |
| KnownFPClass::KnownFPClass(const APFloat &C) |
| : KnownFPClasses(C.classify()), SignBit(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) { |
| KnownFPClasses = Src.KnownFPClasses; |
| // 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()) |
| KnownFPClasses |= fcPosZero; |
| |
| if (!Src.isKnownNeverNegSubnormal() && Mode != DenormalMode::getIEEE()) { |
| if (Mode != DenormalMode::getPositiveZero()) |
| KnownFPClasses |= fcNegZero; |
| |
| if (Mode.Input == DenormalMode::PositiveZero || |
| Mode.Output == DenormalMode::PositiveZero || |
| Mode.Input == DenormalMode::Dynamic || |
| Mode.Output == DenormalMode::Dynamic) |
| KnownFPClasses |= 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 at least one operand is known to be positive, the result must be |
| // positive. |
| if ((KnownLHS.cannotBeOrderedLessThanZero() && |
| KnownLHS.isKnownNeverNaN()) || |
| (KnownRHS.cannotBeOrderedLessThanZero() && KnownRHS.isKnownNeverNaN())) |
| Known.knownNot(KnownFPClass::OrderedLessThanZeroMask); |
| } else if (Kind == MinMaxKind::maximum) { |
| // If at least one operand is known to be positive, the result must be |
| // positive. |
| if (KnownLHS.cannotBeOrderedLessThanZero() || |
| KnownRHS.cannotBeOrderedLessThanZero()) |
| Known.knownNot(KnownFPClass::OrderedLessThanZeroMask); |
| } else if (Kind == MinMaxKind::minnum || Kind == MinMaxKind::minimumnum) { |
| // If at least one operand is known to be negative, the result must be |
| // negative. |
| if ((KnownLHS.cannotBeOrderedGreaterThanZero() && |
| KnownLHS.isKnownNeverNaN()) || |
| (KnownRHS.cannotBeOrderedGreaterThanZero() && |
| KnownRHS.isKnownNeverNaN())) |
| Known.knownNot(KnownFPClass::OrderedGreaterThanZeroMask); |
| } else if (Kind == MinMaxKind::minimum) { |
| // If at least one operand is known to be negative, the result must be |
| // negative. |
| if (KnownLHS.cannotBeOrderedGreaterThanZero() || |
| KnownRHS.cannotBeOrderedGreaterThanZero()) |
| Known.knownNot(KnownFPClass::OrderedGreaterThanZeroMask); |
| } 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.KnownFPClasses & fcZero) != fcNone && |
| !Known.isKnownNeverSubnormal()) { |
| if (Mode != DenormalMode::getIEEE()) |
| Known.KnownFPClasses |= fcZero; |
| } |
| |
| if (Known.isKnownNeverNaN()) { |
| if (KnownLHS.SignBit && KnownRHS.SignBit && |
| *KnownLHS.SignBit == *KnownRHS.SignBit) { |
| if (*KnownLHS.SignBit) |
| 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.SignBit = std::nullopt; |
| if (!KnownRHS.isKnownNeverNaN()) |
| KnownRHS.SignBit = std::nullopt; |
| if ((Kind == MinMaxKind::maximum || Kind == MinMaxKind::maximumnum || |
| Kind == MinMaxKind::maxnum) && |
| (KnownLHS.SignBit == false || KnownRHS.SignBit == false)) |
| Known.signBitMustBeZero(); |
| else if ((Kind == MinMaxKind::minimum || Kind == MinMaxKind::minimumnum || |
| Kind == MinMaxKind::minnum) && |
| (KnownLHS.SignBit == true || KnownRHS.SignBit == 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.KnownFPClasses = KnownSrc.KnownFPClasses | 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)) |
| Known.knownNot(fcNegZero); |
| |
| return Known; |
| } |
| |
| KnownFPClass KnownFPClass::fadd(const KnownFPClass &KnownLHS, |
| const KnownFPClass &KnownRHS, |
| DenormalMode Mode) { |
| KnownFPClass Known; |
| |
| // Adding positive and negative infinity produces NaN. |
| // TODO: Check sign of infinities. |
| if (KnownLHS.isKnownNeverNaN() && KnownRHS.isKnownNeverNaN() && |
| (KnownLHS.isKnownNeverInfinity() || KnownRHS.isKnownNeverInfinity())) |
| Known.knownNot(fcNan); |
| |
| if (KnownLHS.cannotBeOrderedLessThanZero() && |
| KnownRHS.cannotBeOrderedLessThanZero()) |
| Known.knownNot(OrderedLessThanZeroMask); |
| |
| if (KnownLHS.cannotBeOrderedGreaterThanZero() && |
| KnownRHS.cannotBeOrderedGreaterThanZero()) |
| Known.knownNot(OrderedGreaterThanZeroMask); |
| |
| // (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::fmul(const KnownFPClass &KnownLHS, |
| const KnownFPClass &KnownRHS, |
| DenormalMode Mode) { |
| KnownFPClass Known; |
| |
| // xor sign bit. |
| if ((KnownLHS.isKnownNever(fcNegative) && |
| KnownRHS.isKnownNever(fcNegative)) || |
| (KnownLHS.isKnownNever(fcPositive) && KnownRHS.isKnownNever(fcPositive))) |
| Known.knownNot(fcNegative); |
| |
| if ((KnownLHS.isKnownNever(fcPositive) && |
| KnownRHS.isKnownNever(fcNegative)) || |
| (KnownLHS.isKnownNever(fcNegative) && KnownRHS.isKnownNever(fcPositive))) |
| Known.knownNot(fcPositive); |
| |
| // inf * anything => inf or nan |
| if (KnownLHS.isKnownAlways(fcInf | fcNan) || |
| KnownRHS.isKnownAlways(fcInf | fcNan)) |
| Known.knownNot(fcNormal | fcSubnormal | fcZero); |
| |
| // 0 * anything => 0 or nan |
| if (KnownRHS.isKnownAlways(fcZero | fcNan) || |
| KnownLHS.isKnownAlways(fcZero | fcNan)) |
| Known.knownNot(fcNormal | fcSubnormal | fcInf); |
| |
| // +/-0 * +/-inf = nan |
| if ((KnownLHS.isKnownAlways(fcZero | fcNan) && |
| KnownRHS.isKnownAlways(fcInf | fcNan)) || |
| (KnownLHS.isKnownAlways(fcInf | fcNan) && |
| KnownRHS.isKnownAlways(fcZero | fcNan))) |
| Known.knownNot(~fcNan); |
| |
| if (!KnownLHS.isKnownNeverNaN() || !KnownRHS.isKnownNeverNaN()) |
| return Known; |
| |
| if (KnownLHS.SignBit && KnownRHS.SignBit) { |
| if (*KnownLHS.SignBit == *KnownRHS.SignBit) |
| Known.signBitMustBeZero(); |
| else |
| Known.signBitMustBeOne(); |
| } |
| |
| // If 0 * +/-inf produces NaN. |
| if ((KnownRHS.isKnownNeverInfinity() || |
| KnownLHS.isKnownNeverLogicalZero(Mode)) && |
| (KnownLHS.isKnownNeverInfinity() || |
| KnownRHS.isKnownNeverLogicalZero(Mode))) |
| Known.knownNot(fcNan); |
| |
| return Known; |
| } |
| |
| KnownFPClass KnownFPClass::exp(const KnownFPClass &KnownSrc) { |
| KnownFPClass Known; |
| Known.knownNot(fcNegative); |
| |
| Known.propagateNaN(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); |
| propagateNaN(Src, /*PreserveSign=*/true); |
| } |
| |
| KnownFPClass KnownFPClass::log(const KnownFPClass &KnownSrc, |
| DenormalMode Mode) { |
| KnownFPClass Known; |
| Known.knownNot(fcNegZero); |
| |
| 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); |
| if (KnownSrc.isKnownNever(fcSNan)) |
| Known.knownNot(fcSNan); |
| |
| // 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::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.KnownFPClasses & fcPosSubnormal) |
| Known.KnownFPClasses |= fcPosNormal; |
| if (Known.KnownFPClasses & fcNegSubnormal) |
| Known.KnownFPClasses |= fcNegNormal; |
| Known.knownNot(fcSubnormal); |
| } |
| |
| // Sign bit of a nan isn't guaranteed. |
| if (!Known.isKnownNeverNaN()) |
| Known.SignBit = std::nullopt; |
| |
| return Known; |
| } |
| |
| KnownFPClass KnownFPClass::roundToIntegral(const KnownFPClass &KnownSrc, |
| bool IsTrunc, |
| bool IsMultiUnitFPType) { |
| KnownFPClass Known; |
| |
| // Integer results cannot be subnormal. |
| Known.knownNot(fcSubnormal); |
| |
| Known.propagateNaN(KnownSrc, true); |
| |
| // 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; |
| } |