-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[InstCombine] Improve select equiv fold for plain condition #83405
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: Allen (vfdff) ChangesFold select if the user of its select user indicates the condition Fix #83225 Full diff: https://github.com/llvm/llvm-project/pull/83405.diff 2 Files Affected:
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index 71fa9b9ba41ebb..1485fe8674b720 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -500,6 +500,29 @@ static bool isSelect01(const APInt &C1I, const APInt &C2I) {
return C1I.isOne() || C1I.isAllOnes() || C2I.isOne() || C2I.isAllOnes();
}
+/// Try to simplify a select instruction when the user of its select user
+/// indicates the condition.
+static bool simplifySeqSelectWithSameCond(Value *Cond, Value *F) {
+ Value *FalseVal = F;
+ Value *CondNext;
+ Value *FalseNext;
+ while (match(FalseVal, m_Select(m_Value(CondNext), m_Value(),
+ m_OneUse(m_Value(FalseNext))))) {
+ if (CondNext == Cond) {
+ auto *SI = cast<SelectInst>(FalseVal);
+ LLVM_DEBUG(dbgs() << "IC: Fold " << SI << " with " << *FalseNext << '\n');
+
+ SI->replaceAllUsesWith(FalseNext);
+ SI->eraseFromParent();
+ return true;
+ }
+
+ FalseVal = FalseNext;
+ }
+
+ return false;
+}
+
/// Try to fold the select into one of the operands to allow further
/// optimization.
Instruction *InstCombinerImpl::foldSelectIntoOp(SelectInst &SI, Value *TrueVal,
@@ -557,6 +580,9 @@ Instruction *InstCombinerImpl::foldSelectIntoOp(SelectInst &SI, Value *TrueVal,
if (Instruction *R = TryFoldSelectIntoOp(SI, FalseVal, TrueVal, true))
return R;
+ if (simplifySeqSelectWithSameCond(SI.getCondition(), FalseVal))
+ return &SI;
+
return nullptr;
}
diff --git a/llvm/test/Transforms/InstCombine/select.ll b/llvm/test/Transforms/InstCombine/select.ll
index c5f1b77c6d7404..068d168876a328 100644
--- a/llvm/test/Transforms/InstCombine/select.ll
+++ b/llvm/test/Transforms/InstCombine/select.ll
@@ -3672,3 +3672,33 @@ define i32 @src_select_xxory_eq0_xorxy_y(i32 %x, i32 %y) {
%cond = select i1 %xor0, i32 %xor, i32 %y
ret i32 %cond
}
+
+define i32 @sequence_select_with_same_cond (i1 %c1, i1 %c2){
+; CHECK-LABEL: @sequence_select_with_same_cond(
+; CHECK-NEXT: [[S1:%.*]] = select i1 [[C1:%.*]], i32 23, i32 45
+; CHECK-NEXT: [[S2:%.*]] = select i1 [[C2:%.*]], i32 666, i32 [[S1]]
+; CHECK-NEXT: [[S3:%.*]] = select i1 [[C1]], i32 789, i32 [[S2]]
+; CHECK-NEXT: ret i32 [[S3]]
+;
+ %s1 = select i1 %c1, i32 23, i32 45
+ %s2 = select i1 %c2, i32 666, i32 %s1
+ %s3 = select i1 %c1, i32 789, i32 %s2
+ ret i32 %s3
+}
+
+declare void @use32(i32)
+
+define i32 @sequence_select_with_same_cond_extra_use (i1 %c1, i1 %c2){
+; CHECK-LABEL: @sequence_select_with_same_cond_extra_use(
+; CHECK-NEXT: [[S1:%.*]] = select i1 [[C1:%.*]], i32 23, i32 45
+; CHECK-NEXT: call void @use32(i32 [[S1]])
+; CHECK-NEXT: [[S2:%.*]] = select i1 [[C2:%.*]], i32 666, i32 [[S1]]
+; CHECK-NEXT: [[S3:%.*]] = select i1 [[C1]], i32 789, i32 [[S2]]
+; CHECK-NEXT: ret i32 [[S3]]
+;
+ %s1 = select i1 %c1, i32 23, i32 45
+ call void @use32(i32 %s1)
+ %s2 = select i1 %c2, i32 666, i32 %s1
+ %s3 = select i1 %c1, i32 789, i32 %s2
+ ret i32 %s3
+}
|
I think this should be a minor generalization of some existing simplifyWithOpReplaced or foldSelectValueEquivalence style fold. |
|
b246978
to
03d2c81
Compare
while (match(ValOp, m_Select(m_Value(CondNext), m_Value(), m_Value()))) { | ||
if (CondNext == CondVal) | ||
if (simplifyWithOpReplaced(ValOp, CondVal, RepOp, SQ, | ||
/* AllowRefinement */ true)) { |
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 don't understand what the simplifyWithOpReplaced is doing anymore.
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.
deleted the redundant call simplifyWithOpReplaced
, thanks
The select equivalence fold takes a select like "X == Y ? A : B" and then tries to simplify A based on the known equality. This patch also uses it for the case were we have just "C ? A : B" by treating the condition as either "C == 1" or "C != 1". This is intended as an alternative to llvm#83405 for fixing llvm#83225.
FWIW, what I had in mind with my comment was more something along the lines of #85663, which generalizes the existing select equivalence fold. The disadvantage is that it does not handle the multi-use example out of the box (but could be generalized). |
hi @nikic, I saw #85663 is closed because the compile time regression, so would you suggest I continue with current solution? |
ping ? |
InstCombinerImpl &IC) { | ||
Value *CondVal = SI.getCondition(); | ||
if (match(CondVal, m_ImmConstant())) | ||
return false; |
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.
Isn't it handled by simplifySelectInst
?
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.
Yes, it isn't.
#85663 is a more general improvement by nikic, and it has some compile time regression, so I continue on this solution.
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.
llvm-project/llvm/lib/Analysis/InstructionSimplify.cpp
Lines 4767 to 4771 in a612524
if (auto *CondC = dyn_cast<Constant>(Cond)) { | |
if (auto *TrueC = dyn_cast<Constant>(TrueVal)) | |
if (auto *FalseC = dyn_cast<Constant>(FalseVal)) | |
if (Constant *C = ConstantFoldSelectInstruction(CondC, TrueC, FalseC)) | |
return C; |
Can you provide a test to show that the early exit here is necessary?
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.
Thanks, I delete the early exit checking.
I added it just because I thought it was an optimized expression and didn't need to be optimized.
Value *CondNext; | ||
while (match(ValOp, m_Select(m_Value(CondNext), m_Value(), m_Value()))) { | ||
if (CondNext == CondVal) { | ||
IC.replaceOperand(*SINext, OpIndex, |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
967f70b
to
6247957
Compare
; CHECK-NEXT: [[SEL3:%.*]] = select i1 [[TMP1]], i8 [[SEL1_NEW]], i8 [[SEL4]] | ||
; CHECK-NEXT: ret i8 [[SEL3]] | ||
; | ||
%sel0 = select i1 %cond0, i8 %a, i8 %b |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
/// Try to simplify a select instruction when the user of its select user | ||
/// indicates the condition. |
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 don't know how to parse this comment "when the user of its select user". Can you rephrase this whole thing?
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.
Add comment with a example.
%sel2 = select i1 %cond1, i8 %sel1, i8 3 | ||
%sel3 = select i1 %cond0, i8 %sel1, i8 %sel2 | ||
ret i8 %sel3 | ||
} |
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.
Can you add some tests that show flag preservation behavior?
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.
Added sequence_select_with_same_cond_float_and_fmf_flag1
and sequence_select_with_same_cond_float_and_fmf_flag2
, thanks
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.
Better to have tests with different flags (and more specific than fast). Need to see the union / intersect behavior
Fold select if the user of its select user indicates the condition Fix llvm#83225
Refactor with a lambda function to address both false and true value.
According performace considerations, I deleted the support with multi-use select node and rebase to top upstream as the code conflicts. |
close to avoid blocking other solutions |
What do you mean? If there's an alternative patch, it should be mentioned in the closing message. Otherwise, why close it |
Oh,sorry, I reopen it now. |
That is the solution to many combine problems. It's a common and reasonable restriction |
// directly. | ||
// It is not profitable to build a new select for SINext with multi-arms. | ||
while (match(ValOp, m_Select(m_Value(CondNext), m_Value(), m_Value()))) { | ||
if (CondNext == CondVal && SINext->hasOneUse()) { |
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.
It should be match(ValOp, m_OneUse(m_Select(....))
then drop the SINext->hasOneUse()
, otherwise this won't fold the base select if it has multiple uses.
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.
For the following case, it can be optimized because %sel1 is a sinle use node.
If we adjust according above change, then it will not optimize because the operand of %sel1 (%sel0)is multi used.
define i8 @sequence_select_with_same_cond_multi_arms_src(i1 %cond0, i1 %cond1, i8 %a, i8 %b) {
%sel0 = select i1 %cond0, i8 %a, i8 %b
%sel1 = select i1 %cond1, i8 %sel0, i8 2; %sel1 used in single node
%sel2 = select i1 %cond1, i8 %sel0, i8 3
%sel3 = select i1 %cond0, i8 %sel1, i8 %sel2
ret i8 %sel3
}
define i8 @sequence_select_with_same_cond_multi_arms_tgt(i1 %cond0, i1 %cond1, i8 %a, i8 %b) {
%sel0 = select i1 %cond0, i8 %a, i8 %b
%sel1 = select i1 %cond1, i8 %a, i8 2; %sel1 used in single node
%sel2 = select i1 %cond1, i8 %sel0, i8 3
%sel3 = select i1 %cond0, i8 %sel1, i8 %sel2
ret i8 %sel3
}
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.
Makes sense, what about V == SINext || SINext->hasOneUse()
then?
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.
do you mean ValOp == SINext || SINext->hasOneUse() ? the condition ValOp == SINext is protected by ValOp = SINext->getOperand(OpIndex), and the CondNext == CondVal is also need, consider case. https://alive2.llvm.org/ce/z/TKLmMG
define i32 @src(i32 %a, i1 %c1, i1 %c2, i1 %c3){
%s1 = select i1 %c1, i32 23, i32 45
%s2 = select i1 %c2, i32 666, i32 %s1 ; this node can be optimized iff %c1 == %c3
%s3 = select i1 %c3, i32 789, i32 %s2
ret i32 %s3
}
define i32 @tgt(i32 %a, i1 %c1, i1 %c2, i1 %c3){
%s1 = select i1 %c1, i32 23, i32 45
%s2 = select i1 %c2, i32 666, i32 45
%s3 = select i1 %c3, i32 789, i32 %s2
ret i32 %s3
}
Ping. I need this patch to unblock #119884. |
hi @dtcxzyw I don't work on this issue now, please update your MR at your convenience |
Fold select if the user of its select user indicates the condition
Fix #83225