Skip to content

[ValueTracking][InstCombine] Generalize ignoreSignBitOfZero/NaN to handle more cases #141015

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
May 28, 2025
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 the FP value can be ignored by the user when
/// the value is zero.
bool canIgnoreSignBitOfZero(const Use &U);

/// Return true if the sign bit of the FP value can be ignored by the user when
/// the value is NaN.
bool canIgnoreSignBitOfNaN(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
108 changes: 108 additions & 0 deletions llvm/lib/Analysis/ValueTracking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6357,6 +6357,114 @@ std::optional<bool> llvm::computeKnownFPSignBit(const Value *V, unsigned Depth,
return Known.SignBit;
}

bool llvm::canIgnoreSignBitOfZero(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;
}
}

bool llvm::canIgnoreSignBitOfNaN(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() && canIgnoreSignBitOfNaN(*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() || !canIgnoreSignBitOfZero(*SI.use_begin())))
return nullptr;
if (!SI.hasNoNaNs() &&
(!SI.hasOneUse() || !canIgnoreSignBitOfNaN(*SI.use_begin())))
return nullptr;

if (Swap)
Expand Down
Loading