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
7 changes: 7 additions & 0 deletions llvm/include/llvm/Analysis/ValueTracking.h
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,13 @@ 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 when the result is
/// zero.
bool ignoreSignBitOfZero(Instruction &I);

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

/// 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
124 changes: 124 additions & 0 deletions llvm/lib/Analysis/ValueTracking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6357,6 +6357,130 @@ std::optional<bool> llvm::computeKnownFPSignBit(const Value *V, unsigned Depth,
return Known.SignBit;
}

/// Return true if the sign bit of result can be ignored when the result is
/// zero.
bool llvm::ignoreSignBitOfZero(Instruction &I) {
if (auto *FPOp = dyn_cast<FPMathOperator>(&I))
if (FPOp->hasNoSignedZeros())
return true;

// Check if the sign bit is ignored by the only user.
if (!I.hasOneUse())
return false;
Instruction *User = I.user_back();
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 II->getArgOperand(0) == &I;
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::ignoreSignBitOfNaN(Instruction &I) {
if (auto *FPOp = dyn_cast<FPMathOperator>(&I))
if (FPOp->hasNoNaNs())
return true;

// Check if the sign bit is ignored by the only user.
if (!I.hasOneUse())
return false;
Instruction *User = I.user_back();
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 I.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 II->getArgOperand(0) == &I;
// 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(
I.uses().begin()->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
41 changes: 0 additions & 41 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 Down
Loading