[InstSimplify] Fold icmp of X and/or C1 and X and/or C2 into constant (#65905)

This patch simplifies the pattern `icmp X and/or C1, X and/or C2` when
one constant mask is the subset of the other.
If `C1 & C2 == C1`, `A = X and/or C1`, `B = X and/or C2`, we can do the
following folds:
`icmp ule A, B -> true`
`icmp ugt A, B -> false`
We can apply similar folds for signed predicates when `C1` and `C2` are
the same sign:
`icmp sle A, B -> true`
`icmp sgt A, B -> false`

Alive2: https://alive2.llvm.org/ce/z/Q4ekP5
Fixes #65833.
This commit is contained in:
Yingwei Zheng 2023-09-18 21:32:48 +08:00 committed by GitHub
parent fb4bdf361f
commit be2723da5c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 68 additions and 111 deletions

View File

@ -3427,7 +3427,7 @@ static Value *simplifyICmpWithBinOp(CmpInst::Predicate Pred, Value *LHS,
switch (LBO->getOpcode()) {
default:
break;
case Instruction::Shl:
case Instruction::Shl: {
bool NUW = Q.IIQ.hasNoUnsignedWrap(LBO) && Q.IIQ.hasNoUnsignedWrap(RBO);
bool NSW = Q.IIQ.hasNoSignedWrap(LBO) && Q.IIQ.hasNoSignedWrap(RBO);
if (!NUW || (ICmpInst::isSigned(Pred) && !NSW) ||
@ -3436,6 +3436,38 @@ static Value *simplifyICmpWithBinOp(CmpInst::Predicate Pred, Value *LHS,
if (Value *V = simplifyICmpInst(Pred, LBO->getOperand(1),
RBO->getOperand(1), Q, MaxRecurse - 1))
return V;
break;
}
// If C1 & C2 == C1, A = X and/or C1, B = X and/or C2:
// icmp ule A, B -> true
// icmp ugt A, B -> false
// icmp sle A, B -> true (C1 and C2 are the same sign)
// icmp sgt A, B -> false (C1 and C2 are the same sign)
case Instruction::And:
case Instruction::Or: {
const APInt *C1, *C2;
if (ICmpInst::isRelational(Pred) &&
match(LBO->getOperand(1), m_APInt(C1)) &&
match(RBO->getOperand(1), m_APInt(C2))) {
if (!C1->isSubsetOf(*C2)) {
std::swap(C1, C2);
Pred = ICmpInst::getSwappedPredicate(Pred);
}
if (C1->isSubsetOf(*C2)) {
if (Pred == ICmpInst::ICMP_ULE)
return ConstantInt::getTrue(getCompareTy(LHS));
if (Pred == ICmpInst::ICMP_UGT)
return ConstantInt::getFalse(getCompareTy(LHS));
if (C1->isNonNegative() == C2->isNonNegative()) {
if (Pred == ICmpInst::ICMP_SLE)
return ConstantInt::getTrue(getCompareTy(LHS));
if (Pred == ICmpInst::ICMP_SGT)
return ConstantInt::getFalse(getCompareTy(LHS));
}
}
}
break;
}
}
}

View File

@ -1921,10 +1921,7 @@ define i1 @tautological8(i32 %A, i32 %B) {
define i1 @tautological9(i32 %A) {
; CHECK-LABEL: @tautological9(
; CHECK-NEXT: [[C1:%.*]] = and i32 [[A:%.*]], 1
; CHECK-NEXT: [[C2:%.*]] = and i32 [[A]], 3
; CHECK-NEXT: [[D:%.*]] = icmp ugt i32 [[C1]], [[C2]]
; CHECK-NEXT: ret i1 [[D]]
; CHECK-NEXT: ret i1 false
;
%C1 = and i32 %A, 1
%C2 = and i32 %A, 3
@ -1934,10 +1931,7 @@ define i1 @tautological9(i32 %A) {
define <2 x i1> @tautological9_vec(<2 x i32> %A) {
; CHECK-LABEL: @tautological9_vec(
; CHECK-NEXT: [[C1:%.*]] = and <2 x i32> [[A:%.*]], <i32 1, i32 1>
; CHECK-NEXT: [[C2:%.*]] = and <2 x i32> [[A]], <i32 3, i32 3>
; CHECK-NEXT: [[D:%.*]] = icmp ugt <2 x i32> [[C1]], [[C2]]
; CHECK-NEXT: ret <2 x i1> [[D]]
; CHECK-NEXT: ret <2 x i1> zeroinitializer
;
%C1 = and <2 x i32> %A, <i32 1, i32 1>
%C2 = and <2 x i32> %A, <i32 3, i32 3>
@ -1947,10 +1941,7 @@ define <2 x i1> @tautological9_vec(<2 x i32> %A) {
define i1 @tautological10(i32 %A) {
; CHECK-LABEL: @tautological10(
; CHECK-NEXT: [[C1:%.*]] = and i32 [[A:%.*]], 1
; CHECK-NEXT: [[C2:%.*]] = and i32 [[A]], 3
; CHECK-NEXT: [[D:%.*]] = icmp ule i32 [[C1]], [[C2]]
; CHECK-NEXT: ret i1 [[D]]
; CHECK-NEXT: ret i1 true
;
%C1 = and i32 %A, 1
%C2 = and i32 %A, 3
@ -1960,10 +1951,7 @@ define i1 @tautological10(i32 %A) {
define i1 @tautological11(i32 %A) {
; CHECK-LABEL: @tautological11(
; CHECK-NEXT: [[C1:%.*]] = or i32 [[A:%.*]], 1
; CHECK-NEXT: [[C2:%.*]] = or i32 [[A]], 3
; CHECK-NEXT: [[D:%.*]] = icmp ule i32 [[C1]], [[C2]]
; CHECK-NEXT: ret i1 [[D]]
; CHECK-NEXT: ret i1 true
;
%C1 = or i32 %A, 1
%C2 = or i32 %A, 3
@ -1973,10 +1961,7 @@ define i1 @tautological11(i32 %A) {
define i1 @tautological12(i32 %A) {
; CHECK-LABEL: @tautological12(
; CHECK-NEXT: [[C1:%.*]] = or i32 [[A:%.*]], 1
; CHECK-NEXT: [[C2:%.*]] = or i32 [[A]], 3
; CHECK-NEXT: [[D:%.*]] = icmp ugt i32 [[C1]], [[C2]]
; CHECK-NEXT: ret i1 [[D]]
; CHECK-NEXT: ret i1 false
;
%C1 = or i32 %A, 1
%C2 = or i32 %A, 3
@ -1986,10 +1971,7 @@ define i1 @tautological12(i32 %A) {
define i1 @tautological13(i32 %A) {
; CHECK-LABEL: @tautological13(
; CHECK-NEXT: [[C1:%.*]] = or i32 [[A:%.*]], 1
; CHECK-NEXT: [[C2:%.*]] = or i32 [[A]], 3
; CHECK-NEXT: [[D:%.*]] = icmp ult i32 [[C2]], [[C1]]
; CHECK-NEXT: ret i1 [[D]]
; CHECK-NEXT: ret i1 false
;
%C1 = or i32 %A, 1
%C2 = or i32 %A, 3
@ -1999,10 +1981,7 @@ define i1 @tautological13(i32 %A) {
define i1 @tautological14(i32 %A) {
; CHECK-LABEL: @tautological14(
; CHECK-NEXT: [[C1:%.*]] = or i32 [[A:%.*]], 1
; CHECK-NEXT: [[C2:%.*]] = or i32 [[A]], 3
; CHECK-NEXT: [[D:%.*]] = icmp uge i32 [[C2]], [[C1]]
; CHECK-NEXT: ret i1 [[D]]
; CHECK-NEXT: ret i1 true
;
%C1 = or i32 %A, 1
%C2 = or i32 %A, 3
@ -2012,10 +1991,7 @@ define i1 @tautological14(i32 %A) {
define i1 @tautological15(i32 %A) {
; CHECK-LABEL: @tautological15(
; CHECK-NEXT: [[C1:%.*]] = and i32 [[A:%.*]], 1
; CHECK-NEXT: [[C2:%.*]] = and i32 [[A]], 3
; CHECK-NEXT: [[D:%.*]] = icmp uge i32 [[C2]], [[C1]]
; CHECK-NEXT: ret i1 [[D]]
; CHECK-NEXT: ret i1 true
;
%C1 = and i32 %A, 1
%C2 = and i32 %A, 3
@ -2025,10 +2001,7 @@ define i1 @tautological15(i32 %A) {
define i1 @tautological16(i32 %A) {
; CHECK-LABEL: @tautological16(
; CHECK-NEXT: [[C1:%.*]] = and i32 [[A:%.*]], 1
; CHECK-NEXT: [[C2:%.*]] = and i32 [[A]], 3
; CHECK-NEXT: [[D:%.*]] = icmp ult i32 [[C2]], [[C1]]
; CHECK-NEXT: ret i1 [[D]]
; CHECK-NEXT: ret i1 false
;
%C1 = and i32 %A, 1
%C2 = and i32 %A, 3
@ -2142,10 +2115,7 @@ define i1 @tautological16_negative(i32 %A) {
define i1 @tautological17_subset1(i32 %A) {
; CHECK-LABEL: @tautological17_subset1(
; CHECK-NEXT: [[C1:%.*]] = and i32 [[A:%.*]], 1
; CHECK-NEXT: [[C2:%.*]] = and i32 [[A]], 3
; CHECK-NEXT: [[D:%.*]] = icmp sgt i32 [[C1]], [[C2]]
; CHECK-NEXT: ret i1 [[D]]
; CHECK-NEXT: ret i1 false
;
%C1 = and i32 %A, 1
%C2 = and i32 %A, 3
@ -2155,10 +2125,7 @@ define i1 @tautological17_subset1(i32 %A) {
define i1 @tautological17_subset2(i32 %A) {
; CHECK-LABEL: @tautological17_subset2(
; CHECK-NEXT: [[C1:%.*]] = and i32 [[A:%.*]], -4
; CHECK-NEXT: [[C2:%.*]] = and i32 [[A]], -3
; CHECK-NEXT: [[D:%.*]] = icmp sgt i32 [[C1]], [[C2]]
; CHECK-NEXT: ret i1 [[D]]
; CHECK-NEXT: ret i1 false
;
%C1 = and i32 %A, -4
%C2 = and i32 %A, -3
@ -2181,10 +2148,7 @@ define i1 @tautological17_negative(i32 %A) {
define i1 @tautological18_subset1(i32 %A) {
; CHECK-LABEL: @tautological18_subset1(
; CHECK-NEXT: [[C1:%.*]] = and i32 [[A:%.*]], 1
; CHECK-NEXT: [[C2:%.*]] = and i32 [[A]], 3
; CHECK-NEXT: [[D:%.*]] = icmp sle i32 [[C1]], [[C2]]
; CHECK-NEXT: ret i1 [[D]]
; CHECK-NEXT: ret i1 true
;
%C1 = and i32 %A, 1
%C2 = and i32 %A, 3
@ -2194,10 +2158,7 @@ define i1 @tautological18_subset1(i32 %A) {
define i1 @tautological18_subset2(i32 %A) {
; CHECK-LABEL: @tautological18_subset2(
; CHECK-NEXT: [[C1:%.*]] = and i32 [[A:%.*]], -4
; CHECK-NEXT: [[C2:%.*]] = and i32 [[A]], -3
; CHECK-NEXT: [[D:%.*]] = icmp sle i32 [[C1]], [[C2]]
; CHECK-NEXT: ret i1 [[D]]
; CHECK-NEXT: ret i1 true
;
%C1 = and i32 %A, -4
%C2 = and i32 %A, -3
@ -2220,10 +2181,7 @@ define i1 @tautological18_negative(i32 %A) {
define i1 @tautological19_subset1(i32 %A) {
; CHECK-LABEL: @tautological19_subset1(
; CHECK-NEXT: [[C1:%.*]] = or i32 [[A:%.*]], 1
; CHECK-NEXT: [[C2:%.*]] = or i32 [[A]], 3
; CHECK-NEXT: [[D:%.*]] = icmp sgt i32 [[C1]], [[C2]]
; CHECK-NEXT: ret i1 [[D]]
; CHECK-NEXT: ret i1 false
;
%C1 = or i32 %A, 1
%C2 = or i32 %A, 3
@ -2233,10 +2191,7 @@ define i1 @tautological19_subset1(i32 %A) {
define i1 @tautological19_subset2(i32 %A) {
; CHECK-LABEL: @tautological19_subset2(
; CHECK-NEXT: [[C1:%.*]] = and i32 [[A:%.*]], -4
; CHECK-NEXT: [[C2:%.*]] = and i32 [[A]], -3
; CHECK-NEXT: [[D:%.*]] = icmp sgt i32 [[C1]], [[C2]]
; CHECK-NEXT: ret i1 [[D]]
; CHECK-NEXT: ret i1 false
;
%C1 = and i32 %A, -4
%C2 = and i32 %A, -3
@ -2259,10 +2214,7 @@ define i1 @tautological19_negative(i32 %A) {
define i1 @tautological20_subset1(i32 %A) {
; CHECK-LABEL: @tautological20_subset1(
; CHECK-NEXT: [[C1:%.*]] = and i32 [[A:%.*]], 1
; CHECK-NEXT: [[C2:%.*]] = and i32 [[A]], 3
; CHECK-NEXT: [[D:%.*]] = icmp sle i32 [[C1]], [[C2]]
; CHECK-NEXT: ret i1 [[D]]
; CHECK-NEXT: ret i1 true
;
%C1 = and i32 %A, 1
%C2 = and i32 %A, 3
@ -2272,10 +2224,7 @@ define i1 @tautological20_subset1(i32 %A) {
define i1 @tautological20_subset2(i32 %A) {
; CHECK-LABEL: @tautological20_subset2(
; CHECK-NEXT: [[C1:%.*]] = and i32 [[A:%.*]], -4
; CHECK-NEXT: [[C2:%.*]] = and i32 [[A]], -3
; CHECK-NEXT: [[D:%.*]] = icmp sle i32 [[C1]], [[C2]]
; CHECK-NEXT: ret i1 [[D]]
; CHECK-NEXT: ret i1 true
;
%C1 = and i32 %A, -4
%C2 = and i32 %A, -3

View File

@ -2337,9 +2337,7 @@ false:
define i8 @umin_and_mask(i8 %x) {
; CHECK-LABEL: @umin_and_mask(
; CHECK-NEXT: [[AND1:%.*]] = and i8 [[X:%.*]], 1
; CHECK-NEXT: [[AND2:%.*]] = and i8 [[X]], 3
; CHECK-NEXT: [[VAL:%.*]] = call i8 @llvm.umin.i8(i8 [[AND1]], i8 [[AND2]])
; CHECK-NEXT: ret i8 [[VAL]]
; CHECK-NEXT: ret i8 [[AND1]]
;
%and1 = and i8 %x, 1
%and2 = and i8 %x, 3
@ -2349,10 +2347,8 @@ define i8 @umin_and_mask(i8 %x) {
define i8 @umax_and_mask(i8 %x) {
; CHECK-LABEL: @umax_and_mask(
; CHECK-NEXT: [[AND1:%.*]] = and i8 [[X:%.*]], 1
; CHECK-NEXT: [[AND2:%.*]] = and i8 [[X]], 3
; CHECK-NEXT: [[VAL:%.*]] = call i8 @llvm.umax.i8(i8 [[AND1]], i8 [[AND2]])
; CHECK-NEXT: ret i8 [[VAL]]
; CHECK-NEXT: [[AND2:%.*]] = and i8 [[X:%.*]], 3
; CHECK-NEXT: ret i8 [[AND2]]
;
%and1 = and i8 %x, 1
%and2 = and i8 %x, 3
@ -2363,9 +2359,7 @@ define i8 @umax_and_mask(i8 %x) {
define i8 @umin_or_mask(i8 %x) {
; CHECK-LABEL: @umin_or_mask(
; CHECK-NEXT: [[AND1:%.*]] = or i8 [[X:%.*]], 1
; CHECK-NEXT: [[AND2:%.*]] = or i8 [[X]], 3
; CHECK-NEXT: [[VAL:%.*]] = call i8 @llvm.umin.i8(i8 [[AND1]], i8 [[AND2]])
; CHECK-NEXT: ret i8 [[VAL]]
; CHECK-NEXT: ret i8 [[AND1]]
;
%and1 = or i8 %x, 1
%and2 = or i8 %x, 3
@ -2375,10 +2369,8 @@ define i8 @umin_or_mask(i8 %x) {
define i8 @umax_or_mask(i8 %x) {
; CHECK-LABEL: @umax_or_mask(
; CHECK-NEXT: [[AND1:%.*]] = or i8 [[X:%.*]], 1
; CHECK-NEXT: [[AND2:%.*]] = or i8 [[X]], 3
; CHECK-NEXT: [[VAL:%.*]] = call i8 @llvm.umax.i8(i8 [[AND1]], i8 [[AND2]])
; CHECK-NEXT: ret i8 [[VAL]]
; CHECK-NEXT: [[AND2:%.*]] = or i8 [[X:%.*]], 3
; CHECK-NEXT: ret i8 [[AND2]]
;
%and1 = or i8 %x, 1
%and2 = or i8 %x, 3
@ -2441,9 +2433,7 @@ define i8 @umax_or_mask_negative(i8 %x) {
define i8 @smin_and_mask_subset1(i8 %x) {
; CHECK-LABEL: @smin_and_mask_subset1(
; CHECK-NEXT: [[AND1:%.*]] = and i8 [[X:%.*]], 1
; CHECK-NEXT: [[AND2:%.*]] = and i8 [[X]], 3
; CHECK-NEXT: [[VAL:%.*]] = call i8 @llvm.smin.i8(i8 [[AND1]], i8 [[AND2]])
; CHECK-NEXT: ret i8 [[VAL]]
; CHECK-NEXT: ret i8 [[AND1]]
;
%and1 = and i8 %x, 1
%and2 = and i8 %x, 3
@ -2453,10 +2443,8 @@ define i8 @smin_and_mask_subset1(i8 %x) {
define i8 @smax_and_mask_subset1(i8 %x) {
; CHECK-LABEL: @smax_and_mask_subset1(
; CHECK-NEXT: [[AND1:%.*]] = and i8 [[X:%.*]], 1
; CHECK-NEXT: [[AND2:%.*]] = and i8 [[X]], 3
; CHECK-NEXT: [[VAL:%.*]] = call i8 @llvm.smax.i8(i8 [[AND1]], i8 [[AND2]])
; CHECK-NEXT: ret i8 [[VAL]]
; CHECK-NEXT: [[AND2:%.*]] = and i8 [[X:%.*]], 3
; CHECK-NEXT: ret i8 [[AND2]]
;
%and1 = and i8 %x, 1
%and2 = and i8 %x, 3
@ -2467,9 +2455,7 @@ define i8 @smax_and_mask_subset1(i8 %x) {
define i8 @smin_or_mask_subset1(i8 %x) {
; CHECK-LABEL: @smin_or_mask_subset1(
; CHECK-NEXT: [[AND1:%.*]] = or i8 [[X:%.*]], 1
; CHECK-NEXT: [[AND2:%.*]] = or i8 [[X]], 3
; CHECK-NEXT: [[VAL:%.*]] = call i8 @llvm.smin.i8(i8 [[AND1]], i8 [[AND2]])
; CHECK-NEXT: ret i8 [[VAL]]
; CHECK-NEXT: ret i8 [[AND1]]
;
%and1 = or i8 %x, 1
%and2 = or i8 %x, 3
@ -2479,10 +2465,8 @@ define i8 @smin_or_mask_subset1(i8 %x) {
define i8 @smax_or_mask_subset1(i8 %x) {
; CHECK-LABEL: @smax_or_mask_subset1(
; CHECK-NEXT: [[AND1:%.*]] = or i8 [[X:%.*]], 1
; CHECK-NEXT: [[AND2:%.*]] = or i8 [[X]], 3
; CHECK-NEXT: [[VAL:%.*]] = call i8 @llvm.smax.i8(i8 [[AND1]], i8 [[AND2]])
; CHECK-NEXT: ret i8 [[VAL]]
; CHECK-NEXT: [[AND2:%.*]] = or i8 [[X:%.*]], 3
; CHECK-NEXT: ret i8 [[AND2]]
;
%and1 = or i8 %x, 1
%and2 = or i8 %x, 3
@ -2493,9 +2477,7 @@ define i8 @smax_or_mask_subset1(i8 %x) {
define i8 @smin_and_mask_subset2(i8 %x) {
; CHECK-LABEL: @smin_and_mask_subset2(
; CHECK-NEXT: [[AND1:%.*]] = and i8 [[X:%.*]], -4
; CHECK-NEXT: [[AND2:%.*]] = and i8 [[X]], -3
; CHECK-NEXT: [[VAL:%.*]] = call i8 @llvm.smin.i8(i8 [[AND1]], i8 [[AND2]])
; CHECK-NEXT: ret i8 [[VAL]]
; CHECK-NEXT: ret i8 [[AND1]]
;
%and1 = and i8 %x, -4
%and2 = and i8 %x, -3
@ -2505,10 +2487,8 @@ define i8 @smin_and_mask_subset2(i8 %x) {
define i8 @smax_and_mask_subset2(i8 %x) {
; CHECK-LABEL: @smax_and_mask_subset2(
; CHECK-NEXT: [[AND1:%.*]] = and i8 [[X:%.*]], -4
; CHECK-NEXT: [[AND2:%.*]] = and i8 [[X]], -3
; CHECK-NEXT: [[VAL:%.*]] = call i8 @llvm.smax.i8(i8 [[AND1]], i8 [[AND2]])
; CHECK-NEXT: ret i8 [[VAL]]
; CHECK-NEXT: [[AND2:%.*]] = and i8 [[X:%.*]], -3
; CHECK-NEXT: ret i8 [[AND2]]
;
%and1 = and i8 %x, -4
%and2 = and i8 %x, -3
@ -2519,9 +2499,7 @@ define i8 @smax_and_mask_subset2(i8 %x) {
define i8 @smin_or_mask_subset2(i8 %x) {
; CHECK-LABEL: @smin_or_mask_subset2(
; CHECK-NEXT: [[AND1:%.*]] = or i8 [[X:%.*]], -4
; CHECK-NEXT: [[AND2:%.*]] = or i8 [[X]], -3
; CHECK-NEXT: [[VAL:%.*]] = call i8 @llvm.smin.i8(i8 [[AND1]], i8 [[AND2]])
; CHECK-NEXT: ret i8 [[VAL]]
; CHECK-NEXT: ret i8 [[AND1]]
;
%and1 = or i8 %x, -4
%and2 = or i8 %x, -3
@ -2531,10 +2509,8 @@ define i8 @smin_or_mask_subset2(i8 %x) {
define i8 @smax_or_mask_subset2(i8 %x) {
; CHECK-LABEL: @smax_or_mask_subset2(
; CHECK-NEXT: [[AND1:%.*]] = or i8 [[X:%.*]], -4
; CHECK-NEXT: [[AND2:%.*]] = or i8 [[X]], -3
; CHECK-NEXT: [[VAL:%.*]] = call i8 @llvm.smax.i8(i8 [[AND1]], i8 [[AND2]])
; CHECK-NEXT: ret i8 [[VAL]]
; CHECK-NEXT: [[AND2:%.*]] = or i8 [[X:%.*]], -3
; CHECK-NEXT: ret i8 [[AND2]]
;
%and1 = or i8 %x, -4
%and2 = or i8 %x, -3