-
Notifications
You must be signed in to change notification settings - Fork 10.7k
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
[InstCombine] Fold ((A ^ B) & C) | A
-> A | (B & C)
#76572
Conversation
@llvm/pr-subscribers-llvm-transforms Author: Yingwei Zheng (dtcxzyw) ChangesAlive2: https://alive2.llvm.org/ce/z/LfpKzZ Full diff: https://github.com/llvm/llvm-project/pull/76572.diff 2 Files Affected:
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index c03f50d75814d8..ac5bffe2633553 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -3520,6 +3520,12 @@ Instruction *InstCombinerImpl::visitOr(BinaryOperator &I) {
if (match(Op1, m_c_And(m_c_Or(m_Specific(Op0), m_Value(C)), m_Value(A))))
return BinaryOperator::CreateOr(Op0, Builder.CreateAnd(A, C));
+ // ((A ^ B) & C) | A -> A | (B & C)
+ if (match(&I, m_c_Or(m_Value(A),
+ m_OneUse(m_c_And(m_c_Xor(m_Deferred(A), m_Value(B)),
+ m_Value(C))))))
+ return BinaryOperator::CreateOr(A, Builder.CreateAnd(B, C));
+
if (Instruction *DeMorgan = matchDeMorgansLaws(I, *this))
return DeMorgan;
diff --git a/llvm/test/Transforms/InstCombine/or.ll b/llvm/test/Transforms/InstCombine/or.ll
index 573a11599141a7..0450255135f028 100644
--- a/llvm/test/Transforms/InstCombine/or.ll
+++ b/llvm/test/Transforms/InstCombine/or.ll
@@ -1777,3 +1777,114 @@ 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: [[A1:%.*]] = or i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[D:%.*]] = and i32 [[A1]], -2147483648
+; 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: [[TMP1:%.*]] = and i32 [[B:%.*]], [[C:%.*]]
+; CHECK-NEXT: [[OR:%.*]] = or i32 [[TMP1]], [[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: [[TMP1:%.*]] = and i32 [[B:%.*]], [[C:%.*]]
+; CHECK-NEXT: [[OR:%.*]] = or i32 [[TMP1]], [[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: [[TMP1:%.*]] = and i32 [[CC]], [[B:%.*]]
+; CHECK-NEXT: [[OR:%.*]] = or i32 [[TMP1]], [[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: [[TMP1:%.*]] = and i32 [[B:%.*]], [[C:%.*]]
+; CHECK-NEXT: [[OR:%.*]] = or i32 [[AA]], [[TMP1]]
+; 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: [[TMP1:%.*]] = and i32 [[B]], [[C:%.*]]
+; CHECK-NEXT: [[OR:%.*]] = or i32 [[TMP1]], [[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
+}
|
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 this list of transforms has reached the point where we need to generalize it if we want to keep adding increasingly exotic variants to it.
The general transform here is that in x | y
we can simplify x by replacing occurrences of y with 0 -- as long as we only look through bitwise operations. Similar for & and -1.
If we don't care about multi-use, this could be done with
bool InstCombinerImpl::replaceInInstruction(Value *V, Value *Old, Value *New, |
Alive2: https://alive2.llvm.org/ce/z/cNjDTR |
Alive2: https://alive2.llvm.org/ce/z/LfpKzZ
Fixes #76554.