-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[InstCombine] Fold (add X, (sext/zext (icmp eq X, C)))
#93840
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 Author: None (goldsteinn) Changes
Full diff: https://github.com/llvm/llvm-project/pull/93840.diff 3 Files Affected:
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index bff09f5676680..b861c8e9cc19f 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
@@ -1694,6 +1694,26 @@ Instruction *InstCombinerImpl::visitAdd(BinaryOperator &I) {
return BinaryOperator::CreateOr(LHS, Zext);
}
+ {
+ Value *Cond, *Ext;
+ Constant *C;
+ // (add X, (sext/zext (icmp eq X, C)))
+ // -> (select (icmp eq X, C), (add C, (sext/zext 1)), X)
+ auto CondMatcher = m_CombineAnd(
+ m_Value(Cond), m_ICmp(Pred, m_Deferred(A), m_ImmConstant(C)));
+
+ if (match(&I,
+ m_c_Add(m_Value(A),
+ m_CombineAnd(m_Value(Ext), m_ZExtOrSExt(CondMatcher)))) &&
+ Pred == ICmpInst::ICMP_EQ && Ext->hasOneUse()) {
+ Constant *ExtC = match(Ext, m_ZExt(m_Value()))
+ ? ConstantInt::get(C->getType(), 1)
+ : ConstantInt::getAllOnesValue(C->getType());
+ return replaceInstUsesWith(
+ I, Builder.CreateSelect(Cond, Builder.CreateAdd(C, ExtC), A));
+ }
+ }
+
if (Instruction *Ashr = foldAddToAshr(I))
return Ashr;
diff --git a/llvm/test/Transforms/InstCombine/apint-shift.ll b/llvm/test/Transforms/InstCombine/apint-shift.ll
index 05c3db70ce1ca..ecf9c4e9c4e69 100644
--- a/llvm/test/Transforms/InstCombine/apint-shift.ll
+++ b/llvm/test/Transforms/InstCombine/apint-shift.ll
@@ -240,8 +240,8 @@ define i23 @test11(i23 %x) {
define i47 @test12(i47 %X) {
; CHECK-LABEL: @test12(
-; CHECK-NEXT: [[SH2:%.*]] = and i47 [[X:%.*]], -256
-; CHECK-NEXT: ret i47 [[SH2]]
+; CHECK-NEXT: [[SH1:%.*]] = and i47 [[X:%.*]], -256
+; CHECK-NEXT: ret i47 [[SH1]]
;
%sh1 = ashr i47 %X, 8
%sh2 = shl i47 %sh1, 8
@@ -250,8 +250,8 @@ define i47 @test12(i47 %X) {
define <2 x i47> @test12_splat_vec(<2 x i47> %X) {
; CHECK-LABEL: @test12_splat_vec(
-; CHECK-NEXT: [[SH2:%.*]] = and <2 x i47> [[X:%.*]], <i47 -256, i47 -256>
-; CHECK-NEXT: ret <2 x i47> [[SH2]]
+; CHECK-NEXT: [[SH1:%.*]] = and <2 x i47> [[X:%.*]], <i47 -256, i47 -256>
+; CHECK-NEXT: ret <2 x i47> [[SH1]]
;
%sh1 = ashr <2 x i47> %X, <i47 8, i47 8>
%sh2 = shl <2 x i47> %sh1, <i47 8, i47 8>
@@ -564,14 +564,7 @@ define i40 @test26(i40 %A) {
; https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=9880
define i177 @ossfuzz_9880(i177 %X) {
; CHECK-LABEL: @ossfuzz_9880(
-; CHECK-NEXT: [[A:%.*]] = alloca i177, align 8
-; CHECK-NEXT: [[L1:%.*]] = load i177, ptr [[A]], align 4
-; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i177 [[L1]], -1
-; CHECK-NEXT: [[B5_NEG:%.*]] = sext i1 [[TMP1]] to i177
-; CHECK-NEXT: [[B14:%.*]] = add i177 [[L1]], [[B5_NEG]]
-; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i177 [[B14]], -1
-; CHECK-NEXT: [[B1:%.*]] = zext i1 [[TMP2]] to i177
-; CHECK-NEXT: ret i177 [[B1]]
+; CHECK-NEXT: ret i177 0
;
%A = alloca i177
%L1 = load i177, ptr %A
diff --git a/llvm/test/Transforms/InstCombine/fold-ext-eq-c-with-op.ll b/llvm/test/Transforms/InstCombine/fold-ext-eq-c-with-op.ll
new file mode 100644
index 0000000000000..960efc524910d
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/fold-ext-eq-c-with-op.ll
@@ -0,0 +1,56 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+declare void @use.i8(i8)
+define i8 @fold_add_zext_eq_0(i8 %x) {
+; CHECK-LABEL: @fold_add_zext_eq_0(
+; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.umax.i8(i8 [[X:%.*]], i8 1)
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %x_eq = icmp eq i8 %x, 0
+ %x_eq_ext = zext i1 %x_eq to i8
+ %r = add i8 %x, %x_eq_ext
+ ret i8 %r
+}
+
+define i8 @fold_add_sext_eq_4(i8 %x) {
+; CHECK-LABEL: @fold_add_sext_eq_4(
+; CHECK-NEXT: [[X_EQ:%.*]] = icmp eq i8 [[X:%.*]], 4
+; CHECK-NEXT: [[R:%.*]] = select i1 [[X_EQ]], i8 3, i8 [[X]]
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %x_eq = icmp eq i8 %x, 4
+ %x_eq_ext = sext i1 %x_eq to i8
+ %r = add i8 %x, %x_eq_ext
+ ret i8 %r
+}
+
+define i8 @fold_add_zext_eq_0_fail_multiuse_exp(i8 %x) {
+; CHECK-LABEL: @fold_add_zext_eq_0_fail_multiuse_exp(
+; CHECK-NEXT: [[X_EQ:%.*]] = icmp eq i8 [[X:%.*]], 0
+; CHECK-NEXT: [[X_EQ_EXT:%.*]] = zext i1 [[X_EQ]] to i8
+; CHECK-NEXT: [[R:%.*]] = add i8 [[X_EQ_EXT]], [[X]]
+; CHECK-NEXT: call void @use.i8(i8 [[X_EQ_EXT]])
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %x_eq = icmp eq i8 %x, 0
+ %x_eq_ext = zext i1 %x_eq to i8
+ %r = add i8 %x, %x_eq_ext
+ call void @use.i8(i8 %x_eq_ext)
+ ret i8 %r
+}
+
+define i8 @fold_add_sext_eq_4_fail_wrong_cond(i8 %x, i8 %y) {
+; CHECK-LABEL: @fold_add_sext_eq_4_fail_wrong_cond(
+; CHECK-NEXT: [[X_EQ:%.*]] = icmp eq i8 [[Y:%.*]], 4
+; CHECK-NEXT: [[X_EQ_EXT:%.*]] = sext i1 [[X_EQ]] to i8
+; CHECK-NEXT: [[R:%.*]] = add i8 [[X_EQ_EXT]], [[X:%.*]]
+; CHECK-NEXT: call void @use.i8(i8 [[X_EQ_EXT]])
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %x_eq = icmp eq i8 %y, 4
+ %x_eq_ext = sext i1 %x_eq to i8
+ %r = add i8 %x, %x_eq_ext
+ call void @use.i8(i8 %x_eq_ext)
+ ret i8 %r
+}
|
(add X, (sext/zext (icmp eq X, C)))
@dtcxzyw Can you run this? |
? ConstantInt::get(C->getType(), 1) | ||
: ConstantInt::getAllOnesValue(C->getType()); | ||
return replaceInstUsesWith( | ||
I, Builder.CreateSelect(Cond, Builder.CreateAdd(C, ExtC), A)); |
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.
Use InstCombiner::AddOne/SubOne
?
The IR diff LGTM. And there is no extra space for us to generalize this pattern (e.g., add->sub, eq -> ne) :) |
What do you mean there is no extra space for us the generalize? |
Both |
Yeah you guys where right, exact same diff. |
m_c_Add(m_Value(A), | ||
m_CombineAnd(m_Value(Ext), m_ZExtOrSExt(CondMatcher)))) && | ||
Pred == ICmpInst::ICMP_EQ && Ext->hasOneUse()) { | ||
Constant *ExtC = match(Ext, m_ZExt(m_Value())) |
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.
Constant *ExtC = match(Ext, m_ZExt(m_Value())) | |
Constant *ExtC = isa<ZExtInst>(Ext) |
%x_eq_ext = sext i1 %x_eq to i8 | ||
%r = add i8 %x, %x_eq_ext | ||
ret i8 %r | ||
} |
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.
Commuted tests?
// (add X, (sext/zext (icmp eq X, C))) | ||
// -> (select (icmp eq X, C), (add C, (sext/zext 1)), X) | ||
auto CondMatcher = m_CombineAnd( | ||
m_Value(Cond), m_ICmp(Pred, m_Deferred(A), m_ImmConstant(C))); |
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.
I think it might be a bit more elegant if you match m_Value(Cond) first and then do a second match call on Cond. Especially with this in a separate variable (so that m_Deferred occurs before the corresponding m_Value) I found this confusing.
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.
That will be flakey if X
itself is a sext
/zext
of another value though no?
We can convert this to a select based on the `(icmp eq X, C)`, then constant fold the addition the true arm begin `(add C, (sext/zext 1))` and the false arm being `(add X, 0)` e.g - `(select (icmp eq X, C), (add C, (sext/zext 1)), (add X, 0))`. This is essentially a specialization of the only case that sees to actually show up from llvm#89020
8da0428
to
bc2ae38
Compare
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
(add X, (sext/zext (icmp eq X, C)))
; NFC(add X, (sext/zext (icmp eq X, C)))
We can convert this to a select based on the
(icmp eq X, C)
, thenconstant fold the addition the true arm begin
(add C, (sext/zext 1))
and the false arm being
(add X, 0)
e.gThis is essentially a specialization of the only case that sees to
actually show up from #89020
Proofs: https://alive2.llvm.org/ce/z/dMbHbM