Skip to content

[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

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 8 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
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.
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