Skip to content
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

[ValueTracking] Improve isImpliedCondICmps to handle binops #69840

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
54 changes: 45 additions & 9 deletions llvm/lib/Analysis/ValueTracking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8211,14 +8211,11 @@ isImpliedCondMatchingOperands(CmpInst::Predicate LPred,
return std::nullopt;
}

/// Return true if "icmp LPred X, LC" implies "icmp RPred X, RC" is true.
/// Return false if "icmp LPred X, LC" implies "icmp RPred X, RC" is false.
/// Return true if `X in DomCR` implies `X in CR` is true.
/// Return false if `X in DomCR` implies `X in CR` is false.
/// Otherwise, return std::nullopt if we can't infer anything.
static std::optional<bool> isImpliedCondCommonOperandWithConstants(
CmpInst::Predicate LPred, const APInt &LC, CmpInst::Predicate RPred,
const APInt &RC) {
ConstantRange DomCR = ConstantRange::makeExactICmpRegion(LPred, LC);
ConstantRange CR = ConstantRange::makeExactICmpRegion(RPred, RC);
static std::optional<bool> isImpliedCondWithRange(const ConstantRange &DomCR,
const ConstantRange &CR) {
ConstantRange Intersection = DomCR.intersectWith(CR);
ConstantRange Difference = DomCR.difference(CR);
if (Intersection.isEmptySet())
Expand All @@ -8228,6 +8225,17 @@ static std::optional<bool> isImpliedCondCommonOperandWithConstants(
return std::nullopt;
}

/// Return true if "icmp LPred X, LC" implies "icmp RPred X, RC" is true.
/// Return false if "icmp LPred X, LC" implies "icmp RPred X, RC" is false.
/// Otherwise, return std::nullopt if we can't infer anything.
static std::optional<bool> isImpliedCondCommonOperandWithConstants(
CmpInst::Predicate LPred, const APInt &LC, CmpInst::Predicate RPred,
const APInt &RC) {
ConstantRange DomCR = ConstantRange::makeExactICmpRegion(LPred, LC);
ConstantRange CR = ConstantRange::makeExactICmpRegion(RPred, RC);
return isImpliedCondWithRange(DomCR, CR);
}

/// 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.
Expand All @@ -8247,8 +8255,36 @@ static std::optional<bool> isImpliedCondICmps(const ICmpInst *LHS,
// Can we infer anything when the 0-operands match and the 1-operands are
// constants (not necessarily matching)?
const APInt *LC, *RC;
if (L0 == R0 && match(L1, m_APInt(LC)) && match(R1, m_APInt(RC)))
return isImpliedCondCommonOperandWithConstants(LPred, *LC, RPred, *RC);
if (match(L1, m_APInt(LC)) && match(R1, m_APInt(RC))) {
if (L0 == R0)
return isImpliedCondCommonOperandWithConstants(LPred, *LC, RPred, *RC);

// handle R0 = L0 binop V and R0 = V binop L0
Value *R0Op1 = nullptr;
if (match(R0, m_c_BinOp(m_Specific(L0), m_Value(R0Op1)))) {
ConstantRange LHSRange = ConstantRange::makeExactICmpRegion(LPred, *LC);
ConstantRange CR = ConstantRange::makeExactICmpRegion(RPred, *RC);
// TODO: use contextual information from SimplifyQuery
ConstantRange RHSRange =
computeConstantRange(R0Op1, ICmpInst::isSigned(RPred),
/*UseInstrInfo*/ true, /*AC*/ nullptr,
/*CtxI*/ nullptr, /*DT*/ nullptr, Depth);
auto *BO = cast<BinaryOperator>(R0);
if (BO->getOperand(0) != L0)
std::swap(LHSRange, RHSRange);
unsigned NoWrapKind = 0;
if (auto *OBO = dyn_cast<OverflowingBinaryOperator>(BO)) {
if (OBO->hasNoUnsignedWrap())
NoWrapKind |= OverflowingBinaryOperator::NoUnsignedWrap;
if (OBO->hasNoSignedWrap())
NoWrapKind |= OverflowingBinaryOperator::NoSignedWrap;
}
ConstantRange Range =
LHSRange.overflowingBinaryOp(BO->getOpcode(), RHSRange, NoWrapKind);
dtcxzyw marked this conversation as resolved.
Show resolved Hide resolved
if (auto Res = isImpliedCondWithRange(Range, CR))
return Res;
}
}

// Can we infer anything when the two compares have matching operands?
bool AreSwappedOps;
Expand Down
206 changes: 206 additions & 0 deletions llvm/test/Analysis/ValueTracking/implied-icmp-binop.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 3
; RUN: opt -passes=instcombine -S < %s | FileCheck %s

; Tests from PR68799

define i1 @f_and(i32 %x, i32 %y) {
; CHECK-LABEL: define i1 @f_and(
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: ret i1 false
;
entry:
%cmp = icmp ne i32 %x, 0
%0 = or i32 %x, %y
%and14 = icmp eq i32 %0, 0
%and1115 = and i1 %cmp, %and14
ret i1 %and1115
}

define i1 @f_or(i32 %x, i32 %y) {
; CHECK-LABEL: define i1 @f_or(
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: ret i1 true
;
entry:
%cmp.not = icmp eq i32 %x, 0
%0 = or i32 %x, %y
%or14 = icmp ne i32 %0, 0
%or1115 = or i1 %cmp.not, %or14
ret i1 %or1115
}

; Tests for more binops

define i1 @f_add(i32 %x, i32 %y) {
; CHECK-LABEL: define i1 @f_add(
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: ret i1 false
;
entry:
%yr = and i32 %y, 7
%cmp = icmp ult i32 %x, 8
%0 = add i32 %yr, %x
%cmp2 = icmp ugt i32 %0, 16
%and = and i1 %cmp, %cmp2
ret i1 %and
}

define i1 @f_add_nsw(i32 %x, i32 %y) {
; CHECK-LABEL: define i1 @f_add_nsw(
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: ret i1 false
;
entry:
%yr = and i32 %y, 2147483647
%cmp = icmp sgt i32 %x, 5
%0 = add nsw i32 %yr, %x
%cmp2 = icmp slt i32 %0, 5
%and = and i1 %cmp, %cmp2
ret i1 %and
}

define i1 @f_add_nuw(i32 %x, i32 %y) {
; CHECK-LABEL: define i1 @f_add_nuw(
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: ret i1 false
;
entry:
%cmp = icmp ugt i32 %x, 1
%0 = add nuw i32 %x, %y
%cmp2 = icmp eq i32 %0, 1
%and = and i1 %cmp, %cmp2
ret i1 %and
}

define i1 @f_sub_nsw(i32 %x, i32 %y) {
; CHECK-LABEL: define i1 @f_sub_nsw(
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[X]], 5
; CHECK-NEXT: ret i1 [[CMP]]
;
entry:
%yr = and i32 %y, 2147483647
%cmp = icmp slt i32 %x, 5
%0 = sub nsw i32 %x, %yr
%cmp2 = icmp slt i32 %0, 5
%and = and i1 %cmp, %cmp2
ret i1 %and
}

define i1 @f_sub_nuw(i32 %x, i32 %y) {
; CHECK-LABEL: define i1 @f_sub_nuw(
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: ret i1 false
;
entry:
%cmp = icmp ult i32 %x, 5
%0 = sub nuw i32 %x, %y
%cmp2 = icmp eq i32 %0, 6
%and = and i1 %cmp, %cmp2
ret i1 %and
}

; Negative tests

; non-constant range
define i1 @f_add_nofold1(i32 %x, i32 %y, i32 %z) {
; CHECK-LABEL: define i1 @f_add_nofold1(
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]], i32 [[Z:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[YR:%.*]] = and i32 [[Y]], 7
; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[X]], [[Z]]
; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[YR]], [[X]]
; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i32 [[TMP0]], 16
; CHECK-NEXT: [[AND:%.*]] = and i1 [[CMP]], [[CMP2]]
; CHECK-NEXT: ret i1 [[AND]]
;
entry:
%yr = and i32 %y, 7
%cmp = icmp ult i32 %x, %z
%0 = add i32 %yr, %x
%cmp2 = icmp ugt i32 %0, 16
%and = and i1 %cmp, %cmp2
ret i1 %and
}

define i1 @f_add_nofold2(i32 %x, i32 %y, i32 %z) {
; CHECK-LABEL: define i1 @f_add_nofold2(
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]], i32 [[Z:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[YR:%.*]] = and i32 [[Y]], 7
; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[X]], 8
; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[YR]], [[X]]
; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i32 [[TMP0]], [[Z]]
; CHECK-NEXT: [[AND:%.*]] = and i1 [[CMP]], [[CMP2]]
; CHECK-NEXT: ret i1 [[AND]]
;
entry:
%yr = and i32 %y, 7
%cmp = icmp ult i32 %x, 8
%0 = add i32 %yr, %x
%cmp2 = icmp ugt i32 %0, %z
%and = and i1 %cmp, %cmp2
ret i1 %and
}

; narrower range
define i1 @f_add_nofold3(i32 %x, i32 %y) {
; CHECK-LABEL: define i1 @f_add_nofold3(
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[YR:%.*]] = and i32 [[Y]], 7
; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[X]], 8
; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[YR]], [[X]]
; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i32 [[TMP0]], 10
; CHECK-NEXT: [[AND:%.*]] = and i1 [[CMP]], [[CMP2]]
; CHECK-NEXT: ret i1 [[AND]]
;
entry:
%yr = and i32 %y, 7
%cmp = icmp ult i32 %x, 8
%0 = add i32 %yr, %x
%cmp2 = icmp ugt i32 %0, 10
%and = and i1 %cmp, %cmp2
ret i1 %and
}

; sub is not commutative
define i1 @f_sub_nsw_nofold(i32 %x, i32 %y) {
; CHECK-LABEL: define i1 @f_sub_nsw_nofold(
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[YR:%.*]] = and i32 [[Y]], 2147483647
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[X]], 5
; CHECK-NEXT: [[TMP0:%.*]] = sub nsw i32 [[YR]], [[X]]
; CHECK-NEXT: [[CMP2:%.*]] = icmp slt i32 [[TMP0]], 5
; CHECK-NEXT: [[AND:%.*]] = and i1 [[CMP]], [[CMP2]]
; CHECK-NEXT: ret i1 [[AND]]
;
entry:
%yr = and i32 %y, 2147483647
%cmp = icmp slt i32 %x, 5
%0 = sub nsw i32 %yr, %x
%cmp2 = icmp slt i32 %0, 5
%and = and i1 %cmp, %cmp2
ret i1 %and
}

define i1 @pr69038(i32 %a, i32 %b) {
; CHECK-LABEL: define i1 @pr69038(
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne i32 [[A]], 0
; CHECK-NEXT: ret i1 [[TOBOOL]]
;
%tobool = icmp ne i32 %a, 0
%or = or i32 %a, %b
%tobool1 = icmp ne i32 %or, 0
%and = and i1 %tobool, %tobool1
ret i1 %and
}
Loading
Loading