Skip to content
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
4 changes: 4 additions & 0 deletions llvm/lib/Analysis/InstructionSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4164,6 +4164,10 @@ static Value *simplifyFCmpInst(CmpPredicate Pred, Value *LHS, Value *RHS,
return ConstantInt::get(RetTy, Pred == CmpInst::FCMP_UNO);
}

if (std::optional<bool> Res =
isImpliedByDomCondition(Pred, LHS, RHS, Q.CxtI, Q.DL))
return ConstantInt::getBool(RetTy, *Res);

const APFloat *C = nullptr;
match(RHS, m_APFloatAllowPoison(C));
std::optional<KnownFPClass> FullKnownClassLHS;
Expand Down
98 changes: 89 additions & 9 deletions llvm/lib/Analysis/ValueTracking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include "llvm/IR/Attributes.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constant.h"
#include "llvm/IR/ConstantFPRange.h"
#include "llvm/IR/ConstantRange.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DerivedTypes.h"
Expand Down Expand Up @@ -9447,6 +9448,69 @@ isImpliedCondICmps(CmpPredicate LPred, const Value *L0, const Value *L1,
return std::nullopt;
}

/// Return true if LHS implies RHS (expanded to its components as "R0 RPred R1")
/// is true. Return false if LHS implies RHS is false. Otherwise, return
/// std::nullopt if we can't infer anything.
static std::optional<bool>
isImpliedCondFCmps(FCmpInst::Predicate LPred, const Value *L0, const Value *L1,
FCmpInst::Predicate RPred, const Value *R0, const Value *R1,
const DataLayout &DL, bool LHSIsTrue) {
// The rest of the logic assumes the LHS condition is true. If that's not the
// case, invert the predicate to make it so.
if (!LHSIsTrue)
LPred = FCmpInst::getInversePredicate(LPred);

// We can have non-canonical operands, so try to normalize any common operand
// to L0/R0.
if (L0 == R1) {
std::swap(R0, R1);
RPred = FCmpInst::getSwappedPredicate(RPred);
}
if (R0 == L1) {
std::swap(L0, L1);
LPred = FCmpInst::getSwappedPredicate(LPred);
}
if (L1 == R1) {
// If we have L0 == R0 and L1 == R1, then make L1/R1 the constants.
if (L0 != R0 || match(L0, m_ImmConstant())) {
std::swap(L0, L1);
LPred = ICmpInst::getSwappedCmpPredicate(LPred);
std::swap(R0, R1);
RPred = ICmpInst::getSwappedCmpPredicate(RPred);
}
}

// Can we infer anything when the two compares have matching operands?
if (L0 == R0 && L1 == R1) {
if ((LPred & RPred) == LPred)
return true;
if ((LPred & ~RPred) == LPred)
return false;
}

// See if we can infer anything if operand-0 matches and we have at least one
// constant.
const APFloat *L1C, *R1C;
if (L0 == R0 && match(L1, m_APFloat(L1C)) && match(R1, m_APFloat(R1C))) {
if (std::optional<ConstantFPRange> DomCR =
ConstantFPRange::makeExactFCmpRegion(LPred, *L1C)) {
if (std::optional<ConstantFPRange> ImpliedCR =
ConstantFPRange::makeExactFCmpRegion(RPred, *R1C)) {
if (ImpliedCR->contains(*DomCR))
return true;
}
if (std::optional<ConstantFPRange> ImpliedCR =
ConstantFPRange::makeExactFCmpRegion(
FCmpInst::getInversePredicate(RPred), *R1C)) {
if (ImpliedCR->contains(*DomCR))
return false;
}
}
}

return std::nullopt;
}

