Skip to content
8 changes: 8 additions & 0 deletions llvm/include/llvm/Analysis/ValueTracking.h
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,14 @@ bool isKnownNeverNaN(const Value *V, unsigned Depth, const SimplifyQuery &SQ);
std::optional<bool> computeKnownFPSignBit(const Value *V, unsigned Depth,
const SimplifyQuery &SQ);

/// Return true if the sign bit of result can be ignored by the user when the
/// result is zero.
bool ignoreSignBitOfZero(const Use &U);

/// Return true if the sign bit of result can be ignored by the user when the
/// result is NaN.
bool ignoreSignBitOfNaN(const Use &U);

/// If the specified value can be set by repeating the same byte in memory,
/// return the i8 value that it is represented with. This is true for all i8
/// values obviously, but is also true for i32 0, i32 -1, i16 0xF0F0, double
Expand Down
112 changes: 112 additions & 0 deletions llvm/lib/Analysis/ValueTracking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6357,6 +6357,118 @@ std::optional<bool> llvm::computeKnownFPSignBit(const Value *V, unsigned Depth,
return Known.SignBit;
}

/// Return true if the sign bit of result can be ignored by the user when the
/// result is zero.
bool llvm::ignoreSignBitOfZero(const Use &U) {
auto *User = cast<Instruction>(U.getUser());
if (auto *FPOp = dyn_cast<FPMathOperator>(User)) {
if (FPOp->hasNoSignedZeros())
return true;
}

switch (User->getOpcode()) {
case Instruction::FPToSI:
case Instruction::FPToUI:
return true;
case Instruction::FCmp:
// fcmp treats both positive and negative zero as equal.
return true;
case Instruction::Call:
if (auto *II = dyn_cast<IntrinsicInst>(User)) {
switch (II->getIntrinsicID()) {
case Intrinsic::fabs:
return true;
case Intrinsic::copysign:
return U.getOperandNo() == 0;
case Intrinsic::is_fpclass:
case Intrinsic::vp_is_fpclass: {
auto Test =
static_cast<FPClassTest>(
cast<ConstantInt>(II->getArgOperand(1))->getZExtValue()) &
FPClassTest::fcZero;
return Test == FPClassTest::fcZero || Test == FPClassTest::fcNone;
}
default:
return false;
}
}
return false;
default:
return false;
}
}

/// Return true if the sign bit of result can be ignored by the user when the
/// result is NaN.
bool llvm::ignoreSignBitOfNaN(const Use &U) {
auto *User = cast<Instruction>(U.getUser());
if (auto *FPOp = dyn_cast<FPMathOperator>(User)) {
if (FPOp->hasNoNaNs())
return true;
}

switch (User->getOpcode()) {
case Instruction::FPToSI:
case Instruction::FPToUI:
return true;
// Proper FP math operations ignore the sign bit of NaN.
Copy link
Contributor

@nikic nikic May 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For reference: https://llvm.org/docs/LangRef.html#behavior-of-floating-point-nan-values

the result has a non-deterministic sign

case Instruction::FAdd:
case Instruction::FSub:
case Instruction::FMul:
case Instruction::FDiv:
case Instruction::FRem:
case Instruction::FPTrunc:
case Instruction::FPExt:
case Instruction::FCmp:
return true;
// Bitwise FP operations should preserve the sign bit of NaN.
case Instruction::FNeg:
case Instruction::Select:
case Instruction::PHI:
return false;
case Instruction::Ret:
return User->getFunction()->getAttributes().getRetNoFPClass() &
FPClassTest::fcNan;
case Instruction::Call:
case Instruction::Invoke: {
if (auto *II = dyn_cast<IntrinsicInst>(User)) {
switch (II->getIntrinsicID()) {
case Intrinsic::fabs:
return true;
case Intrinsic::copysign:
return U.getOperandNo() == 0;
// Other proper FP math intrinsics ignore the sign bit of NaN.
case Intrinsic::maxnum:
case Intrinsic::minnum:
case Intrinsic::maximum:
case Intrinsic::minimum:
case Intrinsic::maximumnum:
case Intrinsic::minimumnum:
case Intrinsic::canonicalize:
case Intrinsic::fma:
case Intrinsic::fmuladd:
case Intrinsic::sqrt:
case Intrinsic::pow:
case Intrinsic::powi:
case Intrinsic::fptoui_sat:
case Intrinsic::fptosi_sat:
case Intrinsic::is_fpclass:
case Intrinsic::vp_is_fpclass:
return true;
default:
return false;
}
}

FPClassTest NoFPClass =
cast<CallBase>(User)->getParamNoFPClass(U.getOperandNo());
return NoFPClass & FPClassTest::fcNan;
}
default:
return false;
}
}

