-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[InstCombine] Canonicalize and(zext(A), B)
into select A, B & 1, 0
#66740
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
Conversation
@llvm/pr-subscribers-llvm-transforms ChangesThis patch canonicalizes the pattern Full diff: https://github.com/llvm/llvm-project/pull/66740.diff 6 Files Affected:
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index ca7dfa82ab5a5d7..7fa432e1fdc1790 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -2654,6 +2654,12 @@ Instruction *InstCombinerImpl::visitAnd(BinaryOperator &I) {
A->getType()->isIntOrIntVectorTy(1))
return SelectInst::Create(A, Constant::getNullValue(Ty), B);
+ // and(zext(A), B) -> A ? (B & 1) : 0
+ if (match(&I, m_c_And(m_OneUse(m_ZExt(m_Value(A))), m_Value(B))) &&
+ A->getType()->isIntOrIntVectorTy(1))
+ return SelectInst::Create(A, Builder.CreateAnd(B, ConstantInt::get(Ty, 1)),
+ Constant::getNullValue(Ty));
+
// (iN X s>> (N-1)) & Y --> (X s< 0) ? Y : 0 -- with optional sext
if (match(&I, m_c_And(m_OneUse(m_SExtOrSelf(
m_AShr(m_Value(X), m_APIntAllowUndef(C)))),
diff --git a/llvm/test/Transforms/InstCombine/and-or-icmps.ll b/llvm/test/Transforms/InstCombine/and-or-icmps.ll
index 065dbf261e131bf..881a9b7ff129dbb 100644
--- a/llvm/test/Transforms/InstCombine/and-or-icmps.ll
+++ b/llvm/test/Transforms/InstCombine/and-or-icmps.ll
@@ -2769,9 +2769,9 @@ define i64 @icmp_slt_0_and_icmp_sgt_neg1_i64(i64 %x) {
define i64 @icmp_slt_0_and_icmp_sge_neg1_i64_fail(i64 %x) {
; CHECK-LABEL: @icmp_slt_0_and_icmp_sge_neg1_i64_fail(
; CHECK-NEXT: [[A:%.*]] = icmp sgt i64 [[X:%.*]], -2
-; CHECK-NEXT: [[B:%.*]] = zext i1 [[A]] to i64
; CHECK-NEXT: [[C:%.*]] = lshr i64 [[X]], 62
-; CHECK-NEXT: [[D:%.*]] = and i64 [[C]], [[B]]
+; CHECK-NEXT: [[TMP1:%.*]] = and i64 [[C]], 1
+; CHECK-NEXT: [[D:%.*]] = select i1 [[A]], i64 [[TMP1]], i64 0
; CHECK-NEXT: ret i64 [[D]]
;
%A = icmp sge i64 %x, -1
@@ -2871,9 +2871,8 @@ define i32 @icmp_slt_0_and_icmp_sge_neg2_i32_multiuse1(i32 %x) {
define i32 @icmp_slt_0_and_icmp_sge_neg2_i32_multiuse2(i32 %x) {
; CHECK-LABEL: @icmp_slt_0_and_icmp_sge_neg2_i32_multiuse2(
; CHECK-NEXT: [[A:%.*]] = icmp sgt i32 [[X:%.*]], -3
-; CHECK-NEXT: [[B:%.*]] = zext i1 [[A]] to i32
; CHECK-NEXT: [[C:%.*]] = lshr i32 [[X]], 31
-; CHECK-NEXT: [[D:%.*]] = and i32 [[C]], [[B]]
+; CHECK-NEXT: [[D:%.*]] = select i1 [[A]], i32 [[C]], i32 0
; CHECK-NEXT: call void @use32(i32 [[C]])
; CHECK-NEXT: ret i32 [[D]]
;
@@ -2923,10 +2922,9 @@ define i32 @icmp_slt_0_or_icmp_eq_100_i32_multiuse_fail1(i32 %x) {
define i32 @icmp_x_slt_0_and_icmp_y_ne_neg2_i32_multiuse_fail2(i32 %x, i32 %y) {
; CHECK-LABEL: @icmp_x_slt_0_and_icmp_y_ne_neg2_i32_multiuse_fail2(
-; CHECK-NEXT: [[A:%.*]] = icmp ne i32 [[X:%.*]], -2
-; CHECK-NEXT: [[B:%.*]] = zext i1 [[A]] to i32
+; CHECK-NEXT: [[A_NOT:%.*]] = icmp eq i32 [[X:%.*]], -2
; CHECK-NEXT: [[C:%.*]] = lshr i32 [[Y:%.*]], 31
-; CHECK-NEXT: [[D:%.*]] = and i32 [[C]], [[B]]
+; CHECK-NEXT: [[D:%.*]] = select i1 [[A_NOT]], i32 0, i32 [[C]]
; CHECK-NEXT: call void @use32(i32 [[C]])
; CHECK-NEXT: ret i32 [[D]]
;
diff --git a/llvm/test/Transforms/InstCombine/and.ll b/llvm/test/Transforms/InstCombine/and.ll
index 7ec4b0dbb0a12f0..9aa0df3062263cc 100644
--- a/llvm/test/Transforms/InstCombine/and.ll
+++ b/llvm/test/Transforms/InstCombine/and.ll
@@ -2443,8 +2443,8 @@ define i64 @test_and_or_constexpr_infloop() {
define i32 @and_zext(i32 %a, i1 %b) {
; CHECK-LABEL: @and_zext(
-; CHECK-NEXT: [[MASK:%.*]] = zext i1 [[B:%.*]] to i32
-; CHECK-NEXT: [[R:%.*]] = and i32 [[MASK]], [[A:%.*]]
+; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[A:%.*]], 1
+; CHECK-NEXT: [[R:%.*]] = select i1 [[B:%.*]], i32 [[TMP1]], i32 0
; CHECK-NEXT: ret i32 [[R]]
;
%mask = zext i1 %b to i32
@@ -2454,8 +2454,8 @@ define i32 @and_zext(i32 %a, i1 %b) {
define i32 @and_zext_commuted(i32 %a, i1 %b) {
; CHECK-LABEL: @and_zext_commuted(
-; CHECK-NEXT: [[MASK:%.*]] = zext i1 [[B:%.*]] to i32
-; CHECK-NEXT: [[R:%.*]] = and i32 [[MASK]], [[A:%.*]]
+; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[A:%.*]], 1
+; CHECK-NEXT: [[R:%.*]] = select i1 [[B:%.*]], i32 [[TMP1]], i32 0
; CHECK-NEXT: ret i32 [[R]]
;
%mask = zext i1 %b to i32
@@ -2478,8 +2478,8 @@ define i32 @and_zext_multiuse(i32 %a, i1 %b) {
define <2 x i32> @and_zext_vec(<2 x i32> %a, <2 x i1> %b) {
; CHECK-LABEL: @and_zext_vec(
-; CHECK-NEXT: [[MASK:%.*]] = zext <2 x i1> [[B:%.*]] to <2 x i32>
-; CHECK-NEXT: [[R:%.*]] = and <2 x i32> [[MASK]], [[A:%.*]]
+; CHECK-NEXT: [[TMP1:%.*]] = and <2 x i32> [[A:%.*]], <i32 1, i32 1>
+; CHECK-NEXT: [[R:%.*]] = select <2 x i1> [[B:%.*]], <2 x i32> [[TMP1]], <2 x i32> zeroinitializer
; CHECK-NEXT: ret <2 x i32> [[R]]
;
%mask = zext <2 x i1> %b to <2 x i32>
@@ -2490,10 +2490,7 @@ define <2 x i32> @and_zext_vec(<2 x i32> %a, <2 x i1> %b) {
; tests from PR66606
define i32 @and_zext_eq_even(i32 %a) {
; CHECK-LABEL: @and_zext_eq_even(
-; CHECK-NEXT: [[COND:%.*]] = icmp eq i32 [[A:%.*]], 2
-; CHECK-NEXT: [[NOT:%.*]] = zext i1 [[COND]] to i32
-; CHECK-NEXT: [[R:%.*]] = and i32 [[NOT]], [[A]]
-; CHECK-NEXT: ret i32 [[R]]
+; CHECK-NEXT: ret i32 0
;
%cond = icmp eq i32 %a, 2
%not = zext i1 %cond to i32
@@ -2503,10 +2500,7 @@ define i32 @and_zext_eq_even(i32 %a) {
define i32 @and_zext_eq_even_commuted(i32 %a) {
; CHECK-LABEL: @and_zext_eq_even_commuted(
-; CHECK-NEXT: [[COND:%.*]] = icmp eq i32 [[A:%.*]], 2
-; CHECK-NEXT: [[NOT:%.*]] = zext i1 [[COND]] to i32
-; CHECK-NEXT: [[R:%.*]] = and i32 [[NOT]], [[A]]
-; CHECK-NEXT: ret i32 [[R]]
+; CHECK-NEXT: ret i32 0
;
%cond = icmp eq i32 %a, 2
%not = zext i1 %cond to i32
@@ -2517,8 +2511,7 @@ define i32 @and_zext_eq_even_commuted(i32 %a) {
define i32 @and_zext_eq_odd(i32 %a) {
; CHECK-LABEL: @and_zext_eq_odd(
; CHECK-NEXT: [[COND:%.*]] = icmp eq i32 [[A:%.*]], 3
-; CHECK-NEXT: [[NOT:%.*]] = zext i1 [[COND]] to i32
-; CHECK-NEXT: [[R:%.*]] = and i32 [[NOT]], [[A]]
+; CHECK-NEXT: [[R:%.*]] = zext i1 [[COND]] to i32
; CHECK-NEXT: ret i32 [[R]]
;
%cond = icmp eq i32 %a, 3
@@ -2530,8 +2523,7 @@ define i32 @and_zext_eq_odd(i32 %a) {
define i32 @and_zext_eq_odd_commuted(i32 %a) {
; CHECK-LABEL: @and_zext_eq_odd_commuted(
; CHECK-NEXT: [[COND:%.*]] = icmp eq i32 [[A:%.*]], 3
-; CHECK-NEXT: [[NOT:%.*]] = zext i1 [[COND]] to i32
-; CHECK-NEXT: [[R:%.*]] = and i32 [[NOT]], [[A]]
+; CHECK-NEXT: [[R:%.*]] = zext i1 [[COND]] to i32
; CHECK-NEXT: ret i32 [[R]]
;
%cond = icmp eq i32 %a, 3
diff --git a/llvm/test/Transforms/InstCombine/icmp.ll b/llvm/test/Transforms/InstCombine/icmp.ll
index 666d85d6cc32103..a5c05278f4232e8 100644
--- a/llvm/test/Transforms/InstCombine/icmp.ll
+++ b/llvm/test/Transforms/InstCombine/icmp.ll
@@ -4441,9 +4441,9 @@ define i1 @redundant_sign_bit_count_ugt_31_30(i32 %x) {
define i1 @zext_bool_and_eq0(i1 %x, i8 %y) {
; CHECK-LABEL: @zext_bool_and_eq0(
; CHECK-NEXT: [[TMP1:%.*]] = and i8 [[Y:%.*]], 1
-; CHECK-NEXT: [[TMP2:%.*]] = icmp ne i8 [[TMP1]], 0
-; CHECK-NEXT: [[TMP3:%.*]] = and i1 [[TMP2]], [[X:%.*]]
-; CHECK-NEXT: [[R:%.*]] = xor i1 [[TMP3]], true
+; CHECK-NEXT: [[R1:%.*]] = icmp eq i8 [[TMP1]], 0
+; CHECK-NEXT: [[NOT_X:%.*]] = xor i1 [[X:%.*]], true
+; CHECK-NEXT: [[R:%.*]] = select i1 [[NOT_X]], i1 true, i1 [[R1]]
; CHECK-NEXT: ret i1 [[R]]
;
%zx = zext i1 %x to i8
@@ -4454,9 +4454,10 @@ define i1 @zext_bool_and_eq0(i1 %x, i8 %y) {
define <2 x i1> @zext_bool_and_eq0_commute(<2 x i1> %x, <2 x i8> %p) {
; CHECK-LABEL: @zext_bool_and_eq0_commute(
-; CHECK-NEXT: [[TMP1:%.*]] = trunc <2 x i8> [[P:%.*]] to <2 x i1>
-; CHECK-NEXT: [[TMP2:%.*]] = and <2 x i1> [[TMP1]], [[X:%.*]]
-; CHECK-NEXT: [[R:%.*]] = xor <2 x i1> [[TMP2]], <i1 true, i1 true>
+; CHECK-NEXT: [[TMP1:%.*]] = and <2 x i8> [[P:%.*]], <i8 1, i8 1>
+; CHECK-NEXT: [[R1:%.*]] = icmp eq <2 x i8> [[TMP1]], zeroinitializer
+; CHECK-NEXT: [[NOT_X:%.*]] = xor <2 x i1> [[X:%.*]], <i1 true, i1 true>
+; CHECK-NEXT: [[R:%.*]] = select <2 x i1> [[NOT_X]], <2 x i1> <i1 true, i1 true>, <2 x i1> [[R1]]
; CHECK-NEXT: ret <2 x i1> [[R]]
;
%y = mul <2 x i8> %p, %p ; thwart complexity-based canonicalization
@@ -4469,8 +4470,8 @@ define <2 x i1> @zext_bool_and_eq0_commute(<2 x i1> %x, <2 x i8> %p) {
define i1 @zext_bool_and_ne0(i1 %x, i8 %y) {
; CHECK-LABEL: @zext_bool_and_ne0(
; CHECK-NEXT: [[TMP1:%.*]] = and i8 [[Y:%.*]], 1
-; CHECK-NEXT: [[TMP2:%.*]] = icmp ne i8 [[TMP1]], 0
-; CHECK-NEXT: [[R:%.*]] = and i1 [[TMP2]], [[X:%.*]]
+; CHECK-NEXT: [[R1:%.*]] = icmp ne i8 [[TMP1]], 0
+; CHECK-NEXT: [[R:%.*]] = select i1 [[X:%.*]], i1 [[R1]], i1 false
; CHECK-NEXT: ret i1 [[R]]
;
%zx = zext i1 %x to i8
@@ -4482,9 +4483,9 @@ define i1 @zext_bool_and_ne0(i1 %x, i8 %y) {
define i1 @zext_bool_and_ne1(i1 %x, i8 %y) {
; CHECK-LABEL: @zext_bool_and_ne1(
; CHECK-NEXT: [[TMP1:%.*]] = and i8 [[Y:%.*]], 1
-; CHECK-NEXT: [[TMP2:%.*]] = icmp ne i8 [[TMP1]], 0
-; CHECK-NEXT: [[TMP3:%.*]] = and i1 [[TMP2]], [[X:%.*]]
-; CHECK-NEXT: [[R:%.*]] = xor i1 [[TMP3]], true
+; CHECK-NEXT: [[R1:%.*]] = icmp eq i8 [[TMP1]], 0
+; CHECK-NEXT: [[NOT_X:%.*]] = xor i1 [[X:%.*]], true
+; CHECK-NEXT: [[R:%.*]] = select i1 [[NOT_X]], i1 true, i1 [[R1]]
; CHECK-NEXT: ret i1 [[R]]
;
%zx = zext i1 %x to i8
@@ -4495,8 +4496,8 @@ define i1 @zext_bool_and_ne1(i1 %x, i8 %y) {
define <2 x i1> @zext_bool_and_eq1(<2 x i1> %x, <2 x i8> %y) {
; CHECK-LABEL: @zext_bool_and_eq1(
-; CHECK-NEXT: [[TMP1:%.*]] = trunc <2 x i8> [[Y:%.*]] to <2 x i1>
-; CHECK-NEXT: [[R:%.*]] = and <2 x i1> [[TMP1]], [[X:%.*]]
+; CHECK-NEXT: [[R1:%.*]] = trunc <2 x i8> [[Y:%.*]] to <2 x i1>
+; CHECK-NEXT: [[R:%.*]] = select <2 x i1> [[X:%.*]], <2 x i1> [[R1]], <2 x i1> zeroinitializer
; CHECK-NEXT: ret <2 x i1> [[R]]
;
%zx = zext <2 x i1> %x to <2 x i8>
@@ -4541,8 +4542,8 @@ define i1 @zext_bool_and_eq0_use(i1 %x, i64 %y) {
define i1 @zext_bool_and_ne0_use(i1 %x, i64 %y) {
; CHECK-LABEL: @zext_bool_and_ne0_use(
-; CHECK-NEXT: [[ZX:%.*]] = zext i1 [[X:%.*]] to i64
-; CHECK-NEXT: [[A:%.*]] = and i64 [[ZX]], [[Y:%.*]]
+; CHECK-NEXT: [[TMP1:%.*]] = and i64 [[Y:%.*]], 1
+; CHECK-NEXT: [[A:%.*]] = select i1 [[X:%.*]], i64 [[TMP1]], i64 0
; CHECK-NEXT: call void @use_i64(i64 [[A]])
; CHECK-NEXT: [[R:%.*]] = icmp ne i64 [[A]], 0
; CHECK-NEXT: ret i1 [[R]]
diff --git a/llvm/test/Transforms/InstCombine/narrow.ll b/llvm/test/Transforms/InstCombine/narrow.ll
index 9e4670b51ad139a..781974d33bf1157 100644
--- a/llvm/test/Transforms/InstCombine/narrow.ll
+++ b/llvm/test/Transforms/InstCombine/narrow.ll
@@ -155,8 +155,7 @@ define i1 @searchArray2(i32 %hay, ptr %haystack) {
; CHECK-NEXT: [[IDX:%.*]] = getelementptr i32, ptr [[HAYSTACK:%.*]], i64 [[INDVAR]]
; CHECK-NEXT: [[LD:%.*]] = load i32, ptr [[IDX]], align 4
; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i32 [[LD]], [[HAY:%.*]]
-; CHECK-NEXT: [[ZEXT:%.*]] = zext i1 [[CMP1]] to i8
-; CHECK-NEXT: [[AND]] = and i8 [[FOUND]], [[ZEXT]]
+; CHECK-NEXT: [[AND]] = select i1 [[CMP1]], i8 [[FOUND]], i8 0
; CHECK-NEXT: [[INDVAR_NEXT]] = add i64 [[INDVAR]], 1
; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i64 [[INDVAR_NEXT]], 1000
; CHECK-NEXT: br i1 [[EXITCOND]], label [[EXIT:%.*]], label [[LOOP]]
diff --git a/llvm/test/Transforms/InstCombine/zext-or-icmp.ll b/llvm/test/Transforms/InstCombine/zext-or-icmp.ll
index 74d0f2f21aec8e0..dcb8c081f154aa0 100644
--- a/llvm/test/Transforms/InstCombine/zext-or-icmp.ll
+++ b/llvm/test/Transforms/InstCombine/zext-or-icmp.ll
@@ -173,15 +173,15 @@ define i8 @PR49475_infloop(i32 %t0, i16 %insert, i64 %e, i8 %i162) {
; CHECK-NEXT: [[B:%.*]] = icmp eq i32 [[T0:%.*]], 0
; CHECK-NEXT: [[B2:%.*]] = icmp eq i16 [[INSERT:%.*]], 0
; CHECK-NEXT: [[T1:%.*]] = or i1 [[B]], [[B2]]
-; CHECK-NEXT: [[EXT:%.*]] = zext i1 [[T1]] to i32
-; CHECK-NEXT: [[AND:%.*]] = and i32 [[EXT]], [[T0]]
-; CHECK-NEXT: [[TMP1:%.*]] = or i32 [[AND]], 140
-; CHECK-NEXT: [[XOR1:%.*]] = zext i32 [[TMP1]] to i64
+; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[T0]], 1
+; CHECK-NEXT: [[TMP2:%.*]] = or i32 [[TMP1]], 140
+; CHECK-NEXT: [[TMP3:%.*]] = zext i32 [[TMP2]] to i64
+; CHECK-NEXT: [[XOR1:%.*]] = select i1 [[T1]], i64 [[TMP3]], i64 140
; CHECK-NEXT: [[CONV16:%.*]] = sext i8 [[I162:%.*]] to i64
; CHECK-NEXT: [[SUB17:%.*]] = sub i64 [[CONV16]], [[E:%.*]]
; CHECK-NEXT: [[SEXT:%.*]] = shl i64 [[SUB17]], 32
; CHECK-NEXT: [[CONV18:%.*]] = ashr exact i64 [[SEXT]], 32
-; CHECK-NEXT: [[CMP:%.*]] = icmp sle i64 [[CONV18]], [[XOR1]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp sge i64 [[XOR1]], [[CONV18]]
; CHECK-NEXT: [[CONV19:%.*]] = zext i1 [[CMP]] to i16
; CHECK-NEXT: [[OR21:%.*]] = or i16 [[CONV19]], [[INSERT]]
; CHECK-NEXT: [[TOBOOL23_NOT:%.*]] = icmp eq i16 [[OR21]], 0
|
We should use rebase instead of merge. |
I think this is the right canonicalization at the IR level, as select allows better analysis than zext(icmp). However, this seems to be universally worse for the backend: https://llvm.godbolt.org/z/Yh7brfc8b So I think we would need an SDAG undo transform. Interestingly, this already happens for riscv64, so maybe the undo transform already exists but is not enabled for all target. @goldsteinn What do you think? |
this also fixes #28612 |
Yeah, I think I agree, I looked a bit around DAGCombiner and can't find where I'd say since we also do:
to
This makes sense in that context. I'll throw up a patch shortly to cover this case in the backend. Edit: its a bit more ambiguous if the |
Created: #66793 which fixes the backend regression this can cause. |
You might want to check whether this also fixes #66740 and if so add a test. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
… & 1, 0` Fix tests for PR66733.
To avoid backend regressions, I will merge this PR after #66793. |
llvm#66740) This patch canonicalizes the pattern `and(zext(A), B)` into `select A, B & 1, 0`. Thus, we can reuse transforms `select B == even, B & 1, 0 -> 0` and `select B == odd, B & 1, 0 -> zext(B == odd)` in `InstCombine`. It is an alternative to llvm#66676. Alive2: https://alive2.llvm.org/ce/z/598phE Fixes llvm#66733. Fixes llvm#66606. Fixes llvm#28612.
…B & 1, 0` (llvm#66740)" This reverts commit a7f962c.
…B & 1, 0` (llvm#66740)" This reverts commit a7f962c.
…B & 1, 0` (llvm#66740)" This reverts commit a7f962c.
…B & 1, 0` (llvm#66740)" This reverts commit a7f962c.
…B & 1, 0` (llvm#66740)" This reverts commit a7f962c.
This patch canonicalizes the pattern
and(zext(A), B)
intoselect A, B & 1, 0
. Thus, we can reuse transformsselect B == even, B & 1, 0 -> 0
andselect B == odd, B & 1, 0 -> zext(B == odd)
inInstCombine
.It is an alternative to #66676.
Alive2: https://alive2.llvm.org/ce/z/598phE
Fixes #66733.
Fixes #66606.
Fixes #28612.