Skip to content

[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

Merged
merged 4 commits into from
Jul 17, 2024

Conversation

tgymnich
Copy link
Member

@tgymnich tgymnich commented Jul 16, 2024

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

@tgymnich tgymnich changed the title [InstCombine] Select [InstCombine] Select of Symmetric Selects Jul 16, 2024
@tgymnich tgymnich marked this pull request as ready for review July 16, 2024 21:34
@tgymnich tgymnich requested a review from nikic as a code owner July 16, 2024 21:34
@llvmbot
Copy link
Member

llvmbot commented Jul 16, 2024

@llvm/pr-subscribers-llvm-transforms

Author: Tim Gymnich (tgymnich)

Changes

fixes #98800

Fold patterns like:
select c2 (select c1 a b) (select c1 b a)
into:
select (xor c1 c2) b a


Full diff: https://github.com/llvm/llvm-project/pull/99245.diff

2 Files Affected:

  • (modified) llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp (+25)
  • (added) llvm/test/Transforms/InstCombine/select-of-symmetric-selects.ll (+92)
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
+}

@goldsteinn goldsteinn requested a review from dtcxzyw July 17, 2024 06:51
m_Value(InnerSel.FalseVal)),
m_Select(m_Deferred(InnerSel.Cond),
m_Deferred(InnerSel.FalseVal),
m_Deferred(InnerSel.TrueVal)))))
Copy link
Contributor

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?

Copy link
Member

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.

@dtcxzyw
Copy link
Member

dtcxzyw commented Jul 17, 2024

Can you move the tests from #99244 into this PR?
See https://llvm.org/docs/InstCombineContributorGuide.html#precommit-tests.

@tgymnich
Copy link
Member Author

tgymnich commented Jul 17, 2024

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

dtcxzyw added a commit to dtcxzyw/llvm-opt-benchmark that referenced this pull request Jul 17, 2024
Copy link
Member

@dtcxzyw dtcxzyw left a 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.

@tgymnich
Copy link
Member Author

You are welcome.

done!

@dtcxzyw
Copy link
Member

dtcxzyw commented Jul 17, 2024

@tgymnich Do you have commit access?

@tgymnich
Copy link
Member Author

tgymnich commented Jul 17, 2024

@dtcxzyw I don't have commit access

@sftlbcn
Copy link

sftlbcn commented Jul 17, 2024

would it be ok if only one of the inner selects is oneuse?

Copy link
Contributor

@nikic nikic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@tgymnich
Copy link
Member Author

@sftlbcn Not on x86_64 and aarch64 https://godbolt.org/z/na7f9nMKT

@dtcxzyw dtcxzyw changed the title [InstCombine] Select of Symmetric Selects [InstCombine] Fold select of symmetric selects Jul 17, 2024
@dtcxzyw dtcxzyw merged commit c034c44 into llvm:main Jul 17, 2024
5 of 6 checks passed
yuxuanchen1997 pushed a commit that referenced this pull request Jul 25, 2024
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
@tgymnich tgymnich deleted the sel-of-sym-sels branch October 9, 2024 19:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[instcombine] select of symmetric selects
6 participants