Value *llvm::isBytewiseValue(Value *V, const DataLayout &DL) {

// All byte-wide stores are splatable, even of arbitrary variables.
Expand Down
50 changes: 7 additions & 43 deletions llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2773,47 +2773,6 @@ Instruction *InstCombinerImpl::foldAndOrOfSelectUsingImpliedCond(Value *Op,
return nullptr;
}

/// Return true if the sign bit of result can be ignored when the result is
/// zero.
static bool ignoreSignBitOfZero(Instruction &I) {
if (I.hasNoSignedZeros())
return true;

// Check if the sign bit is ignored by the only user.
if (!I.hasOneUse())
return false;
Instruction *User = I.user_back();

// fcmp treats both positive and negative zero as equal.
if (User->getOpcode() == Instruction::FCmp)
return true;

if (auto *FPOp = dyn_cast<FPMathOperator>(User))
return FPOp->hasNoSignedZeros();

return false;
}

/// Return true if the sign bit of result can be ignored when the result is NaN.
static bool ignoreSignBitOfNaN(Instruction &I) {
if (I.hasNoNaNs())
return true;

// Check if the sign bit is ignored by the only user.
if (!I.hasOneUse())
return false;
Instruction *User = I.user_back();

// fcmp ignores the sign bit of NaN.
if (User->getOpcode() == Instruction::FCmp)
return true;

if (auto *FPOp = dyn_cast<FPMathOperator>(User))
return FPOp->hasNoNaNs();

return false;
}

// Canonicalize select with fcmp to fabs(). -0.0 makes this tricky. We need
// fast-math-flags (nsz) or fsub with +0.0 (not fneg) for this to work.
static Instruction *foldSelectWithFCmpToFabs(SelectInst &SI,
Expand All @@ -2838,7 +2797,8 @@ static Instruction *foldSelectWithFCmpToFabs(SelectInst &SI,
// of NAN, but IEEE-754 specifies the signbit of NAN values with
// fneg/fabs operations.
if (match(TrueVal, m_FSub(m_PosZeroFP(), m_Specific(X))) &&
(cast<FPMathOperator>(CondVal)->hasNoNaNs() || ignoreSignBitOfNaN(SI) ||
(cast<FPMathOperator>(CondVal)->hasNoNaNs() || SI.hasNoNaNs() ||
(SI.hasOneUse() && ignoreSignBitOfNaN(*SI.use_begin())) ||
isKnownNeverNaN(X, /*Depth=*/0,
IC.getSimplifyQuery().getWithInstruction(
cast<Instruction>(CondVal))))) {
Expand Down Expand Up @@ -2885,7 +2845,11 @@ static Instruction *foldSelectWithFCmpToFabs(SelectInst &SI,
// Note: We require "nnan" for this fold because fcmp ignores the signbit
// of NAN, but IEEE-754 specifies the signbit of NAN values with
// fneg/fabs operations.
if (!ignoreSignBitOfZero(SI) || !ignoreSignBitOfNaN(SI))
if (!SI.hasNoSignedZeros() &&
(!SI.hasOneUse() || !ignoreSignBitOfZero(*SI.use_begin())))
return nullptr;
if (!SI.hasNoNaNs() &&
(!SI.hasOneUse() || !ignoreSignBitOfNaN(*SI.use_begin())))
return nullptr;

if (Swap)
Expand Down
Loading