Skip to content
Open
Show file tree
Hide file tree
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
5 changes: 5 additions & 0 deletions llvm/include/llvm/Support/KnownFPClass.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,11 @@ struct KnownFPClass {
signBitMustBeZero();
}

/// Apply the canonicalize intrinsic to this value. This is essentially a
/// stronger form of propagateCanonicalizingSrc.
LLVM_ABI void
canonicalize(DenormalMode DenormMode = DenormalMode::getDynamic());

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

Expand Down
55 changes: 6 additions & 49 deletions llvm/lib/Analysis/ValueTracking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5269,58 +5269,15 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
break;
}
case Intrinsic::canonicalize: {
KnownFPClass KnownSrc;
computeKnownFPClass(II->getArgOperand(0), DemandedElts, InterestedClasses,
KnownSrc, Q, Depth + 1);

// 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);
Known, Q, Depth + 1);

const Function *F = II->getFunction();
if (!F)
break;

// If the parent function flushes denormals, the canonical output cannot
// be a denormal.
const fltSemantics &FPType =
II->getType()->getScalarType()->getFltSemantics();
DenormalMode DenormMode = F->getDenormalMode(FPType);
if (DenormMode == DenormalMode::getIEEE()) {
if (KnownSrc.isKnownNever(fcPosZero))
Known.knownNot(fcPosZero);
if (KnownSrc.isKnownNever(fcNegZero))
Known.knownNot(fcNegZero);
break;
}

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);
break;
}

if (DenormMode.Input == DenormalMode::PositiveZero ||
(DenormMode.Output == DenormalMode::PositiveZero &&
DenormMode.Input == DenormalMode::IEEE))
Known.knownNot(fcNegZero);

DenormalMode DenormMode =
F ? F->getDenormalMode(
II->getType()->getScalarType()->getFltSemantics())
: DenormalMode::getDynamic();
Known.canonicalize(DenormMode);
break;
}
case Intrinsic::vector_reduce_fmax:
Expand Down
48 changes: 48 additions & 0 deletions llvm/lib/Support/KnownFPClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,54 @@ void KnownFPClass::propagateDenormal(const KnownFPClass &Src,
}
}

void KnownFPClass::canonicalize(DenormalMode DenormMode) {
KnownFPClass KnownSrc = *this;
*this = KnownFPClass();

// 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.
KnownFPClasses = KnownSrc.KnownFPClasses | fcZero | fcQNan;

// Stronger version of propagateNaN
// Canonicalize is guaranteed to quiet signaling nans.
if (KnownSrc.isKnownNeverNaN())
knownNot(fcNan);
else
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))
knownNot(fcPosZero);
if (KnownSrc.isKnownNever(fcNegZero))
knownNot(fcNegZero);
return;
}

if (DenormMode.inputsAreZero() || DenormMode.outputsAreZero())
knownNot(fcSubnormal);

if (DenormMode == DenormalMode::getPreserveSign()) {
if (KnownSrc.isKnownNever(fcPosZero | fcPosSubnormal))
knownNot(fcPosZero);
if (KnownSrc.isKnownNever(fcNegZero | fcNegSubnormal))
knownNot(fcNegZero);
return;
}

if (DenormMode.Input == DenormalMode::PositiveZero ||
(DenormMode.Output == DenormalMode::PositiveZero &&
DenormMode.Input == DenormalMode::IEEE))
knownNot(fcNegZero);
}

void KnownFPClass::propagateCanonicalizingSrc(const KnownFPClass &Src,
DenormalMode Mode) {
propagateDenormal(Src, Mode);
Expand Down
73 changes: 73 additions & 0 deletions llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2106,6 +2106,79 @@ Value *InstCombinerImpl::SimplifyDemandedUseFPClass(Value *V,
Known.copysign(KnownSign);
break;
}
case Intrinsic::canonicalize: {
Type *EltTy = VTy->getScalarType();

// TODO: This could have more refined support for PositiveZero denormal
// mode.
if (EltTy->isIEEELikeFPTy()) {
DenormalMode Mode = F.getDenormalMode(EltTy->getFltSemantics());

FPClassTest SrcDemandedMask = DemandedMask;

// A demanded quiet nan result may have come from a signaling nan, so we
// need to expand the demanded mask.
if ((DemandedMask & fcQNan) != fcNone)
SrcDemandedMask |= fcSNan;

// This cannot have produced a signaling nan, so we can trim any snan
// inputs.
if ((DemandedMask & fcSNan) != fcNone)
SrcDemandedMask &= ~fcSNan;

if (Mode != DenormalMode::getIEEE()) {
// Any zero results may have come from flushed denormals.
if (DemandedMask & fcPosZero)
SrcDemandedMask |= fcPosSubnormal;
if (DemandedMask & fcNegZero)
SrcDemandedMask |= fcNegSubnormal;
}

if (Mode == DenormalMode::getPreserveSign()) {
// If a denormal input will be flushed, and we don't need zeros, we
// don't need denormals either.
if ((DemandedMask & fcPosZero) == fcNone)
SrcDemandedMask &= ~fcPosSubnormal;

if ((DemandedMask & fcNegZero) == fcNone)
SrcDemandedMask &= ~fcNegSubnormal;
}

KnownFPClass KnownSrc;

// Simplify upstream operations before trying to simplify this call.
if (SimplifyDemandedFPClass(I, 0, SrcDemandedMask, KnownSrc, Depth + 1))
return I;

Known = KnownSrc;

// Perform the canonicalization to see if this folded to a constant.
Known.canonicalize(Mode);

if (Constant *SingleVal =
getFPClassConstant(VTy, DemandedMask & Known.KnownFPClasses))
return SingleVal;

if (Mode == DenormalMode::getIEEE()) {
// For IEEE handling, there is only a bit change for nan inputs, so we
// can drop it if we do not demand nan results or we know the input
// isn't a nan.
if (KnownSrc.isKnownNeverNaN() || (DemandedMask & fcNan) == fcNone)
return CI->getArgOperand(0);
} else {
// Nan handling is the same as the IEEE case, but we also need to
// avoid denormal inputs to drop the canonicalize.
if ((KnownSrc.isKnownNeverNaN() ||
(DemandedMask & fcNan) == fcNone) &&
KnownSrc.isKnownNeverSubnormal())
return CI->getArgOperand(0);
}

return nullptr;
}

[[fallthrough]];
}
default:
Known = computeKnownFPClass(I, ~DemandedMask, CxtI, Depth + 1);
break;
Expand Down
Loading
Loading