-
Notifications
You must be signed in to change notification settings - Fork 11.2k
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 select Cond, not X, X
into Cond ^ X
#90089
Conversation
@llvm/pr-subscribers-llvm-transforms Author: Yingwei Zheng (dtcxzyw) ChangesSee the following example:
I find that this pattern is common in C/C++/Rust code base.
Alive2: https://alive2.llvm.org/ce/z/hSmkHS Full diff: https://github.com/llvm/llvm-project/pull/90089.diff 2 Files Affected:
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index 117eb7a1dcc933..69341c8d3f41cf 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -3985,5 +3985,10 @@ Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) {
}
}
+ if (CondVal->getType() == SI.getType() && impliesPoison(FalseVal, TrueVal) &&
+ isImpliedCondition(FalseVal, TrueVal, DL, /*LHSIsTrue=*/true) == false &&
+ isImpliedCondition(FalseVal, TrueVal, DL, /*LHSIsTrue=*/false) == true)
+ return BinaryOperator::CreateXor(CondVal, FalseVal);
+
return nullptr;
}
diff --git a/llvm/test/Transforms/InstCombine/select-cmp.ll b/llvm/test/Transforms/InstCombine/select-cmp.ll
index 711fac542179f8..3ec89af8d868c7 100644
--- a/llvm/test/Transforms/InstCombine/select-cmp.ll
+++ b/llvm/test/Transforms/InstCombine/select-cmp.ll
@@ -345,4 +345,78 @@ define i1 @icmp_no_common(i1 %c, i8 %x, i8 %y, i8 %z) {
ret i1 %r
}
+define i1 @test_select_inverse1(i64 %x, i1 %y) {
+; CHECK-LABEL: @test_select_inverse1(
+; CHECK-NEXT: [[CMP2:%.*]] = icmp eq i64 [[X:%.*]], 0
+; CHECK-NEXT: [[SEL:%.*]] = xor i1 [[CMP2]], [[Y:%.*]]
+; CHECK-NEXT: ret i1 [[SEL]]
+;
+ %cmp1 = icmp ne i64 %x, 0
+ %cmp2 = icmp eq i64 %x, 0
+ %sel = select i1 %y, i1 %cmp1, i1 %cmp2
+ ret i1 %sel
+}
+
+define i1 @test_select_inverse2(i64 %x, i1 %y) {
+; CHECK-LABEL: @test_select_inverse2(
+; CHECK-NEXT: [[CMP2:%.*]] = icmp slt i64 [[X:%.*]], 0
+; CHECK-NEXT: [[SEL:%.*]] = xor i1 [[CMP2]], [[Y:%.*]]
+; CHECK-NEXT: ret i1 [[SEL]]
+;
+ %cmp1 = icmp sgt i64 %x, -1
+ %cmp2 = icmp slt i64 %x, 0
+ %sel = select i1 %y, i1 %cmp1, i1 %cmp2
+ ret i1 %sel
+}
+
+define i1 @test_select_inverse3(ptr %x, i1 %y) {
+; CHECK-LABEL: @test_select_inverse3(
+; CHECK-NEXT: [[CMP2:%.*]] = icmp ne ptr [[X:%.*]], null
+; CHECK-NEXT: [[SEL:%.*]] = xor i1 [[CMP2]], [[Y:%.*]]
+; CHECK-NEXT: ret i1 [[SEL]]
+;
+ %cmp1 = icmp eq ptr %x, null
+ %cmp2 = icmp ne ptr %x, null
+ %sel = select i1 %y, i1 %cmp1, i1 %cmp2
+ ret i1 %sel
+}
+
+define i1 @test_select_inverse_fail(i64 %x, i1 %y) {
+; CHECK-LABEL: @test_select_inverse_fail(
+; CHECK-NEXT: [[CMP1:%.*]] = icmp sgt i64 [[X:%.*]], 0
+; CHECK-NEXT: [[CMP2:%.*]] = icmp slt i64 [[X]], 0
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[Y:%.*]], i1 [[CMP1]], i1 [[CMP2]]
+; CHECK-NEXT: ret i1 [[SEL]]
+;
+ %cmp1 = icmp sgt i64 %x, 0
+ %cmp2 = icmp slt i64 %x, 0
+ %sel = select i1 %y, i1 %cmp1, i1 %cmp2
+ ret i1 %sel
+}
+
+define <2 x i1> @test_select_inverse_vec(<2 x i64> %x, <2 x i1> %y) {
+; CHECK-LABEL: @test_select_inverse_vec(
+; CHECK-NEXT: [[CMP2:%.*]] = icmp eq <2 x i64> [[X:%.*]], zeroinitializer
+; CHECK-NEXT: [[SEL:%.*]] = xor <2 x i1> [[CMP2]], [[Y:%.*]]
+; CHECK-NEXT: ret <2 x i1> [[SEL]]
+;
+ %cmp1 = icmp ne <2 x i64> %x, zeroinitializer
+ %cmp2 = icmp eq <2 x i64> %x, zeroinitializer
+ %sel = select <2 x i1> %y, <2 x i1> %cmp1, <2 x i1> %cmp2
+ ret <2 x i1> %sel
+}
+
+define <2 x i1> @test_select_inverse_vec_fail(<2 x i64> %x, i1 %y) {
+; CHECK-LABEL: @test_select_inverse_vec_fail(
+; CHECK-NEXT: [[CMP1:%.*]] = icmp ne <2 x i64> [[X:%.*]], zeroinitializer
+; CHECK-NEXT: [[CMP2:%.*]] = icmp eq <2 x i64> [[X]], zeroinitializer
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[Y:%.*]], <2 x i1> [[CMP1]], <2 x i1> [[CMP2]]
+; CHECK-NEXT: ret <2 x i1> [[SEL]]
+;
+ %cmp1 = icmp ne <2 x i64> %x, zeroinitializer
+ %cmp2 = icmp eq <2 x i64> %x, zeroinitializer
+ %sel = select i1 %y, <2 x i1> %cmp1, <2 x i1> %cmp2
+ ret <2 x i1> %sel
+}
+
declare void @use(i1)
|
@@ -3985,5 +3985,10 @@ Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) { | |||
} | |||
} | |||
|
|||
if (CondVal->getType() == SI.getType() && impliesPoison(FalseVal, TrueVal) && | |||
isImpliedCondition(FalseVal, TrueVal, DL, /*LHSIsTrue=*/true) == false && | |||
isImpliedCondition(FalseVal, TrueVal, DL, /*LHSIsTrue=*/false) == 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.
Would it cover the motivating cases to check for icmp with same operands and inverted predicate?
This is a pretty weird check and I'm not sure if doing the poison check in one direction is sufficient here.
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.
Hm, I guess it's fine as FalseVal is the value we're replacing with.
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'd still like an answer to this question. This is a very roundabout way to check whether the conditions are inverses of each other, and it does have some overhead (http://llvm-compile-time-tracker.com/compare.php?from=76a3be7c766bd55221c3d0d0a74c42f82c5d76ed&to=1731798e2ef457fb5cabd29ae054c700b8b13c14&stat=instructions%3Au). Not enough to make me overly concerned, about enough to contribute to the death by a thousand cuts :)
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 can simplify the implementation to just handle select Cond, icmp X, C1, icmp X, C2
, which covers all the motivating cases.
@@ -3985,5 +3985,10 @@ Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) { | |||
} | |||
} | |||
|
|||
if (CondVal->getType() == SI.getType() && impliesPoison(FalseVal, TrueVal) && | |||
isImpliedCondition(FalseVal, TrueVal, DL, /*LHSIsTrue=*/true) == false && | |||
isImpliedCondition(FalseVal, TrueVal, DL, /*LHSIsTrue=*/false) == 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.
Shouldnt the check be (isImpliedCondition(..., true) ^ isImpliedCondition(..., 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.
When FVal implies TVal and !FVal -> !TVal, TVal must be the same as FVal. If not, we need a canonicalization for this pattern.
BTW, I tried isImpliedCondition(FalseVal, TrueVal, DL, /*LHSIsTrue=*/true) == true && isImpliedCondition(FalseVal, TrueVal, DL, /*LHSIsTrue=*/false) == false
before submitting this patch. But it didn't improve the result.
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.
Maybe add a comment that the inverse case doesn't naturally occur in code to avoid people re-testing / re-submitting.
This food gives me a few more ideas. Particularly it takes me back to that rejected PR. Perhaps I could use this property somehow to fold expressions regarding bitwise nots of each other... |
LGTM, wait for 1 more |
The test failure is unrelated with this patch. |
I've also seen that test fail on a few buildbots. |
Ping @nikic |
LGTM! |
Closing in favor of #93591. |
See the following example:
I find that this pattern is common in C/C++/Rust code base.
This patch folds
select Cond, Y, X
intoCond ^ X
iff:Alive2: https://alive2.llvm.org/ce/z/hSmkHS