Skip to content

Commit

Permalink
[InstCombine] canonicalize icmp with trunc op into mask and cmp, part 2
Browse files Browse the repository at this point in the history
If C is a high-bit mask:
(trunc X) u< C --> (X & C) != C (are any masked-high-bits clear?)

If C is low-bit mask:
(trunc X) u> C --> (X & ~C) != 0 (are any masked-high-bits set?)

If C is not-of-power-of-2 (one clear bit):
(trunc X) u> C --> (X & (C+1)) == C+1 (are all masked-high-bits set?)

This extends the fold added with:
acabad9 (https://alive2.llvm.org/ce/z/aFr7qV)

Using decomposeBitTestICmp() to generalize this is a planned follow-up, but that requires removing an inverse fold.

Here are Alive2 generalizations for these folds:
https://alive2.llvm.org/ce/z/u-ZpC_ (ult, the previous patch)
https://alive2.llvm.org/ce/z/YsuAu2 (ult, this patch)
https://alive2.llvm.org/ce/z/ekktQP (ugt, low bitmask)
https://alive2.llvm.org/ce/z/pJY9wR (ugt, one clear bit)

Differential Revision: https://reviews.llvm.org/D112634
  • Loading branch information
rotateright committed Nov 16, 2021
1 parent 900cc1a commit 8fce94f
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 15 deletions.
29 changes: 26 additions & 3 deletions llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
Expand Up @@ -4627,16 +4627,39 @@ static Instruction *foldICmpWithTrunc(ICmpInst &ICmp,
unsigned SrcBits = X->getType()->getScalarSizeInBits();
if (Pred == ICmpInst::ICMP_ULT) {
if (C->isPowerOf2()) {
// If C is a power-of-2:
// If C is a power-of-2 (one set bit):
// (trunc X) u< C --> (X & -C) == 0 (are all masked-high-bits clear?)
Constant *MaskC = ConstantInt::get(X->getType(), (-*C).zext(SrcBits));
Value *And = Builder.CreateAnd(X, MaskC);
Constant *Zero = ConstantInt::getNullValue(X->getType());
return new ICmpInst(ICmpInst::ICMP_EQ, And, Zero);
}
// TODO: Handle C is negative-power-of-2.
// If C is a negative power-of-2 (high-bit mask):
// (trunc X) u< C --> (X & C) != C (are any masked-high-bits clear?)
if (C->isNegatedPowerOf2()) {
Constant *MaskC = ConstantInt::get(X->getType(), C->zext(SrcBits));
Value *And = Builder.CreateAnd(X, MaskC);
return new ICmpInst(ICmpInst::ICMP_NE, And, MaskC);
}
}

if (Pred == ICmpInst::ICMP_UGT) {
// If C is a low-bit-mask (C+1 is a power-of-2):
// (trunc X) u> C --> (X & ~C) != 0 (are any masked-high-bits set?)
if (C->isMask()) {
Constant *MaskC = ConstantInt::get(X->getType(), (~*C).zext(SrcBits));
Value *And = Builder.CreateAnd(X, MaskC);
Constant *Zero = ConstantInt::getNullValue(X->getType());
return new ICmpInst(ICmpInst::ICMP_NE, And, Zero);
}
// If C is not-of-power-of-2 (one clear bit):
// (trunc X) u> C --> (X & (C+1)) == C+1 (are all masked-high-bits set?)
if ((~*C).isPowerOf2()) {
Constant *MaskC = ConstantInt::get(X->getType(), (*C + 1).zext(SrcBits));
Value *And = Builder.CreateAnd(X, MaskC);
return new ICmpInst(ICmpInst::ICMP_EQ, And, MaskC);
}
}
// TODO: Handle ugt.

return nullptr;
}
Expand Down
36 changes: 24 additions & 12 deletions llvm/test/Transforms/InstCombine/icmp-trunc.ll
Expand Up @@ -71,8 +71,8 @@ define i1 @PR52260(i32 %x) {

define i1 @ult_192(i32 %x) {
; CHECK-LABEL: @ult_192(
; CHECK-NEXT: [[T:%.*]] = trunc i32 [[X:%.*]] to i8
; CHECK-NEXT: [[R:%.*]] = icmp ult i8 [[T]], -64
; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[X:%.*]], 192
; CHECK-NEXT: [[R:%.*]] = icmp ne i32 [[TMP1]], 192
; CHECK-NEXT: ret i1 [[R]]
;
%t = trunc i32 %x to i8
Expand All @@ -82,15 +82,17 @@ define i1 @ult_192(i32 %x) {

define <2 x i1> @ult_2044_splat(<2 x i16> %x) {
; CHECK-LABEL: @ult_2044_splat(
; CHECK-NEXT: [[T:%.*]] = trunc <2 x i16> [[X:%.*]] to <2 x i11>
; CHECK-NEXT: [[R:%.*]] = icmp ult <2 x i11> [[T]], <i11 -4, i11 -4>
; CHECK-NEXT: [[TMP1:%.*]] = and <2 x i16> [[X:%.*]], <i16 2044, i16 2044>
; CHECK-NEXT: [[R:%.*]] = icmp ne <2 x i16> [[TMP1]], <i16 2044, i16 2044>
; CHECK-NEXT: ret <2 x i1> [[R]]
;
%t = trunc <2 x i16> %x to <2 x i11>
%r = icmp ult <2 x i11> %t, <i11 2044, i11 2044> ; 0b111_1111_1100
ret <2 x i1> %r
}

; negative test - need high-bit-mask constant

define i1 @ult_96(i32 %x) {
; CHECK-LABEL: @ult_96(
; CHECK-NEXT: [[T:%.*]] = trunc i32 [[X:%.*]] to i8
Expand All @@ -102,6 +104,8 @@ define i1 @ult_96(i32 %x) {
ret i1 %r
}

; negative test - no extra use allowed

define i1 @ult_192_use(i32 %x) {
; CHECK-LABEL: @ult_192_use(
; CHECK-NEXT: [[T:%.*]] = trunc i32 [[X:%.*]] to i8
Expand All @@ -117,8 +121,8 @@ define i1 @ult_192_use(i32 %x) {

define i1 @ugt_3(i32 %x) {
; CHECK-LABEL: @ugt_3(
; CHECK-NEXT: [[T:%.*]] = trunc i32 [[X:%.*]] to i8
; CHECK-NEXT: [[R:%.*]] = icmp ugt i8 [[T]], 3
; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[X:%.*]], 252
; CHECK-NEXT: [[R:%.*]] = icmp ne i32 [[TMP1]], 0
; CHECK-NEXT: ret i1 [[R]]
;
%t = trunc i32 %x to i8
Expand All @@ -128,15 +132,17 @@ define i1 @ugt_3(i32 %x) {

define <2 x i1> @ugt_7_splat(<2 x i16> %x) {
; CHECK-LABEL: @ugt_7_splat(
; CHECK-NEXT: [[T:%.*]] = trunc <2 x i16> [[X:%.*]] to <2 x i11>
; CHECK-NEXT: [[R:%.*]] = icmp ugt <2 x i11> [[T]], <i11 7, i11 7>
; CHECK-NEXT: [[TMP1:%.*]] = and <2 x i16> [[X:%.*]], <i16 2040, i16 2040>
; CHECK-NEXT: [[R:%.*]] = icmp ne <2 x i16> [[TMP1]], zeroinitializer
; CHECK-NEXT: ret <2 x i1> [[R]]
;
%t = trunc <2 x i16> %x to <2 x i11>
%r = icmp ugt <2 x i11> %t, <i11 7, i11 7>
ret <2 x i1> %r
}

; negative test - need low-bit-mask constant

define i1 @ugt_4(i32 %x) {
; CHECK-LABEL: @ugt_4(
; CHECK-NEXT: [[T:%.*]] = trunc i32 [[X:%.*]] to i8
Expand All @@ -148,6 +154,8 @@ define i1 @ugt_4(i32 %x) {
ret i1 %r
}

; negative test - no extra use allowed

define i1 @ugt_3_use(i32 %x) {
; CHECK-LABEL: @ugt_3_use(
; CHECK-NEXT: [[T:%.*]] = trunc i32 [[X:%.*]] to i8
Expand All @@ -163,8 +171,8 @@ define i1 @ugt_3_use(i32 %x) {

define i1 @ugt_253(i32 %x) {
; CHECK-LABEL: @ugt_253(
; CHECK-NEXT: [[T:%.*]] = trunc i32 [[X:%.*]] to i8
; CHECK-NEXT: [[R:%.*]] = icmp ugt i8 [[T]], -3
; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[X:%.*]], 254
; CHECK-NEXT: [[R:%.*]] = icmp eq i32 [[TMP1]], 254
; CHECK-NEXT: ret i1 [[R]]
;
%t = trunc i32 %x to i8
Expand All @@ -174,15 +182,17 @@ define i1 @ugt_253(i32 %x) {

define <2 x i1> @ugt_2043_splat(<2 x i16> %x) {
; CHECK-LABEL: @ugt_2043_splat(
; CHECK-NEXT: [[T:%.*]] = trunc <2 x i16> [[X:%.*]] to <2 x i11>
; CHECK-NEXT: [[R:%.*]] = icmp ugt <2 x i11> [[T]], <i11 -5, i11 -5>
; CHECK-NEXT: [[TMP1:%.*]] = and <2 x i16> [[X:%.*]], <i16 2044, i16 2044>
; CHECK-NEXT: [[R:%.*]] = icmp eq <2 x i16> [[TMP1]], <i16 2044, i16 2044>
; CHECK-NEXT: ret <2 x i1> [[R]]
;
%t = trunc <2 x i16> %x to <2 x i11>
%r = icmp ugt <2 x i11> %t, <i11 2043, i11 2043> ; 0b111_1111_101
ret <2 x i1> %r
}

; negative test - need not-of-power-of-2 constant

define i1 @ugt_252(i32 %x) {
; CHECK-LABEL: @ugt_252(
; CHECK-NEXT: [[T:%.*]] = trunc i32 [[X:%.*]] to i8
Expand All @@ -194,6 +204,8 @@ define i1 @ugt_252(i32 %x) {
ret i1 %r
}

; negative test - no extra use allowed

define i1 @ugt_253_use(i32 %x) {
; CHECK-LABEL: @ugt_253_use(
; CHECK-NEXT: [[T:%.*]] = trunc i32 [[X:%.*]] to i8
Expand Down

0 comments on commit 8fce94f

Please sign in to comment.