-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[InstCombine] Fold select of symmetric selects #99245
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: Tim Gymnich (tgymnich) Changesfixes #98800 Fold patterns like: Full diff: https://github.com/llvm/llvm-project/pull/99245.diff 2 Files Affected:
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index 394dfca262e13..67f8986abef36 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -3012,6 +3012,28 @@ struct DecomposedSelect {
};
} // namespace
+/// Folds patterns like:
+/// select c2 (select c1 a b) (select c1 b a)
+/// into:
+/// select (xor c1 c2) b a
+static Instruction *
+foldSelectOfSymmetricSelect(SelectInst &OuterSelVal,
+ InstCombiner::BuilderTy &Builder) {
+
+ DecomposedSelect OuterSel, InnerSel;
+ if (!match(&OuterSelVal, m_Select(m_Value(OuterSel.Cond),
+ m_Select(m_Value(InnerSel.Cond),
+ m_Value(InnerSel.TrueVal),
+ m_Value(InnerSel.FalseVal)),
+ m_Select(m_Deferred(InnerSel.Cond),
+ m_Deferred(InnerSel.FalseVal),
+ m_Deferred(InnerSel.TrueVal)))))
+ return nullptr;
+
+ Value *Xor = Builder.CreateXor(InnerSel.Cond, OuterSel.Cond);
+ return SelectInst::Create(Xor, InnerSel.FalseVal, InnerSel.TrueVal);
+}
+
/// Look for patterns like
/// %outer.cond = select i1 %inner.cond, i1 %alt.cond, i1 false
/// %inner.sel = select i1 %inner.cond, i8 %inner.sel.t, i8 %inner.sel.f
@@ -3987,6 +4009,9 @@ Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) {
}
}
+ if (Instruction *I = foldSelectOfSymmetricSelect(SI, Builder))
+ return I;
+
if (Instruction *I = foldNestedSelects(SI, Builder))
return I;
diff --git a/llvm/test/Transforms/InstCombine/select-of-symmetric-selects.ll b/llvm/test/Transforms/InstCombine/select-of-symmetric-selects.ll
new file mode 100644
index 0000000000000..9391f841482ae
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/select-of-symmetric-selects.ll
@@ -0,0 +1,92 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+define i32 @select_of_symmetric_selects(i32 %a, i32 %b, i1 %c1, i1 %c2) {
+; CHECK-LABEL: @select_of_symmetric_selects(
+; CHECK-NEXT: [[TMP1:%.*]] = xor i1 [[C1:%.*]], [[C2:%.*]]
+; CHECK-NEXT: [[RET:%.*]] = select i1 [[TMP1]], i32 [[B:%.*]], i32 [[A:%.*]]
+; CHECK-NEXT: ret i32 [[RET]]
+;
+ %sel1 = select i1 %c1, i32 %a, i32 %b
+ %sel2 = select i1 %c1, i32 %b, i32 %a
+ %ret = select i1 %c2, i32 %sel1, i32 %sel2
+ ret i32 %ret
+}
+
+define i32 @select_of_symmetric_selects_negative1(i32 %a, i32 %b, i1 %c1, i1 %c2) {
+; CHECK-LABEL: @select_of_symmetric_selects_negative1(
+; CHECK-NEXT: [[SEL1:%.*]] = select i1 [[C1:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]
+; CHECK-NEXT: [[RET:%.*]] = select i1 [[C2:%.*]], i32 [[SEL1]], i32 [[A]]
+; CHECK-NEXT: ret i32 [[RET]]
+;
+ %sel1 = select i1 %c1, i32 %a, i32 %b
+ %sel2 = select i1 %c2, i32 %b, i32 %a
+ %ret = select i1 %c2, i32 %sel1, i32 %sel2
+ ret i32 %ret
+}
+
+define i32 @select_of_symmetric_selects_negative2(i32 %a, i32 %b, i32 %c, i1 %c1, i1 %c2) {
+; CHECK-LABEL: @select_of_symmetric_selects_negative2(
+; CHECK-NEXT: [[SEL1:%.*]] = select i1 [[C1:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]
+; CHECK-NEXT: [[SEL2:%.*]] = select i1 [[C1]], i32 [[B]], i32 [[C:%.*]]
+; CHECK-NEXT: [[RET:%.*]] = select i1 [[C2:%.*]], i32 [[SEL1]], i32 [[SEL2]]
+; CHECK-NEXT: ret i32 [[RET]]
+;
+ %sel1 = select i1 %c1, i32 %a, i32 %b
+ %sel2 = select i1 %c1, i32 %b, i32 %c
+ %ret = select i1 %c2, i32 %sel1, i32 %sel2
+ ret i32 %ret
+}
+
+declare void @use(i32)
+
+define i32 @select_of_symmetric_selects_multi_use(i32 %a, i32 %b, i1 %c1, i1 %c2) {
+; CHECK-LABEL: @select_of_symmetric_selects_multi_use(
+; CHECK-NEXT: [[SEL2:%.*]] = select i1 [[C1:%.*]], i32 [[B:%.*]], i32 [[A:%.*]]
+; CHECK-NEXT: call void @use(i32 [[SEL2]])
+; CHECK-NEXT: [[TMP1:%.*]] = xor i1 [[C1]], [[C2:%.*]]
+; CHECK-NEXT: [[RET:%.*]] = select i1 [[TMP1]], i32 [[B]], i32 [[A]]
+; CHECK-NEXT: ret i32 [[RET]]
+;
+ %sel1 = select i1 %c1, i32 %a, i32 %b
+ %sel2 = select i1 %c1, i32 %b, i32 %a
+ call void @use(i32 %sel2)
+ %ret = select i1 %c2, i32 %sel1, i32 %sel2
+ ret i32 %ret
+}
+
+define i32 @select_of_symmetric_selects_commuted(i32 %a, i32 %b, i1 %c1, i1 %c2) {
+; CHECK-LABEL: @select_of_symmetric_selects_commuted(
+; CHECK-NEXT: [[TMP1:%.*]] = xor i1 [[C1:%.*]], [[C2:%.*]]
+; CHECK-NEXT: [[RET:%.*]] = select i1 [[TMP1]], i32 [[A:%.*]], i32 [[B:%.*]]
+; CHECK-NEXT: ret i32 [[RET]]
+;
+ %sel1 = select i1 %c1, i32 %a, i32 %b
+ %sel2 = select i1 %c1, i32 %b, i32 %a
+ %ret = select i1 %c2, i32 %sel2, i32 %sel1
+ ret i32 %ret
+}
+
+define <4 x i32> @select_of_symmetric_selects_vector1(<4 x i32> %a, <4 x i32> %b, i1 %c1, i1 %c2) {
+; CHECK-LABEL: @select_of_symmetric_selects_vector1(
+; CHECK-NEXT: [[TMP1:%.*]] = xor i1 [[C1:%.*]], [[C2:%.*]]
+; CHECK-NEXT: [[RET:%.*]] = select i1 [[TMP1]], <4 x i32> [[A:%.*]], <4 x i32> [[B:%.*]]
+; CHECK-NEXT: ret <4 x i32> [[RET]]
+;
+ %sel1 = select i1 %c1, <4 x i32> %a, <4 x i32> %b
+ %sel2 = select i1 %c1, <4 x i32> %b, <4 x i32> %a
+ %ret = select i1 %c2, <4 x i32> %sel2, <4 x i32> %sel1
+ ret <4 x i32> %ret
+}
+
+define <4 x i32> @select_of_symmetric_selects_vector2(<4 x i32> %a, <4 x i32> %b, <4 x i1> %c1, <4 x i1> %c2) {
+; CHECK-LABEL: @select_of_symmetric_selects_vector2(
+; CHECK-NEXT: [[TMP1:%.*]] = xor <4 x i1> [[C1:%.*]], [[C2:%.*]]
+; CHECK-NEXT: [[RET:%.*]] = select <4 x i1> [[TMP1]], <4 x i32> [[A:%.*]], <4 x i32> [[B:%.*]]
+; CHECK-NEXT: ret <4 x i32> [[RET]]
+;
+ %sel1 = select <4 x i1> %c1, <4 x i32> %a, <4 x i32> %b
+ %sel2 = select <4 x i1> %c1, <4 x i32> %b, <4 x i32> %a
+ %ret = select <4 x i1> %c2, <4 x i32> %sel2, <4 x i32> %sel1
+ ret <4 x i32> %ret
+}
|
m_Value(InnerSel.FalseVal)), | ||
m_Select(m_Deferred(InnerSel.Cond), | ||
m_Deferred(InnerSel.FalseVal), | ||
m_Deferred(InnerSel.TrueVal))))) |
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.
Any value in handling select c2 (select c1 a b), (select ~c1, a, b)
with isKnownInversion
?
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 guess not. See the fuzzy matching results in dtcxzyw/llvm-opt-benchmark#976 and dtcxzyw/llvm-opt-benchmark#977.
Can you move the tests from #99244 into this PR? |
f68723b
to
94abd42
Compare
Would be nice if we could also proof the splat case: define <2 x i4> @src(<2 x i4> %a, <2 x i4> %b, <2 x i1> %c1, i1 %c2) {
#0:
%sel1 = select <2 x i1> %c1, <2 x i4> %a, <2 x i4> %b
%sel2 = select <2 x i1> %c1, <2 x i4> %b, <2 x i4> %a
%ret = select i1 %c2, <2 x i4> %sel1, <2 x i4> %sel2
ret <2 x i4> %ret
}
=>
define <2 x i4> @tgt(<2 x i4> %a, <2 x i4> %b, <2 x i1> %c1, i1 %c2) {
#0:
%splat = insertelement <2 x i1> poison, i1 %c2, i32 0
%splat1 = insertelement <2 x i1> %splat, i1 %c2, i32 1
%xor = xor <2 x i1> %c1, %splat1
%ret = select <2 x i1> %xor, <2 x i4> %b, <2 x i4> %a
ret <2 x i4> %ret
}
Transformation doesn't verify!
ERROR: Target is more poisonous than source
Example:
<2 x i4> %a = < #x0 (0) [based on undef value], #x0 (0) [based on undef value] >
<2 x i4> %b = < poison, poison >
<2 x i1> %c1 = < #x0 (0), #x1 (1) >
i1 %c2 = undef
Source:
<2 x i4> %sel1 = < poison, #x0 (0) >
<2 x i4> %sel2 = < #x0 (0), poison >
<2 x i4> %ret = < poison, poison >
Target:
<2 x i1> %splat = < #x1 (1), poison >
<2 x i1> %splat1 = < #x1 (1), #x0 (0) >
<2 x i1> %xor = < #x1 (1), #x1 (1) >
<2 x i4> %ret = < poison, poison >
Source value: < poison, poison >
Target value: < poison, poison > Pure vector case verifies just fine: https://alive2.llvm.org/ce/z/vTVRnC |
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. Thank you!
Please attach the alive2 proof to the PR description.
You are welcome. done! |
@tgymnich Do you have commit access? |
@dtcxzyw I don't have commit access |
would it be ok if only one of the inner selects is oneuse? |
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
@sftlbcn Not on x86_64 and aarch64 https://godbolt.org/z/na7f9nMKT |
Summary: fixes #98800 Fold patterns like: select c2 (select c1 a b) (select c1 b a) into: select (xor c1 c2) b a Alive2 proofs: https://alive2.llvm.org/ce/z/4QAm4K https://alive2.llvm.org/ce/z/vTVRnC Test Plan: Reviewers: Subscribers: Tasks: Tags: Differential Revision: https://phabricator.intern.facebook.com/D60250948
fixes #98800
Fold patterns like:
select c2 (select c1 a b) (select c1 b a)
into:
select (xor c1 c2) b a
Alive2 proofs:
https://alive2.llvm.org/ce/z/4QAm4K
https://alive2.llvm.org/ce/z/vTVRnC