[llvm] 8fce94f - [InstCombine] canonicalize icmp with trunc op into mask and cmp, part 2
Sanjay Patel via llvm-commits
llvm-commits at lists.llvm.org
Tue Nov 16 06:29:00 PST 2021
Author: Sanjay Patel
Date: 2021-11-16T09:27:30-05:00
New Revision: 8fce94f91610bc4614b1fe0bff8f143ea2b54742
URL: https://github.com/llvm/llvm-project/commit/8fce94f91610bc4614b1fe0bff8f143ea2b54742
DIFF: https://github.com/llvm/llvm-project/commit/8fce94f91610bc4614b1fe0bff8f143ea2b54742.diff
LOG: [InstCombine] canonicalize icmp with trunc op into mask and cmp, part 2
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:
acabad9ff6bf (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
Added:
Modified:
llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
llvm/test/Transforms/InstCombine/icmp-trunc.ll
Removed:
################################################################################
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index b697ddd37aa0..7a9e177f19da 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -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;
}
diff --git a/llvm/test/Transforms/InstCombine/icmp-trunc.ll b/llvm/test/Transforms/InstCombine/icmp-trunc.ll
index 02e30c620461..54e4a9d0e5dc 100644
--- a/llvm/test/Transforms/InstCombine/icmp-trunc.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-trunc.ll
@@ -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
@@ -82,8 +82,8 @@ 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>
@@ -91,6 +91,8 @@ define <2 x i1> @ult_2044_splat(<2 x i16> %x) {
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
@@ -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
@@ -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
@@ -128,8 +132,8 @@ 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>
@@ -137,6 +141,8 @@ define <2 x i1> @ugt_7_splat(<2 x i16> %x) {
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
@@ -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
@@ -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
@@ -174,8 +182,8 @@ 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>
@@ -183,6 +191,8 @@ define <2 x i1> @ugt_2043_splat(<2 x i16> %x) {
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
@@ -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
More information about the llvm-commits
mailing list