/// Return true if LHS implies RHS is true. Return false if LHS implies RHS is
/// false. Otherwise, return std::nullopt if we can't infer anything. We
/// expect the RHS to be an icmp and the LHS to be an 'and', 'or', or a 'select'
Expand Down Expand Up @@ -9502,15 +9566,24 @@ llvm::isImpliedCondition(const Value *LHS, CmpPredicate RHSPred,
LHSIsTrue = !LHSIsTrue;

// Both LHS and RHS are icmps.
if (const auto *LHSCmp = dyn_cast<ICmpInst>(LHS))
return isImpliedCondICmps(LHSCmp->getCmpPredicate(), LHSCmp->getOperand(0),
LHSCmp->getOperand(1), RHSPred, RHSOp0, RHSOp1,
DL, LHSIsTrue);
const Value *V;
if (match(LHS, m_NUWTrunc(m_Value(V))))
return isImpliedCondICmps(CmpInst::ICMP_NE, V,
ConstantInt::get(V->getType(), 0), RHSPred,
RHSOp0, RHSOp1, DL, LHSIsTrue);
if (RHSOp0->getType()->getScalarType()->isIntOrPtrTy()) {
if (const auto *LHSCmp = dyn_cast<ICmpInst>(LHS))
return isImpliedCondICmps(LHSCmp->getCmpPredicate(),
LHSCmp->getOperand(0), LHSCmp->getOperand(1),
RHSPred, RHSOp0, RHSOp1, DL, LHSIsTrue);
const Value *V;
if (match(LHS, m_NUWTrunc(m_Value(V))))
return isImpliedCondICmps(CmpInst::ICMP_NE, V,
ConstantInt::get(V->getType(), 0), RHSPred,
RHSOp0, RHSOp1, DL, LHSIsTrue);
} else {
assert(RHSOp0->getType()->isFPOrFPVectorTy() &&
"Expected floating point type only!");
if (const auto *LHSCmp = dyn_cast<FCmpInst>(LHS))
return isImpliedCondFCmps(LHSCmp->getPredicate(), LHSCmp->getOperand(0),
LHSCmp->getOperand(1), RHSPred, RHSOp0, RHSOp1,
DL, LHSIsTrue);
}

/// The LHS should be an 'or', 'and', or a 'select' instruction. We expect
/// the RHS to be an icmp.
Expand Down Expand Up @@ -9547,6 +9620,13 @@ std::optional<bool> llvm::isImpliedCondition(const Value *LHS, const Value *RHS,
return InvertRHS ? !*Implied : *Implied;
return std::nullopt;
}
if (const FCmpInst *RHSCmp = dyn_cast<FCmpInst>(RHS)) {
if (auto Implied = isImpliedCondition(
LHS, RHSCmp->getPredicate(), RHSCmp->getOperand(0),
RHSCmp->getOperand(1), DL, LHSIsTrue, Depth))
return InvertRHS ? !*Implied : *Implied;
return std::nullopt;
}

const Value *V;
if (match(RHS, m_NUWTrunc(m_Value(V)))) {
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/CodeGen/AMDGPU/sgpr-copy.ll
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ endif: ; preds = %else, %if
define amdgpu_kernel void @copy1(ptr addrspace(1) %out, ptr addrspace(1) %in0) {
entry:
%tmp = load float, ptr addrspace(1) %in0
%tmp1 = fcmp oeq float %tmp, 0.000000e+00
%tmp1 = fcmp one float %tmp, 0.000000e+00
br i1 %tmp1, label %if0, label %endif

if0: ; preds = %entry
Expand Down
6 changes: 2 additions & 4 deletions llvm/test/Transforms/InstCombine/clamp-to-minmax.ll
Original file line number Diff line number Diff line change
Expand Up @@ -172,10 +172,8 @@ define float @clamp_negative_wrong_const(float %x) {
; Like @clamp_test_1 but both are min
define float @clamp_negative_same_op(float %x) {
; CHECK-LABEL: @clamp_negative_same_op(
; CHECK-NEXT: [[INNER_CMP_INV:%.*]] = fcmp fast oge float [[X:%.*]], 2.550000e+02
; CHECK-NEXT: [[INNER_SEL:%.*]] = select nnan ninf i1 [[INNER_CMP_INV]], float 2.550000e+02, float [[X]]
; CHECK-NEXT: [[OUTER_CMP:%.*]] = fcmp fast ult float [[X]], 1.000000e+00
; CHECK-NEXT: [[R:%.*]] = select i1 [[OUTER_CMP]], float [[INNER_SEL]], float 1.000000e+00
; CHECK-NEXT: [[OUTER_CMP_INV:%.*]] = fcmp fast oge float [[X:%.*]], 1.000000e+00
; CHECK-NEXT: [[R:%.*]] = select nnan ninf i1 [[OUTER_CMP_INV]], float 1.000000e+00, float [[X]]
; CHECK-NEXT: ret float [[R]]
;
%inner_cmp = fcmp fast ult float %x, 255.0
Expand Down
207 changes: 207 additions & 0 deletions llvm/test/Transforms/InstSimplify/domcondition.ll
Original file line number Diff line number Diff line change
Expand Up @@ -278,3 +278,210 @@ end:
}

declare void @foo(i32)


define i1 @simplify_fcmp_implied_by_dom_cond_range_true(float %x) {
; CHECK-LABEL: @simplify_fcmp_implied_by_dom_cond_range_true(
; CHECK-NEXT: [[CMP:%.*]] = fcmp olt float [[X:%.*]], 0.000000e+00
; CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
; CHECK: if.then:
; CHECK-NEXT: ret i1 true
; CHECK: if.else:
; CHECK-NEXT: ret i1 false
;
%cmp = fcmp olt float %x, 0.0
br i1 %cmp, label %if.then, label %if.else

if.then:
%cmp2 = fcmp olt float %x, 1.0
ret i1 %cmp2

if.else:
ret i1 false
}

define i1 @simplify_fcmp_in_else_implied_by_dom_cond_range_true(float %x) {
; CHECK-LABEL: @simplify_fcmp_in_else_implied_by_dom_cond_range_true(
; CHECK-NEXT: [[CMP:%.*]] = fcmp olt float [[X:%.*]], 1.000000e+00
; CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
; CHECK: if.then:
; CHECK-NEXT: ret i1 true
; CHECK: if.else:
; CHECK-NEXT: ret i1 true
;
%cmp = fcmp olt float %x, 1.0
br i1 %cmp, label %if.then, label %if.else

if.then:
ret i1 true

if.else:
%cmp2 = fcmp uge float %x, 0.5
ret i1 %cmp2
}

define i1 @simplify_fcmp_implied_by_dom_cond_range_false(float %x) {
; CHECK-LABEL: @simplify_fcmp_implied_by_dom_cond_range_false(
; CHECK-NEXT: [[CMP:%.*]] = fcmp olt float [[X:%.*]], 0.000000e+00
; CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
; CHECK: if.then:
; CHECK-NEXT: ret i1 false
; CHECK: if.else:
; CHECK-NEXT: ret i1 false
;
%cmp = fcmp olt float %x, 0.0
br i1 %cmp, label %if.then, label %if.else

if.then:
%cmp2 = fcmp ogt float %x, 1.0
ret i1 %cmp2

if.else:
ret i1 false
}

define i1 @simplify_fcmp_implied_by_dom_cond_pred_true(float %x, float %y) {
; CHECK-LABEL: @simplify_fcmp_implied_by_dom_cond_pred_true(
; CHECK-NEXT: [[CMP:%.*]] = fcmp olt float [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
; CHECK: if.then:
; CHECK-NEXT: ret i1 true
; CHECK: if.else:
; CHECK-NEXT: ret i1 false
;
%cmp = fcmp olt float %x, %y
br i1 %cmp, label %if.then, label %if.else

if.then:
%cmp2 = fcmp ole float %x, %y
ret i1 %cmp2

if.else:
ret i1 false
}

define i1 @simplify_fcmp_implied_by_dom_cond_pred_false(float %x, float %y) {
; CHECK-LABEL: @simplify_fcmp_implied_by_dom_cond_pred_false(
; CHECK-NEXT: [[CMP:%.*]] = fcmp olt float [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
; CHECK: if.then:
; CHECK-NEXT: ret i1 false
; CHECK: if.else:
; CHECK-NEXT: ret i1 false
;
%cmp = fcmp olt float %x, %y
br i1 %cmp, label %if.then, label %if.else

if.then:
%cmp2 = fcmp ogt float %x, %y
ret i1 %cmp2

if.else:
ret i1 false
}

define i1 @simplify_fcmp_implied_by_dom_cond_pred_commuted(float %x, float %y) {
; CHECK-LABEL: @simplify_fcmp_implied_by_dom_cond_pred_commuted(
; CHECK-NEXT: [[CMP:%.*]] = fcmp olt float [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
; CHECK: if.then:
; CHECK-NEXT: ret i1 true
; CHECK: if.else:
; CHECK-NEXT: ret i1 false
;
%cmp = fcmp olt float %x, %y
br i1 %cmp, label %if.then, label %if.else

if.then:
%cmp2 = fcmp oge float %y, %x
ret i1 %cmp2

if.else:
ret i1 false
}

; Negative tests

define i1 @simplify_fcmp_implied_by_dom_cond_wrong_range(float %x) {
; CHECK-LABEL: @simplify_fcmp_implied_by_dom_cond_wrong_range(
; CHECK-NEXT: [[CMP:%.*]] = fcmp olt float [[X:%.*]], 0.000000e+00
; CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
; CHECK: if.then:
; CHECK-NEXT: [[CMP2:%.*]] = fcmp olt float [[X]], -1.000000e+00
; CHECK-NEXT: ret i1 [[CMP2]]
; CHECK: if.else:
; CHECK-NEXT: ret i1 false
;
%cmp = fcmp olt float %x, 0.0
br i1 %cmp, label %if.then, label %if.else

if.then:
%cmp2 = fcmp olt float %x, -1.0
ret i1 %cmp2

if.else:
ret i1 false
}

define i1 @simplify_fcmp_implied_by_dom_cond_range_mismatched_operand(float %x, float %y) {
; CHECK-LABEL: @simplify_fcmp_implied_by_dom_cond_range_mismatched_operand(
; CHECK-NEXT: [[CMP:%.*]] = fcmp olt float [[X:%.*]], 0.000000e+00
; CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
; CHECK: if.then:
; CHECK-NEXT: [[CMP2:%.*]] = fcmp olt float [[Y:%.*]], 1.000000e+00
; CHECK-NEXT: ret i1 [[CMP2]]
; CHECK: if.else:
; CHECK-NEXT: ret i1 false
;
%cmp = fcmp olt float %x, 0.0
br i1 %cmp, label %if.then, label %if.else

if.then:
%cmp2 = fcmp olt float %y, 1.0
ret i1 %cmp2

if.else:
ret i1 false
}

define i1 @simplify_fcmp_implied_by_dom_cond_wrong_pred(float %x, float %y) {
; CHECK-LABEL: @simplify_fcmp_implied_by_dom_cond_wrong_pred(
; CHECK-NEXT: [[CMP:%.*]] = fcmp ole float [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
; CHECK: if.then:
; CHECK-NEXT: [[CMP2:%.*]] = fcmp olt float [[X]], [[Y]]
; CHECK-NEXT: ret i1 [[CMP2]]
; CHECK: if.else:
; CHECK-NEXT: ret i1 false
;
%cmp = fcmp ole float %x, %y
br i1 %cmp, label %if.then, label %if.else

if.then:
%cmp2 = fcmp olt float %x, %y
ret i1 %cmp2

if.else:
ret i1 false
}

define i1 @simplify_fcmp_implied_by_dom_cond_pred_mismatched_operand(float %x, float %y, float %z) {
; CHECK-LABEL: @simplify_fcmp_implied_by_dom_cond_pred_mismatched_operand(
; CHECK-NEXT: [[CMP:%.*]] = fcmp olt float [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
; CHECK: if.then:
; CHECK-NEXT: [[CMP2:%.*]] = fcmp ole float [[X]], [[Z:%.*]]
; CHECK-NEXT: ret i1 [[CMP2]]
; CHECK: if.else:
; CHECK-NEXT: ret i1 false
;
%cmp = fcmp olt float %x, %y
br i1 %cmp, label %if.then, label %if.else

if.then:
%cmp2 = fcmp ole float %x, %z
ret i1 %cmp2

if.else:
ret i1 false
}