[llvm] [InstCombine] Simplify and/or by replacing operands with constants (PR #77231)
Yingwei Zheng via llvm-commits
llvm-commits at lists.llvm.org
Tue Jan 30 09:06:17 PST 2024
https://github.com/dtcxzyw updated https://github.com/llvm/llvm-project/pull/77231
>From c37fae17d4e4c90e66a79641cef48886576b4815 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Mon, 22 Jan 2024 10:58:27 +0800
Subject: [PATCH 1/3] [InstCombine] Add pre-commit tests for PR76554. NFC.
---
.../test/Transforms/InstCombine/and-xor-or.ll | 30 +++++
llvm/test/Transforms/InstCombine/or.ll | 117 ++++++++++++++++++
2 files changed, 147 insertions(+)
diff --git a/llvm/test/Transforms/InstCombine/and-xor-or.ll b/llvm/test/Transforms/InstCombine/and-xor-or.ll
index 69a7890bee22f..ca0ba9206ffde 100644
--- a/llvm/test/Transforms/InstCombine/and-xor-or.ll
+++ b/llvm/test/Transforms/InstCombine/and-xor-or.ll
@@ -3,6 +3,7 @@
declare void @use(i32)
declare void @use_i8(i8)
+declare void @use_i1(i1)
; a & (a ^ b) --> a & ~b
@@ -4780,3 +4781,32 @@ define i32 @canonicalize_logic_first_constexpr_nuw(i32 %x) {
%r = and i32 %a, -10
ret i32 %r
}
+
+define i1 @test_and_xor_freely_invertable(i32 %x, i32 %y, i1 %z) {
+; CHECK-LABEL: define {{[^@]+}}@test_and_xor_freely_invertable
+; CHECK-SAME: (i32 [[X:%.*]], i32 [[Y:%.*]], i1 [[Z:%.*]]) {
+; CHECK-NEXT: [[CMP:%.*]] = icmp sle i32 [[X]], [[Y]]
+; CHECK-NEXT: [[AND:%.*]] = and i1 [[CMP]], [[Z]]
+; CHECK-NEXT: ret i1 [[AND]]
+;
+ %cmp = icmp sgt i32 %x, %y
+ %xor = xor i1 %cmp, %z
+ %and = and i1 %xor, %z
+ ret i1 %and
+}
+
+define i1 @test_and_xor_freely_invertable_multiuse(i32 %x, i32 %y, i1 %z) {
+; CHECK-LABEL: define {{[^@]+}}@test_and_xor_freely_invertable_multiuse
+; CHECK-SAME: (i32 [[X:%.*]], i32 [[Y:%.*]], i1 [[Z:%.*]]) {
+; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[X]], [[Y]]
+; CHECK-NEXT: call void @use_i1(i1 [[CMP]])
+; CHECK-NEXT: [[TMP1:%.*]] = xor i1 [[CMP]], true
+; CHECK-NEXT: [[AND:%.*]] = and i1 [[TMP1]], [[Z]]
+; CHECK-NEXT: ret i1 [[AND]]
+;
+ %cmp = icmp sgt i32 %x, %y
+ call void @use_i1(i1 %cmp)
+ %xor = xor i1 %cmp, %z
+ %and = and i1 %xor, %z
+ ret i1 %and
+}
diff --git a/llvm/test/Transforms/InstCombine/or.ll b/llvm/test/Transforms/InstCombine/or.ll
index 573a11599141a..b710f3afaa42c 100644
--- a/llvm/test/Transforms/InstCombine/or.ll
+++ b/llvm/test/Transforms/InstCombine/or.ll
@@ -1777,3 +1777,120 @@ if.then:
if.else:
ret i32 0
}
+
+; Tests from PR76554
+define i32 @test_or_and_xor_constant(i32 %x, i32 %y) {
+; CHECK-LABEL: @test_or_and_xor_constant(
+; CHECK-NEXT: [[A:%.*]] = and i32 [[X:%.*]], -2147483648
+; CHECK-NEXT: [[B:%.*]] = xor i32 [[A]], -2147483648
+; CHECK-NEXT: [[C:%.*]] = and i32 [[B]], [[Y:%.*]]
+; CHECK-NEXT: [[D:%.*]] = or i32 [[C]], [[A]]
+; CHECK-NEXT: ret i32 [[D]]
+;
+ %a = and i32 %x, -2147483648
+ %b = xor i32 %a, -2147483648
+ %c = and i32 %b, %y
+ %d = or i32 %c, %a
+ ret i32 %d
+}
+
+define i32 @test_or_and_xor(i32 %a, i32 %b, i32 %c) {
+; CHECK-LABEL: @test_or_and_xor(
+; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT: [[AND:%.*]] = and i32 [[XOR]], [[C:%.*]]
+; CHECK-NEXT: [[OR:%.*]] = or i32 [[AND]], [[A]]
+; CHECK-NEXT: ret i32 [[OR]]
+;
+ %xor = xor i32 %a, %b
+ %and = and i32 %xor, %c
+ %or = or i32 %and, %a
+ ret i32 %or
+}
+
+define i32 @test_or_and_xor_commuted1(i32 %a, i32 %b, i32 %c) {
+; CHECK-LABEL: @test_or_and_xor_commuted1(
+; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[B:%.*]], [[A:%.*]]
+; CHECK-NEXT: [[AND:%.*]] = and i32 [[XOR]], [[C:%.*]]
+; CHECK-NEXT: [[OR:%.*]] = or i32 [[AND]], [[A]]
+; CHECK-NEXT: ret i32 [[OR]]
+;
+ %xor = xor i32 %b, %a
+ %and = and i32 %xor, %c
+ %or = or i32 %and, %a
+ ret i32 %or
+}
+
+define i32 @test_or_and_xor_commuted2(i32 %a, i32 %b, i32 %c) {
+; CHECK-LABEL: @test_or_and_xor_commuted2(
+; CHECK-NEXT: [[CC:%.*]] = mul i32 [[C:%.*]], [[C]]
+; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT: [[AND:%.*]] = and i32 [[CC]], [[XOR]]
+; CHECK-NEXT: [[OR:%.*]] = or i32 [[AND]], [[A]]
+; CHECK-NEXT: ret i32 [[OR]]
+;
+ %cc = mul i32 %c, %c
+ %xor = xor i32 %a, %b
+ %and = and i32 %cc, %xor
+ %or = or i32 %and, %a
+ ret i32 %or
+}
+
+define i32 @test_or_and_xor_commuted3(i32 %a, i32 %b, i32 %c) {
+; CHECK-LABEL: @test_or_and_xor_commuted3(
+; CHECK-NEXT: [[AA:%.*]] = mul i32 [[A:%.*]], [[A]]
+; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[AA]], [[B:%.*]]
+; CHECK-NEXT: [[AND:%.*]] = and i32 [[XOR]], [[C:%.*]]
+; CHECK-NEXT: [[OR:%.*]] = or i32 [[AA]], [[AND]]
+; CHECK-NEXT: ret i32 [[OR]]
+;
+ %aa = mul i32 %a, %a
+ %xor = xor i32 %aa, %b
+ %and = and i32 %xor, %c
+ %or = or i32 %aa, %and
+ ret i32 %or
+}
+
+define i32 @test_or_and_xor_multiuse1(i32 %a, i32 %b, i32 %c) {
+; CHECK-LABEL: @test_or_and_xor_multiuse1(
+; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT: call void @use(i32 [[XOR]])
+; CHECK-NEXT: [[AND:%.*]] = and i32 [[XOR]], [[C:%.*]]
+; CHECK-NEXT: [[OR:%.*]] = or i32 [[AND]], [[A]]
+; CHECK-NEXT: ret i32 [[OR]]
+;
+ %xor = xor i32 %a, %b
+ call void @use(i32 %xor)
+ %and = and i32 %xor, %c
+ %or = or i32 %and, %a
+ ret i32 %or
+}
+
+; Negative tests
+
+define i32 @test_or_and_xor_mismatched_op(i32 %a, i32 %b, i32 %c, i32 %d) {
+; CHECK-LABEL: @test_or_and_xor_mismatched_op(
+; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT: [[AND:%.*]] = and i32 [[XOR]], [[C:%.*]]
+; CHECK-NEXT: [[OR:%.*]] = or i32 [[AND]], [[D:%.*]]
+; CHECK-NEXT: ret i32 [[OR]]
+;
+ %xor = xor i32 %a, %b
+ %and = and i32 %xor, %c
+ %or = or i32 %and, %d
+ ret i32 %or
+}
+
+define i32 @test_or_and_xor_multiuse2(i32 %a, i32 %b, i32 %c) {
+; CHECK-LABEL: @test_or_and_xor_multiuse2(
+; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT: [[AND:%.*]] = and i32 [[XOR]], [[C:%.*]]
+; CHECK-NEXT: call void @use(i32 [[AND]])
+; CHECK-NEXT: [[OR:%.*]] = or i32 [[AND]], [[A]]
+; CHECK-NEXT: ret i32 [[OR]]
+;
+ %xor = xor i32 %a, %b
+ %and = and i32 %xor, %c
+ call void @use(i32 %and)
+ %or = or i32 %and, %a
+ ret i32 %or
+}
>From c8b3af5f0ed80caa9e471f110d2408f9e5b3a85f Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Mon, 22 Jan 2024 11:01:06 +0800
Subject: [PATCH 2/3] [InstCombine] Simplify and/or by replacing operands with
constants
---
.../InstCombine/InstCombineAndOrXor.cpp | 72 ++++++++++++++++---
.../Transforms/InstCombine/and-or-icmps.ll | 14 ++--
.../test/Transforms/InstCombine/and-or-not.ll | 14 +---
llvm/test/Transforms/InstCombine/or.ll | 30 ++++----
4 files changed, 81 insertions(+), 49 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index 5fd944a859ef0..f9571819bfc02 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -2179,6 +2179,57 @@ foldBitwiseLogicWithIntrinsics(BinaryOperator &I,
}
}
+// Try to simplify X | Y by replacing occurrences of Y in X with 0.
+// Similarly, simplify X & Y by replacing occurrences of Y in X with -1.
+// Return the simplified result of X if successful, and nullptr otherwise.
+static Value *simplifyAndOrWithOpReplaced(Value *X, Value *Y, bool IsAnd,
+ InstCombinerImpl &IC,
+ unsigned Depth = 0) {
+ if (isa<Constant>(X) || X == Y)
+ return nullptr;
+
+ auto RecursivelyReplaceUses = [&](Instruction::BinaryOps Opcode, Value *Op0,
+ Value *Op1) -> Value * {
+ if (Depth == 2)
+ return nullptr;
+
+ // TODO: Relax the one-use constraint to clean up existing hard-coded
+ // simplifications.
+ if (!X->hasOneUse())
+ return nullptr;
+ Value *NewOp0 = simplifyAndOrWithOpReplaced(Op0, Y, IsAnd, IC, Depth + 1);
+ Value *NewOp1 = simplifyAndOrWithOpReplaced(Op1, Y, IsAnd, IC, Depth + 1);
+ if (!NewOp0 && !NewOp1)
+ return nullptr;
+ return IC.Builder.CreateBinOp(Opcode, NewOp0 ? NewOp0 : Op0,
+ NewOp1 ? NewOp1 : Op1);
+ };
+
+ Value *RHS;
+ if (match(X, m_c_And(m_Specific(Y), m_Value(RHS)))) {
+ return IsAnd ? RHS : Constant::getNullValue(X->getType());
+ } else if (match(X, m_c_Or(m_Specific(Y), m_Value(RHS)))) {
+ return IsAnd ? Constant::getAllOnesValue(X->getType()) : RHS;
+ } else if (match(X, m_c_Xor(m_Specific(Y), m_Value(RHS)))) {
+ if (IsAnd) {
+ if (X->hasOneUse())
+ return IC.Builder.CreateNot(RHS);
+
+ if (Value *NotRHS =
+ IC.getFreelyInverted(RHS, RHS->hasOneUse(), &IC.Builder))
+ return NotRHS;
+ } else
+ return RHS;
+ }
+
+ // FIXME: Is it correct to decompose xor if Y may be undef?
+ Value *Op0, *Op1;
+ if (match(X, m_BitwiseLogic(m_Value(Op0), m_Value(Op1))))
+ return RecursivelyReplaceUses(cast<BinaryOperator>(X)->getOpcode(), Op0,
+ Op1);
+ return nullptr;
+}
+
// FIXME: We use commutative matchers (m_c_*) for some, but not all, matches
// here. We should standardize that construct where it is needed or choose some
// other way to ensure that commutated variants of patterns are not missed.
@@ -2505,13 +2556,6 @@ Instruction *InstCombinerImpl::visitAnd(BinaryOperator &I) {
{
Value *A, *B, *C;
- // A & (A ^ B) --> A & ~B
- if (match(Op1, m_OneUse(m_c_Xor(m_Specific(Op0), m_Value(B)))))
- return BinaryOperator::CreateAnd(Op0, Builder.CreateNot(B));
- // (A ^ B) & A --> A & ~B
- if (match(Op0, m_OneUse(m_c_Xor(m_Specific(Op1), m_Value(B)))))
- return BinaryOperator::CreateAnd(Op1, Builder.CreateNot(B));
-
// A & ~(A ^ B) --> A & B
if (match(Op1, m_Not(m_c_Xor(m_Specific(Op0), m_Value(B)))))
return BinaryOperator::CreateAnd(Op0, B);
@@ -2708,6 +2752,11 @@ Instruction *InstCombinerImpl::visitAnd(BinaryOperator &I) {
if (Instruction *Res = foldBitwiseLogicWithIntrinsics(I, Builder))
return Res;
+ if (Value *V = simplifyAndOrWithOpReplaced(Op0, Op1, /*IsAnd*/ true, *this))
+ return BinaryOperator::CreateAnd(Op1, V);
+ if (Value *V = simplifyAndOrWithOpReplaced(Op1, Op0, /*IsAnd*/ true, *this))
+ return BinaryOperator::CreateAnd(Op0, V);
+
return nullptr;
}
@@ -3423,10 +3472,6 @@ Instruction *InstCombinerImpl::visitOr(BinaryOperator &I) {
return BinaryOperator::CreateMul(X, IncrementY);
}
- // X | (X ^ Y) --> X | Y (4 commuted patterns)
- if (match(&I, m_c_Or(m_Value(X), m_c_Xor(m_Deferred(X), m_Value(Y)))))
- return BinaryOperator::CreateOr(X, Y);
-
// (A & C) | (B & D)
Value *A, *B, *C, *D;
if (match(Op0, m_And(m_Value(A), m_Value(C))) &&
@@ -3911,6 +3956,11 @@ Instruction *InstCombinerImpl::visitOr(BinaryOperator &I) {
if (Instruction *Res = foldBitwiseLogicWithIntrinsics(I, Builder))
return Res;
+ if (Value *V = simplifyAndOrWithOpReplaced(Op0, Op1, /*IsAnd*/ false, *this))
+ return BinaryOperator::CreateOr(Op1, V);
+ if (Value *V = simplifyAndOrWithOpReplaced(Op1, Op0, /*IsAnd*/ false, *this))
+ return BinaryOperator::CreateOr(Op0, V);
+
return nullptr;
}
diff --git a/llvm/test/Transforms/InstCombine/and-or-icmps.ll b/llvm/test/Transforms/InstCombine/and-or-icmps.ll
index 91ecf24760259..b43a65d5b8e3b 100644
--- a/llvm/test/Transforms/InstCombine/and-or-icmps.ll
+++ b/llvm/test/Transforms/InstCombine/and-or-icmps.ll
@@ -369,16 +369,12 @@ define void @simplify_before_foldAndOfICmps(ptr %p) {
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i16 [[L7]], -1
; CHECK-NEXT: [[B11:%.*]] = zext i1 [[TMP1]] to i16
; CHECK-NEXT: [[C10:%.*]] = icmp ugt i16 [[L7]], [[B11]]
-; CHECK-NEXT: [[C5:%.*]] = icmp slt i16 [[L7]], 1
; CHECK-NEXT: [[C7:%.*]] = icmp slt i16 [[L7]], 0
-; CHECK-NEXT: [[B15:%.*]] = xor i1 [[C7]], [[C10]]
-; CHECK-NEXT: [[C6:%.*]] = xor i1 [[B15]], true
-; CHECK-NEXT: [[TMP2:%.*]] = and i1 [[C5]], [[C6]]
-; CHECK-NEXT: [[C3:%.*]] = and i1 [[TMP2]], [[C10]]
-; CHECK-NEXT: [[TMP3:%.*]] = xor i1 [[C10]], true
-; CHECK-NEXT: [[C18:%.*]] = or i1 [[C7]], [[TMP3]]
-; CHECK-NEXT: [[TMP4:%.*]] = sext i1 [[C3]] to i64
-; CHECK-NEXT: [[G26:%.*]] = getelementptr i1, ptr null, i64 [[TMP4]]
+; CHECK-NEXT: [[C3:%.*]] = and i1 [[C10]], [[C7]]
+; CHECK-NEXT: [[TMP2:%.*]] = xor i1 [[C10]], true
+; CHECK-NEXT: [[C18:%.*]] = or i1 [[C7]], [[TMP2]]
+; CHECK-NEXT: [[TMP3:%.*]] = sext i1 [[C3]] to i64
+; CHECK-NEXT: [[G26:%.*]] = getelementptr i1, ptr null, i64 [[TMP3]]
; CHECK-NEXT: store i16 [[L7]], ptr [[P:%.*]], align 2
; CHECK-NEXT: store i1 [[C18]], ptr [[P]], align 1
; CHECK-NEXT: store ptr [[G26]], ptr [[P]], align 8
diff --git a/llvm/test/Transforms/InstCombine/and-or-not.ll b/llvm/test/Transforms/InstCombine/and-or-not.ll
index ca093eba1b568..2e351c30ea1f7 100644
--- a/llvm/test/Transforms/InstCombine/and-or-not.ll
+++ b/llvm/test/Transforms/InstCombine/and-or-not.ll
@@ -761,11 +761,7 @@ define i4 @simplify_and_common_op_use1(i4 %x, i4 %y, i4 %z) {
define i4 @simplify_and_common_op_use2(i4 %x, i4 %y, i4 %z) {
; CHECK-LABEL: @simplify_and_common_op_use2(
; CHECK-NEXT: call void @use(i4 [[Y:%.*]])
-; CHECK-NEXT: [[TMP1:%.*]] = or i4 [[X:%.*]], [[Z:%.*]]
-; CHECK-NEXT: [[XYZ:%.*]] = or i4 [[TMP1]], [[Y]]
-; CHECK-NEXT: [[NOT_XYZ:%.*]] = xor i4 [[XYZ]], -1
-; CHECK-NEXT: [[R:%.*]] = and i4 [[NOT_XYZ]], [[X]]
-; CHECK-NEXT: ret i4 [[R]]
+; CHECK-NEXT: ret i4 0
;
%xy = or i4 %y, %x
call void @use(i4 %y)
@@ -779,12 +775,8 @@ define i4 @simplify_and_common_op_use2(i4 %x, i4 %y, i4 %z) {
define i4 @simplify_and_common_op_use3(i4 %x, i4 %y, i4 %z) {
; CHECK-LABEL: @simplify_and_common_op_use3(
-; CHECK-NEXT: [[XY:%.*]] = or i4 [[X:%.*]], [[Y:%.*]]
-; CHECK-NEXT: [[XYZ:%.*]] = or i4 [[XY]], [[Z:%.*]]
-; CHECK-NEXT: call void @use(i4 [[Z]])
-; CHECK-NEXT: [[NOT_XYZ:%.*]] = xor i4 [[XYZ]], -1
-; CHECK-NEXT: [[R:%.*]] = and i4 [[NOT_XYZ]], [[X]]
-; CHECK-NEXT: ret i4 [[R]]
+; CHECK-NEXT: call void @use(i4 [[Z:%.*]])
+; CHECK-NEXT: ret i4 0
;
%xy = or i4 %x, %y
%xyz = or i4 %xy, %z
diff --git a/llvm/test/Transforms/InstCombine/or.ll b/llvm/test/Transforms/InstCombine/or.ll
index b710f3afaa42c..0450255135f02 100644
--- a/llvm/test/Transforms/InstCombine/or.ll
+++ b/llvm/test/Transforms/InstCombine/or.ll
@@ -1781,10 +1781,8 @@ if.else:
; Tests from PR76554
define i32 @test_or_and_xor_constant(i32 %x, i32 %y) {
; CHECK-LABEL: @test_or_and_xor_constant(
-; CHECK-NEXT: [[A:%.*]] = and i32 [[X:%.*]], -2147483648
-; CHECK-NEXT: [[B:%.*]] = xor i32 [[A]], -2147483648
-; CHECK-NEXT: [[C:%.*]] = and i32 [[B]], [[Y:%.*]]
-; CHECK-NEXT: [[D:%.*]] = or i32 [[C]], [[A]]
+; CHECK-NEXT: [[A1:%.*]] = or i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[D:%.*]] = and i32 [[A1]], -2147483648
; CHECK-NEXT: ret i32 [[D]]
;
%a = and i32 %x, -2147483648
@@ -1796,9 +1794,8 @@ define i32 @test_or_and_xor_constant(i32 %x, i32 %y) {
define i32 @test_or_and_xor(i32 %a, i32 %b, i32 %c) {
; CHECK-LABEL: @test_or_and_xor(
-; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[A:%.*]], [[B:%.*]]
-; CHECK-NEXT: [[AND:%.*]] = and i32 [[XOR]], [[C:%.*]]
-; CHECK-NEXT: [[OR:%.*]] = or i32 [[AND]], [[A]]
+; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[B:%.*]], [[C:%.*]]
+; CHECK-NEXT: [[OR:%.*]] = or i32 [[TMP1]], [[A:%.*]]
; CHECK-NEXT: ret i32 [[OR]]
;
%xor = xor i32 %a, %b
@@ -1809,9 +1806,8 @@ define i32 @test_or_and_xor(i32 %a, i32 %b, i32 %c) {
define i32 @test_or_and_xor_commuted1(i32 %a, i32 %b, i32 %c) {
; CHECK-LABEL: @test_or_and_xor_commuted1(
-; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[B:%.*]], [[A:%.*]]
-; CHECK-NEXT: [[AND:%.*]] = and i32 [[XOR]], [[C:%.*]]
-; CHECK-NEXT: [[OR:%.*]] = or i32 [[AND]], [[A]]
+; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[B:%.*]], [[C:%.*]]
+; CHECK-NEXT: [[OR:%.*]] = or i32 [[TMP1]], [[A:%.*]]
; CHECK-NEXT: ret i32 [[OR]]
;
%xor = xor i32 %b, %a
@@ -1823,9 +1819,8 @@ define i32 @test_or_and_xor_commuted1(i32 %a, i32 %b, i32 %c) {
define i32 @test_or_and_xor_commuted2(i32 %a, i32 %b, i32 %c) {
; CHECK-LABEL: @test_or_and_xor_commuted2(
; CHECK-NEXT: [[CC:%.*]] = mul i32 [[C:%.*]], [[C]]
-; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[A:%.*]], [[B:%.*]]
-; CHECK-NEXT: [[AND:%.*]] = and i32 [[CC]], [[XOR]]
-; CHECK-NEXT: [[OR:%.*]] = or i32 [[AND]], [[A]]
+; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[CC]], [[B:%.*]]
+; CHECK-NEXT: [[OR:%.*]] = or i32 [[TMP1]], [[A:%.*]]
; CHECK-NEXT: ret i32 [[OR]]
;
%cc = mul i32 %c, %c
@@ -1838,9 +1833,8 @@ define i32 @test_or_and_xor_commuted2(i32 %a, i32 %b, i32 %c) {
define i32 @test_or_and_xor_commuted3(i32 %a, i32 %b, i32 %c) {
; CHECK-LABEL: @test_or_and_xor_commuted3(
; CHECK-NEXT: [[AA:%.*]] = mul i32 [[A:%.*]], [[A]]
-; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[AA]], [[B:%.*]]
-; CHECK-NEXT: [[AND:%.*]] = and i32 [[XOR]], [[C:%.*]]
-; CHECK-NEXT: [[OR:%.*]] = or i32 [[AA]], [[AND]]
+; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[B:%.*]], [[C:%.*]]
+; CHECK-NEXT: [[OR:%.*]] = or i32 [[AA]], [[TMP1]]
; CHECK-NEXT: ret i32 [[OR]]
;
%aa = mul i32 %a, %a
@@ -1854,8 +1848,8 @@ define i32 @test_or_and_xor_multiuse1(i32 %a, i32 %b, i32 %c) {
; CHECK-LABEL: @test_or_and_xor_multiuse1(
; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[A:%.*]], [[B:%.*]]
; CHECK-NEXT: call void @use(i32 [[XOR]])
-; CHECK-NEXT: [[AND:%.*]] = and i32 [[XOR]], [[C:%.*]]
-; CHECK-NEXT: [[OR:%.*]] = or i32 [[AND]], [[A]]
+; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[B]], [[C:%.*]]
+; CHECK-NEXT: [[OR:%.*]] = or i32 [[TMP1]], [[A]]
; CHECK-NEXT: ret i32 [[OR]]
;
%xor = xor i32 %a, %b
>From e48497498b2c2ac38101db2a2d722a37f85eed2e Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Wed, 31 Jan 2024 00:49:36 +0800
Subject: [PATCH 3/3] fixup! [InstCombine] Simplify and/or by replacing
operands with constants
---
.../InstCombine/InstCombineAndOrXor.cpp | 38 ++++++++-----------
.../Transforms/InstCombine/and-or-icmps.ll | 2 +-
.../test/Transforms/InstCombine/and-xor-or.ll | 2 +-
llvm/test/Transforms/InstCombine/or-xor.ll | 26 ++++++-------
llvm/test/Transforms/InstCombine/or.ll | 17 ++++++++-
5 files changed, 45 insertions(+), 40 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index f9571819bfc02..1cfa797be2207 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -2188,23 +2188,6 @@ static Value *simplifyAndOrWithOpReplaced(Value *X, Value *Y, bool IsAnd,
if (isa<Constant>(X) || X == Y)
return nullptr;
- auto RecursivelyReplaceUses = [&](Instruction::BinaryOps Opcode, Value *Op0,
- Value *Op1) -> Value * {
- if (Depth == 2)
- return nullptr;
-
- // TODO: Relax the one-use constraint to clean up existing hard-coded
- // simplifications.
- if (!X->hasOneUse())
- return nullptr;
- Value *NewOp0 = simplifyAndOrWithOpReplaced(Op0, Y, IsAnd, IC, Depth + 1);
- Value *NewOp1 = simplifyAndOrWithOpReplaced(Op1, Y, IsAnd, IC, Depth + 1);
- if (!NewOp0 && !NewOp1)
- return nullptr;
- return IC.Builder.CreateBinOp(Opcode, NewOp0 ? NewOp0 : Op0,
- NewOp1 ? NewOp1 : Op1);
- };
-
Value *RHS;
if (match(X, m_c_And(m_Specific(Y), m_Value(RHS)))) {
return IsAnd ? RHS : Constant::getNullValue(X->getType());
@@ -2222,11 +2205,20 @@ static Value *simplifyAndOrWithOpReplaced(Value *X, Value *Y, bool IsAnd,
return RHS;
}
- // FIXME: Is it correct to decompose xor if Y may be undef?
+ // Replace uses of Y in X recursively.
Value *Op0, *Op1;
- if (match(X, m_BitwiseLogic(m_Value(Op0), m_Value(Op1))))
- return RecursivelyReplaceUses(cast<BinaryOperator>(X)->getOpcode(), Op0,
- Op1);
+ if (Depth < 2 && match(X, m_BitwiseLogic(m_Value(Op0), m_Value(Op1)))) {
+ // TODO: Relax the one-use constraint to clean up existing hard-coded
+ // simplifications.
+ if (!X->hasOneUse())
+ return nullptr;
+ Value *NewOp0 = simplifyAndOrWithOpReplaced(Op0, Y, IsAnd, IC, Depth + 1);
+ Value *NewOp1 = simplifyAndOrWithOpReplaced(Op1, Y, IsAnd, IC, Depth + 1);
+ if (!NewOp0 && !NewOp1)
+ return nullptr;
+ return IC.Builder.CreateBinOp(cast<BinaryOperator>(X)->getOpcode(),
+ NewOp0 ? NewOp0 : Op0, NewOp1 ? NewOp1 : Op1);
+ }
return nullptr;
}
@@ -2753,7 +2745,7 @@ Instruction *InstCombinerImpl::visitAnd(BinaryOperator &I) {
return Res;
if (Value *V = simplifyAndOrWithOpReplaced(Op0, Op1, /*IsAnd*/ true, *this))
- return BinaryOperator::CreateAnd(Op1, V);
+ return BinaryOperator::CreateAnd(V, Op1);
if (Value *V = simplifyAndOrWithOpReplaced(Op1, Op0, /*IsAnd*/ true, *this))
return BinaryOperator::CreateAnd(Op0, V);
@@ -3957,7 +3949,7 @@ Instruction *InstCombinerImpl::visitOr(BinaryOperator &I) {
return Res;
if (Value *V = simplifyAndOrWithOpReplaced(Op0, Op1, /*IsAnd*/ false, *this))
- return BinaryOperator::CreateOr(Op1, V);
+ return BinaryOperator::CreateOr(V, Op1);
if (Value *V = simplifyAndOrWithOpReplaced(Op1, Op0, /*IsAnd*/ false, *this))
return BinaryOperator::CreateOr(Op0, V);
diff --git a/llvm/test/Transforms/InstCombine/and-or-icmps.ll b/llvm/test/Transforms/InstCombine/and-or-icmps.ll
index b43a65d5b8e3b..c8d348df5f427 100644
--- a/llvm/test/Transforms/InstCombine/and-or-icmps.ll
+++ b/llvm/test/Transforms/InstCombine/and-or-icmps.ll
@@ -370,7 +370,7 @@ define void @simplify_before_foldAndOfICmps(ptr %p) {
; CHECK-NEXT: [[B11:%.*]] = zext i1 [[TMP1]] to i16
; CHECK-NEXT: [[C10:%.*]] = icmp ugt i16 [[L7]], [[B11]]
; CHECK-NEXT: [[C7:%.*]] = icmp slt i16 [[L7]], 0
-; CHECK-NEXT: [[C3:%.*]] = and i1 [[C10]], [[C7]]
+; CHECK-NEXT: [[C3:%.*]] = and i1 [[C7]], [[C10]]
; CHECK-NEXT: [[TMP2:%.*]] = xor i1 [[C10]], true
; CHECK-NEXT: [[C18:%.*]] = or i1 [[C7]], [[TMP2]]
; CHECK-NEXT: [[TMP3:%.*]] = sext i1 [[C3]] to i64
diff --git a/llvm/test/Transforms/InstCombine/and-xor-or.ll b/llvm/test/Transforms/InstCombine/and-xor-or.ll
index ca0ba9206ffde..d072dc15cbb2c 100644
--- a/llvm/test/Transforms/InstCombine/and-xor-or.ll
+++ b/llvm/test/Transforms/InstCombine/and-xor-or.ll
@@ -113,7 +113,7 @@ define i32 @and_xor_not_common_op_extrause(i32 %a, i32 %b, ptr %dst) {
; CHECK-SAME: (i32 [[A:%.*]], i32 [[B:%.*]], ptr [[DST:%.*]]) {
; CHECK-NEXT: [[B2:%.*]] = xor i32 [[B]], -1
; CHECK-NEXT: store i32 [[B2]], ptr [[DST]], align 4
-; CHECK-NEXT: [[T4:%.*]] = and i32 [[A]], [[B]]
+; CHECK-NEXT: [[T4:%.*]] = and i32 [[B]], [[A]]
; CHECK-NEXT: ret i32 [[T4]]
;
%b2 = xor i32 %b, -1
diff --git a/llvm/test/Transforms/InstCombine/or-xor.ll b/llvm/test/Transforms/InstCombine/or-xor.ll
index 361aab6c21e27..443a0898df0d7 100644
--- a/llvm/test/Transforms/InstCombine/or-xor.ll
+++ b/llvm/test/Transforms/InstCombine/or-xor.ll
@@ -156,7 +156,7 @@ define i16 @test5_extra_use_not_xor(i16 %x, i16 %y, ptr %dst_not, ptr %dst_xor)
define i8 @xor_common_op_commute0(i8 %x, i8 %y) {
; CHECK-LABEL: @xor_common_op_commute0(
-; CHECK-NEXT: [[Z:%.*]] = or i8 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[Z:%.*]] = or i8 [[Y:%.*]], [[X:%.*]]
; CHECK-NEXT: ret i8 [[Z]]
;
%xor = xor i8 %x, %y
@@ -168,7 +168,7 @@ define i8 @xor_common_op_commute1(i8 %x, i8 %y) {
; CHECK-LABEL: @xor_common_op_commute1(
; CHECK-NEXT: [[XOR:%.*]] = xor i8 [[Y:%.*]], [[X:%.*]]
; CHECK-NEXT: call void @use(i8 [[XOR]])
-; CHECK-NEXT: [[Z:%.*]] = or i8 [[X]], [[Y]]
+; CHECK-NEXT: [[Z:%.*]] = or i8 [[Y]], [[X]]
; CHECK-NEXT: ret i8 [[Z]]
;
%xor = xor i8 %y, %x
@@ -1059,7 +1059,7 @@ define i8 @or_nand_xor_common_op_commute3_use3(i8 %x, i8 %y, i8 %z) {
; (a ^ 4) & (a ^ ~4) -> -1
define i32 @PR75692_1(i32 %x) {
; CHECK-LABEL: @PR75692_1(
-; CHECK-NEXT: ret i32 -1
+; CHECK-NEXT: ret i32 -1
;
%t2 = xor i32 %x, 4
%t3 = xor i32 %x, -5
@@ -1069,11 +1069,11 @@ define i32 @PR75692_1(i32 %x) {
; (a ^ 4) & (a ^ 3) is not -1
define i32 @PR75692_2(i32 %x) {
-; CHECK-LABEL: @PR75692_2
-; CHECK-NEXT: %t2 = xor i32 %x, 4
-; CHECK-NEXT: %t3 = xor i32 %x, -4
-; CHECK-NEXT: %t4 = or i32 %t2, %t3
-; CHECK-NEXT: ret i32 %t4
+; CHECK-LABEL: @PR75692_2(
+; CHECK-NEXT: [[T2:%.*]] = xor i32 [[X:%.*]], 4
+; CHECK-NEXT: [[T3:%.*]] = xor i32 [[X]], -4
+; CHECK-NEXT: [[T4:%.*]] = or i32 [[T2]], [[T3]]
+; CHECK-NEXT: ret i32 [[T4]]
;
%t2 = xor i32 %x, 4
%t3 = xor i32 %x, -4
@@ -1083,11 +1083,11 @@ define i32 @PR75692_2(i32 %x) {
; (a ^ 4) & (b ^ ~4) is not -1, since a != b is possible
define i32 @PR75692_3(i32 %x, i32 %y) {
-; CHECK-LABEL: @PR75692_3
-; CHECK-NEXT: %t2 = xor i32 %x, 4
-; CHECK-NEXT: %t3 = xor i32 %y, -5
-; CHECK-NEXT: %t4 = or i32 %t2, %t3
-; CHECK-NEXT: ret i32 %t4
+; CHECK-LABEL: @PR75692_3(
+; CHECK-NEXT: [[T2:%.*]] = xor i32 [[X:%.*]], 4
+; CHECK-NEXT: [[T3:%.*]] = xor i32 [[Y:%.*]], -5
+; CHECK-NEXT: [[T4:%.*]] = or i32 [[T2]], [[T3]]
+; CHECK-NEXT: ret i32 [[T4]]
;
%t2 = xor i32 %x, 4
%t3 = xor i32 %y, -5
diff --git a/llvm/test/Transforms/InstCombine/or.ll b/llvm/test/Transforms/InstCombine/or.ll
index 0450255135f02..2238b6bcc5653 100644
--- a/llvm/test/Transforms/InstCombine/or.ll
+++ b/llvm/test/Transforms/InstCombine/or.ll
@@ -1781,8 +1781,8 @@ if.else:
; Tests from PR76554
define i32 @test_or_and_xor_constant(i32 %x, i32 %y) {
; CHECK-LABEL: @test_or_and_xor_constant(
-; CHECK-NEXT: [[A1:%.*]] = or i32 [[X:%.*]], [[Y:%.*]]
-; CHECK-NEXT: [[D:%.*]] = and i32 [[A1]], -2147483648
+; CHECK-NEXT: [[TMP1:%.*]] = or i32 [[Y:%.*]], [[X:%.*]]
+; CHECK-NEXT: [[D:%.*]] = and i32 [[TMP1]], -2147483648
; CHECK-NEXT: ret i32 [[D]]
;
%a = and i32 %x, -2147483648
@@ -1888,3 +1888,16 @@ define i32 @test_or_and_xor_multiuse2(i32 %a, i32 %b, i32 %c) {
%or = or i32 %and, %a
ret i32 %or
}
+
+define i32 @test_or_add_xor(i32 %a, i32 %b, i32 %c) {
+; CHECK-LABEL: @test_or_add_xor(
+; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT: [[ADD:%.*]] = add i32 [[XOR]], [[C:%.*]]
+; CHECK-NEXT: [[OR:%.*]] = or i32 [[ADD]], [[A]]
+; CHECK-NEXT: ret i32 [[OR]]
+;
+ %xor = xor i32 %a, %b
+ %add = add i32 %xor, %c
+ %or = or i32 %add, %a
+ ret i32 %or
+}
More information about the llvm-commits
mailing list