[llvm] [InstCombine] Add combines for `(icmp eq/ne (and X, P2), (and X, -P2))` (PR #94867)
via llvm-commits
llvm-commits at lists.llvm.org
Sat Jun 8 13:54:41 PDT 2024
https://github.com/goldsteinn updated https://github.com/llvm/llvm-project/pull/94867
>From 8f1c940f245ce51b01f19359aea01968e647b486 Mon Sep 17 00:00:00 2001
From: Noah Goldstein <goldstein.w.n at gmail.com>
Date: Sat, 8 Jun 2024 14:22:35 -0500
Subject: [PATCH 1/3] [InstCombine] Add tests for combining `(icmp eq/ne (and
X, P2), (and X, -P2))`; NFC
---
.../Transforms/InstCombine/and-compare.ll | 99 +++++++++++++++++++
1 file changed, 99 insertions(+)
diff --git a/llvm/test/Transforms/InstCombine/and-compare.ll b/llvm/test/Transforms/InstCombine/and-compare.ll
index 14379ebf3a905..29a39429cc1d1 100644
--- a/llvm/test/Transforms/InstCombine/and-compare.ll
+++ b/llvm/test/Transforms/InstCombine/and-compare.ll
@@ -4,6 +4,8 @@
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
+declare void @use.i8(i8)
+
; Should be optimized to one and.
define i1 @test1(i32 %a, i32 %b) {
; CHECK-LABEL: @test1(
@@ -75,3 +77,100 @@ define <2 x i1> @test3vec(<2 x i64> %A) {
ret <2 x i1> %cmp
}
+define i1 @test_ne_cp2(i8 %x, i8 %yy) {
+; CHECK-LABEL: @test_ne_cp2(
+; CHECK-NEXT: [[AND_X_NEG_Y:%.*]] = and i8 [[X:%.*]], -16
+; CHECK-NEXT: [[AND_X_Y:%.*]] = and i8 [[X]], 16
+; CHECK-NEXT: call void @use.i8(i8 [[AND_X_NEG_Y]])
+; CHECK-NEXT: call void @use.i8(i8 [[AND_X_Y]])
+; CHECK-NEXT: [[R:%.*]] = icmp ne i8 [[AND_X_NEG_Y]], [[AND_X_Y]]
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %and_x_neg_y = and i8 %x, -16
+ %and_x_y = and i8 %x, 16
+ call void @use.i8(i8 %and_x_neg_y)
+ call void @use.i8(i8 %and_x_y)
+ %r = icmp ne i8 %and_x_neg_y, %and_x_y
+ ret i1 %r
+}
+
+define i1 @test_ne_cp2_2(i8 %x, i8 %yy) {
+; CHECK-LABEL: @test_ne_cp2_2(
+; CHECK-NEXT: [[AND_X_NEG_Y:%.*]] = and i8 [[X:%.*]], -4
+; CHECK-NEXT: [[AND_X_Y:%.*]] = and i8 [[X]], 4
+; CHECK-NEXT: call void @use.i8(i8 [[AND_X_NEG_Y]])
+; CHECK-NEXT: call void @use.i8(i8 [[AND_X_Y]])
+; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[AND_X_Y]], [[AND_X_NEG_Y]]
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %and_x_neg_y = and i8 %x, -4
+ %and_x_y = and i8 %x, 4
+ call void @use.i8(i8 %and_x_neg_y)
+ call void @use.i8(i8 %and_x_y)
+ %r = icmp eq i8 %and_x_y, %and_x_neg_y
+ ret i1 %r
+}
+
+define i1 @test_ne_cp2_other_fail(i8 %x, i8 %yy) {
+; CHECK-LABEL: @test_ne_cp2_other_fail(
+; CHECK-NEXT: [[AND_X_NEG_Y:%.*]] = and i8 [[X:%.*]], -17
+; CHECK-NEXT: [[AND_X_Y:%.*]] = and i8 [[X]], 16
+; CHECK-NEXT: call void @use.i8(i8 [[AND_X_NEG_Y]])
+; CHECK-NEXT: call void @use.i8(i8 [[AND_X_Y]])
+; CHECK-NEXT: [[R:%.*]] = icmp ne i8 [[AND_X_NEG_Y]], [[AND_X_Y]]
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %and_x_neg_y = and i8 %x, -17
+ %and_x_y = and i8 %x, 16
+ call void @use.i8(i8 %and_x_neg_y)
+ call void @use.i8(i8 %and_x_y)
+ %r = icmp ne i8 %and_x_neg_y, %and_x_y
+ ret i1 %r
+}
+
+define i1 @test_ne_cp2_other_fail2(i8 %x, i8 %yy) {
+; CHECK-LABEL: @test_ne_cp2_other_fail2(
+; CHECK-NEXT: [[AND_X_NEG_Y:%.*]] = and i8 [[X:%.*]], -16
+; CHECK-NEXT: [[AND_X_Y:%.*]] = and i8 [[X]], 17
+; CHECK-NEXT: call void @use.i8(i8 [[AND_X_NEG_Y]])
+; CHECK-NEXT: call void @use.i8(i8 [[AND_X_Y]])
+; CHECK-NEXT: [[R:%.*]] = icmp ne i8 [[AND_X_NEG_Y]], [[AND_X_Y]]
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %and_x_neg_y = and i8 %x, -16
+ %and_x_y = and i8 %x, 17
+ call void @use.i8(i8 %and_x_neg_y)
+ call void @use.i8(i8 %and_x_y)
+ %r = icmp ne i8 %and_x_neg_y, %and_x_y
+ ret i1 %r
+}
+
+define i1 @test_ne_cp2_other_okay(i8 %x, i8 %yy) {
+; CHECK-LABEL: @test_ne_cp2_other_okay(
+; CHECK-NEXT: [[AND_X_NEG_Y:%.*]] = and i8 [[X:%.*]], -17
+; CHECK-NEXT: [[AND_X_Y:%.*]] = and i8 [[X]], 16
+; CHECK-NEXT: call void @use.i8(i8 [[AND_X_Y]])
+; CHECK-NEXT: [[R:%.*]] = icmp ne i8 [[AND_X_NEG_Y]], [[AND_X_Y]]
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %and_x_neg_y = and i8 %x, -17
+ %and_x_y = and i8 %x, 16
+ call void @use.i8(i8 %and_x_y)
+ %r = icmp ne i8 %and_x_neg_y, %and_x_y
+ ret i1 %r
+}
+
+define i1 @test_ne_cp2_other_okay2(i8 %x, i8 %yy) {
+; CHECK-LABEL: @test_ne_cp2_other_okay2(
+; CHECK-NEXT: [[AND_X_NEG_Y:%.*]] = and i8 [[X:%.*]], -17
+; CHECK-NEXT: [[AND_X_Y:%.*]] = and i8 [[X]], 16
+; CHECK-NEXT: call void @use.i8(i8 [[AND_X_Y]])
+; CHECK-NEXT: [[R:%.*]] = icmp ne i8 [[AND_X_Y]], [[AND_X_NEG_Y]]
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %and_x_neg_y = and i8 %x, -17
+ %and_x_y = and i8 %x, 16
+ call void @use.i8(i8 %and_x_y)
+ %r = icmp ne i8 %and_x_y, %and_x_neg_y
+ ret i1 %r
+}
>From a5424eca17552840e759cacf0e3594db0307d5f9 Mon Sep 17 00:00:00 2001
From: Noah Goldstein <goldstein.w.n at gmail.com>
Date: Sat, 8 Jun 2024 14:22:37 -0500
Subject: [PATCH 2/3] [InstCombine] Add combines for `(icmp eq/ne (and X, P2),
(and X, -P2))`
`(icmp eq/ne (and X, P2), (and X, -P2))`
-> `(icmp ult/uge X, P2 * 2)`
If `P2` is constant, we can perform this fold profitably even if the
`and` ops are multi-use.
NB: This came up in some of the diffs resulting from #94648
Proofs: https://alive2.llvm.org/ce/z/mfd3G9
---
.../InstCombine/InstCombineCompares.cpp | 31 +++++++++++++++----
.../Transforms/InstCombine/and-compare.ll | 4 +--
2 files changed, 27 insertions(+), 8 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 4203147bc6a54..530c118743954 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -5548,8 +5548,8 @@ Instruction *InstCombinerImpl::foldICmpEquality(ICmpInst &I) {
}
// (X&Z) == (Y&Z) -> (X^Y) & Z == 0
- if (match(Op0, m_OneUse(m_And(m_Value(A), m_Value(B)))) &&
- match(Op1, m_OneUse(m_And(m_Value(C), m_Value(D))))) {
+ if (match(Op0, m_And(m_Value(A), m_Value(B))) &&
+ match(Op1, m_And(m_Value(C), m_Value(D)))) {
Value *X = nullptr, *Y = nullptr, *Z = nullptr;
if (A == C) {
@@ -5570,10 +5570,29 @@ Instruction *InstCombinerImpl::foldICmpEquality(ICmpInst &I) {
Z = B;
}
- if (X) { // Build (X^Y) & Z
- Op1 = Builder.CreateXor(X, Y);
- Op1 = Builder.CreateAnd(Op1, Z);
- return new ICmpInst(Pred, Op1, Constant::getNullValue(Op1->getType()));
+ if (X) {
+ // (X&P2) == (X&-P2)
+ // -> X u< P2*2
+ // (X&P2) != (X&-P2)
+ // -> X u>= P2*2
+ // iff P2 is not INT_MIN
+ const APInt *CP2;
+ ICmpInst::Predicate P2Pred =
+ Pred == ICmpInst::ICMP_EQ ? ICmpInst::ICMP_ULT : ICmpInst::ICMP_UGE;
+ if (match(X, m_APInt(CP2)) && match(Y, m_SpecificInt(-*CP2)) &&
+ (CP2->isPowerOf2() || CP2->isNegatedPowerOf2()) &&
+ !CP2->isMinSignedValue()) {
+ APInt CMask = CP2->isPowerOf2() ? *CP2 : -*CP2;
+ return new ICmpInst(P2Pred, Z,
+ ConstantInt::get(Z->getType(), CMask + CMask));
+ }
+
+ if (Op0->hasOneUse() && Op1->hasOneUse()) {
+ // Build (X^Y) & Z
+ Op1 = Builder.CreateXor(X, Y);
+ Op1 = Builder.CreateAnd(Op1, Z);
+ return new ICmpInst(Pred, Op1, Constant::getNullValue(Op1->getType()));
+ }
}
}
diff --git a/llvm/test/Transforms/InstCombine/and-compare.ll b/llvm/test/Transforms/InstCombine/and-compare.ll
index 29a39429cc1d1..526c93069614d 100644
--- a/llvm/test/Transforms/InstCombine/and-compare.ll
+++ b/llvm/test/Transforms/InstCombine/and-compare.ll
@@ -83,7 +83,7 @@ define i1 @test_ne_cp2(i8 %x, i8 %yy) {
; CHECK-NEXT: [[AND_X_Y:%.*]] = and i8 [[X]], 16
; CHECK-NEXT: call void @use.i8(i8 [[AND_X_NEG_Y]])
; CHECK-NEXT: call void @use.i8(i8 [[AND_X_Y]])
-; CHECK-NEXT: [[R:%.*]] = icmp ne i8 [[AND_X_NEG_Y]], [[AND_X_Y]]
+; CHECK-NEXT: [[R:%.*]] = icmp ugt i8 [[X]], 31
; CHECK-NEXT: ret i1 [[R]]
;
%and_x_neg_y = and i8 %x, -16
@@ -100,7 +100,7 @@ define i1 @test_ne_cp2_2(i8 %x, i8 %yy) {
; CHECK-NEXT: [[AND_X_Y:%.*]] = and i8 [[X]], 4
; CHECK-NEXT: call void @use.i8(i8 [[AND_X_NEG_Y]])
; CHECK-NEXT: call void @use.i8(i8 [[AND_X_Y]])
-; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[AND_X_Y]], [[AND_X_NEG_Y]]
+; CHECK-NEXT: [[R:%.*]] = icmp ult i8 [[X]], 8
; CHECK-NEXT: ret i1 [[R]]
;
%and_x_neg_y = and i8 %x, -4
>From a89b83da012f7cfdd2d92e10a947eacde37c907a Mon Sep 17 00:00:00 2001
From: Noah Goldstein <goldstein.w.n at gmail.com>
Date: Sat, 8 Jun 2024 15:36:30 -0500
Subject: [PATCH 3/3] [InstCombine] Extend `(icmp eq/ne (and Z, X), (and Z,
Y))`
Only require one of the `and` ops to be single-use if both `X` / `Y`
are constant.
---
.../lib/Transforms/InstCombine/InstCombineCompares.cpp | 8 +++++++-
llvm/test/Transforms/InstCombine/and-compare.ll | 10 ++++------
2 files changed, 11 insertions(+), 7 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 530c118743954..3271314f88fe4 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -5587,7 +5587,13 @@ Instruction *InstCombinerImpl::foldICmpEquality(ICmpInst &I) {
ConstantInt::get(Z->getType(), CMask + CMask));
}
- if (Op0->hasOneUse() && Op1->hasOneUse()) {
+ // If either Op0/Op1 are both one use or X^Y will constant fold and one of
+ // Op0/Op1 are one use proceeed. In those cases we are instruction neutral
+ // but icmp eq/ne A, 0 is easier to analyze than icmp eq/ne A, B
+ int UseCnt =
+ int(Op0->hasOneUse()) + int(Op1->hasOneUse()) +
+ (int(match(X, m_ImmConstant()) && match(Y, m_ImmConstant())));
+ if (UseCnt >= 2) {
// Build (X^Y) & Z
Op1 = Builder.CreateXor(X, Y);
Op1 = Builder.CreateAnd(Op1, Z);
diff --git a/llvm/test/Transforms/InstCombine/and-compare.ll b/llvm/test/Transforms/InstCombine/and-compare.ll
index 526c93069614d..c2d5d2185db7d 100644
--- a/llvm/test/Transforms/InstCombine/and-compare.ll
+++ b/llvm/test/Transforms/InstCombine/and-compare.ll
@@ -147,10 +147,9 @@ define i1 @test_ne_cp2_other_fail2(i8 %x, i8 %yy) {
define i1 @test_ne_cp2_other_okay(i8 %x, i8 %yy) {
; CHECK-LABEL: @test_ne_cp2_other_okay(
-; CHECK-NEXT: [[AND_X_NEG_Y:%.*]] = and i8 [[X:%.*]], -17
-; CHECK-NEXT: [[AND_X_Y:%.*]] = and i8 [[X]], 16
+; CHECK-NEXT: [[AND_X_Y:%.*]] = and i8 [[X:%.*]], 16
; CHECK-NEXT: call void @use.i8(i8 [[AND_X_Y]])
-; CHECK-NEXT: [[R:%.*]] = icmp ne i8 [[AND_X_NEG_Y]], [[AND_X_Y]]
+; CHECK-NEXT: [[R:%.*]] = icmp ne i8 [[X]], 0
; CHECK-NEXT: ret i1 [[R]]
;
%and_x_neg_y = and i8 %x, -17
@@ -162,10 +161,9 @@ define i1 @test_ne_cp2_other_okay(i8 %x, i8 %yy) {
define i1 @test_ne_cp2_other_okay2(i8 %x, i8 %yy) {
; CHECK-LABEL: @test_ne_cp2_other_okay2(
-; CHECK-NEXT: [[AND_X_NEG_Y:%.*]] = and i8 [[X:%.*]], -17
-; CHECK-NEXT: [[AND_X_Y:%.*]] = and i8 [[X]], 16
+; CHECK-NEXT: [[AND_X_Y:%.*]] = and i8 [[X:%.*]], 16
; CHECK-NEXT: call void @use.i8(i8 [[AND_X_Y]])
-; CHECK-NEXT: [[R:%.*]] = icmp ne i8 [[AND_X_Y]], [[AND_X_NEG_Y]]
+; CHECK-NEXT: [[R:%.*]] = icmp ne i8 [[X]], 0
; CHECK-NEXT: ret i1 [[R]]
;
%and_x_neg_y = and i8 %x, -17
More information about the llvm-commits
mailing list