Skip to content

IR: introduce CmpInst::isEquivalence #111979

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 5 commits into from
Nov 4, 2024
Merged
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/IR/InstrTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -912,6 +912,11 @@ class CmpInst : public Instruction {
/// Determine if this is an equals/not equals predicate.
bool isEquality() const { return isEquality(getPredicate()); }

/// Determine if one operand of this compare can always be replaced by the
/// other operand, ignoring provenance considerations. If \p Invert, check for
/// equivalence with the inverse predicate.
bool isEquivalence(bool Invert = false) const;

/// Return true if the predicate is relational (not EQ or NE).
static bool isRelational(Predicate P) { return !isEquality(P); }

Expand Down
10 changes: 10 additions & 0 deletions llvm/include/llvm/IR/PatternMatch.h
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,16 @@ inline cstfp_pred_ty<is_non_zero_fp> m_NonZeroFP() {
return cstfp_pred_ty<is_non_zero_fp>();
}

struct is_non_zero_not_denormal_fp {
bool isValue(const APFloat &C) { return !C.isDenormal() && C.isNonZero(); }
};

/// Match a floating-point non-zero that is not a denormal.
/// For vectors, this includes constants with undefined elements.
inline cstfp_pred_ty<is_non_zero_not_denormal_fp> m_NonZeroNotDenormalFP() {
return cstfp_pred_ty<is_non_zero_not_denormal_fp>();
}

///////////////////////////////////////////////////////////////////////////////

template <typename Class> struct bind_ty {
Expand Down
31 changes: 31 additions & 0 deletions llvm/lib/IR/Instructions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "llvm/IR/Metadata.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Operator.h"
#include "llvm/IR/PatternMatch.h"
#include "llvm/IR/ProfDataUtils.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/Value.h"
Expand Down Expand Up @@ -3471,6 +3472,36 @@ bool CmpInst::isEquality(Predicate P) {
llvm_unreachable("Unsupported predicate kind");
}

// Returns true if either operand of CmpInst is a provably non-zero
// floating-point constant.
static bool hasNonZeroFPOperands(const CmpInst *Cmp) {
auto *LHS = dyn_cast<Constant>(Cmp->getOperand(0));
auto *RHS = dyn_cast<Constant>(Cmp->getOperand(1));
if (auto *Const = LHS ? LHS : RHS) {
using namespace llvm::PatternMatch;
return match(Const, m_NonZeroNotDenormalFP());
}
return false;
}

// Floating-point equality is not an equivalence when comparing +0.0 with
// -0.0, when comparing NaN with another value, or when flushing
// denormals-to-zero.
bool CmpInst::isEquivalence(bool Invert) const {
switch (Invert ? getInversePredicate() : getPredicate()) {
case CmpInst::Predicate::ICMP_EQ:
return true;
case CmpInst::Predicate::FCMP_UEQ:
if (!hasNoNaNs())
return false;
[[fallthrough]];
case CmpInst::Predicate::FCMP_OEQ:
return hasNonZeroFPOperands(this);
default:
return false;
}
}

CmpInst::Predicate CmpInst::getInversePredicate(Predicate pred) {
switch (pred) {
default: llvm_unreachable("Unknown cmp predicate!");
Expand Down
58 changes: 2 additions & 56 deletions llvm/lib/Transforms/Scalar/GVN.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1989,59 +1989,6 @@ bool GVNPass::processNonLocalLoad(LoadInst *Load) {
return Changed;
}

static bool impliesEquivalanceIfTrue(CmpInst* Cmp) {
if (Cmp->getPredicate() == CmpInst::Predicate::ICMP_EQ)
return true;

// Floating point comparisons can be equal, but not equivalent. Cases:
// NaNs for unordered operators
// +0.0 vs 0.0 for all operators
if (Cmp->getPredicate() == CmpInst::Predicate::FCMP_OEQ ||
(Cmp->getPredicate() == CmpInst::Predicate::FCMP_UEQ &&
Cmp->getFastMathFlags().noNaNs())) {
Value *LHS = Cmp->getOperand(0);
Value *RHS = Cmp->getOperand(1);
// If we can prove either side non-zero, then equality must imply
// equivalence.
// FIXME: We should do this optimization if 'no signed zeros' is
// applicable via an instruction-level fast-math-flag or some other
// indicator that relaxed FP semantics are being used.
if (isa<ConstantFP>(LHS) && !cast<ConstantFP>(LHS)->isZero())
return true;
if (isa<ConstantFP>(RHS) && !cast<ConstantFP>(RHS)->isZero())
return true;
// TODO: Handle vector floating point constants
}
return false;
}

static bool impliesEquivalanceIfFalse(CmpInst* Cmp) {
if (Cmp->getPredicate() == CmpInst::Predicate::ICMP_NE)
return true;

// Floating point comparisons can be equal, but not equivelent. Cases:
// NaNs for unordered operators
// +0.0 vs 0.0 for all operators
if ((Cmp->getPredicate() == CmpInst::Predicate::FCMP_ONE &&
Cmp->getFastMathFlags().noNaNs()) ||
Cmp->getPredicate() == CmpInst::Predicate::FCMP_UNE) {
Value *LHS = Cmp->getOperand(0);
Value *RHS = Cmp->getOperand(1);
// If we can prove either side non-zero, then equality must imply
// equivalence.
// FIXME: We should do this optimization if 'no signed zeros' is
// applicable via an instruction-level fast-math-flag or some other
// indicator that relaxed FP semantics are being used.
if (isa<ConstantFP>(LHS) && !cast<ConstantFP>(LHS)->isZero())
return true;
if (isa<ConstantFP>(RHS) && !cast<ConstantFP>(RHS)->isZero())
return true;
// TODO: Handle vector floating point constants
}
return false;
}


static bool hasUsersIn(Value *V, BasicBlock *BB) {
return llvm::any_of(V->users(), [BB](User *U) {
auto *I = dyn_cast<Instruction>(U);
Expand Down Expand Up @@ -2143,7 +2090,7 @@ bool GVNPass::processAssumeIntrinsic(AssumeInst *IntrinsicI) {
// call void @llvm.assume(i1 %cmp)
// ret float %load ; will change it to ret float %0
if (auto *CmpI = dyn_cast<CmpInst>(V)) {
if (impliesEquivalanceIfTrue(CmpI)) {
if (CmpI->isEquivalence()) {
Value *CmpLHS = CmpI->getOperand(0);
Value *CmpRHS = CmpI->getOperand(1);
// Heuristically pick the better replacement -- the choice of heuristic
Expand Down Expand Up @@ -2567,8 +2514,7 @@ bool GVNPass::propagateEquality(Value *LHS, Value *RHS,
// If "A == B" is known true, or "A != B" is known false, then replace
// A with B everywhere in the scope. For floating point operations, we
// have to be careful since equality does not always imply equivalance.
if ((isKnownTrue && impliesEquivalanceIfTrue(Cmp)) ||
(isKnownFalse && impliesEquivalanceIfFalse(Cmp)))
if (Cmp->isEquivalence(isKnownFalse))
Worklist.push_back(std::make_pair(Op0, Op1));

// If "A >= B" is known true, replace "A < B" with false everywhere.
Expand Down
30 changes: 29 additions & 1 deletion llvm/test/Transforms/GVN/edge.ll
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,34 @@ return:
ret double %retval
}

; Denormals may be flushed to zero in some cases by the backend.
; Hence, treat denormals as 0.
define float @fcmp_oeq_denormal(float %x, float %y) {
; CHECK-LABEL: define float @fcmp_oeq_denormal(
; CHECK-SAME: float [[X:%.*]], float [[Y:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*]]:
; CHECK-NEXT: [[CMP:%.*]] = fcmp oeq float [[Y]], 0x3800000000000000
; CHECK-NEXT: br i1 [[CMP]], label %[[IF:.*]], label %[[RETURN:.*]]
; CHECK: [[IF]]:
; CHECK-NEXT: [[DIV:%.*]] = fdiv float [[X]], [[Y]]
; CHECK-NEXT: br label %[[RETURN]]
; CHECK: [[RETURN]]:
; CHECK-NEXT: [[RETVAL:%.*]] = phi float [ [[DIV]], %[[IF]] ], [ [[X]], %[[ENTRY]] ]
; CHECK-NEXT: ret float [[RETVAL]]
;
entry:
%cmp = fcmp oeq float %y, 0x3800000000000000
br i1 %cmp, label %if, label %return

if:
%div = fdiv float %x, %y
br label %return

return:
%retval = phi float [ %div, %if ], [ %x, %entry ]
ret float %retval
}

define double @fcmp_une_zero(double %x, double %y) {
; CHECK-LABEL: define double @fcmp_une_zero(
; CHECK-SAME: double [[X:%.*]], double [[Y:%.*]]) {
Expand Down Expand Up @@ -251,7 +279,7 @@ return:
}

; We also cannot propagate a value if it's not a constant.
; This is because the value could be 0.0 or -0.0.
; This is because the value could be 0.0, -0.0, or a denormal.

define double @fcmp_oeq_maybe_zero(double %x, double %y, double %z1, double %z2) {
; CHECK-LABEL: define double @fcmp_oeq_maybe_zero(
Expand Down
Loading