-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[ValueTracking] Respect samesign
flag in isKnownInversion
#112390
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: Yingwei Zheng (dtcxzyw) ChangesIn #93591 we introduced Alive2 link: https://alive2.llvm.org/ce/z/rj3EwQ (Please run it locally with this patch and AliveToolkit/alive2#1098). This approach is the most conservative way in my mind to address this problem. If Pros: No API breaking/fast I also tried the following solutions:
Fixes #112350. Full diff: https://github.com/llvm/llvm-project/pull/112390.diff 2 Files Affected:
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index c71d17011d7a0d..a3c4eee7d78a43 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -8526,14 +8526,26 @@ bool llvm::isKnownInversion(const Value *X, const Value *Y) {
!match(Y, m_c_ICmp(Pred2, m_Specific(A), m_Value(C))))
return false;
+ // If they are only differ in predicate. They must both have samesign flag or
+ // not.
if (B == C)
- return Pred1 == ICmpInst::getInversePredicate(Pred2);
+ return Pred1 == ICmpInst::getInversePredicate(Pred2) &&
+ (!cast<ICmpInst>(X)->hasSameSign() ||
+ cast<ICmpInst>(Y)->hasSameSign());
+ ;
// Try to infer the relationship from constant ranges.
const APInt *RHSC1, *RHSC2;
if (!match(B, m_APInt(RHSC1)) || !match(C, m_APInt(RHSC2)))
return false;
+ // They must both have samesign flag or not. And sign bits of two RHSCs should
+ // match.
+ if (cast<ICmpInst>(X)->hasSameSign() &&
+ (!cast<ICmpInst>(Y)->hasSameSign() ||
+ RHSC1->isNonNegative() != RHSC2->isNonNegative()))
+ return false;
+
const auto CR1 = ConstantRange::makeExactICmpRegion(Pred1, *RHSC1);
const auto CR2 = ConstantRange::makeExactICmpRegion(Pred2, *RHSC2);
diff --git a/llvm/test/Transforms/InstCombine/select-cmp.ll b/llvm/test/Transforms/InstCombine/select-cmp.ll
index 234815949d77d4..a2eabf1ede1994 100644
--- a/llvm/test/Transforms/InstCombine/select-cmp.ll
+++ b/llvm/test/Transforms/InstCombine/select-cmp.ll
@@ -480,6 +480,93 @@ define i1 @test_select_inverse_nonconst4(i64 %x, i64 %y, i64 %z, i1 %cond) {
ret i1 %sel
}
+define i1 @test_select_inverse_samesign_true_arm(i64 %x, i64 %y, i1 %cond) {
+; CHECK-LABEL: @test_select_inverse_samesign_true_arm(
+; CHECK-NEXT: [[CMP2:%.*]] = icmp uge i64 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[SEL:%.*]] = xor i1 [[COND:%.*]], [[CMP2]]
+; CHECK-NEXT: ret i1 [[SEL]]
+;
+ %cmp1 = icmp samesign ult i64 %x, %y
+ %cmp2 = icmp uge i64 %x, %y
+ %sel = select i1 %cond, i1 %cmp1, i1 %cmp2
+ ret i1 %sel
+}
+
+define i1 @test_select_inverse_samesign_false_arm(i64 %x, i64 %y, i1 %cond) {
+; CHECK-LABEL: @test_select_inverse_samesign_false_arm(
+; CHECK-NEXT: [[CMP1:%.*]] = icmp ult i64 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[CMP2:%.*]] = icmp samesign uge i64 [[X]], [[Y]]
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND:%.*]], i1 [[CMP1]], i1 [[CMP2]]
+; CHECK-NEXT: ret i1 [[SEL]]
+;
+ %cmp1 = icmp ult i64 %x, %y
+ %cmp2 = icmp samesign uge i64 %x, %y
+ %sel = select i1 %cond, i1 %cmp1, i1 %cmp2
+ ret i1 %sel
+}
+
+define i1 @test_select_inverse_samesign_both(i64 %x, i64 %y, i1 %cond) {
+; CHECK-LABEL: @test_select_inverse_samesign_both(
+; CHECK-NEXT: [[CMP2:%.*]] = icmp samesign uge i64 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[SEL:%.*]] = xor i1 [[COND:%.*]], [[CMP2]]
+; CHECK-NEXT: ret i1 [[SEL]]
+;
+ %cmp1 = icmp samesign ult i64 %x, %y
+ %cmp2 = icmp samesign uge i64 %x, %y
+ %sel = select i1 %cond, i1 %cmp1, i1 %cmp2
+ ret i1 %sel
+}
+
+define i1 @test_select_inverse_samesign_false_arm_rhsc_same_sign(i64 %x, i64 %y, i1 %cond) {
+; CHECK-LABEL: @test_select_inverse_samesign_false_arm_rhsc_same_sign(
+; CHECK-NEXT: [[CMP1:%.*]] = icmp ult i64 [[X:%.*]], 11
+; CHECK-NEXT: [[CMP2:%.*]] = icmp samesign ugt i64 [[X]], 10
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND:%.*]], i1 [[CMP1]], i1 [[CMP2]]
+; CHECK-NEXT: ret i1 [[SEL]]
+;
+ %cmp1 = icmp ult i64 %x, 11
+ %cmp2 = icmp samesign ugt i64 %x, 10
+ %sel = select i1 %cond, i1 %cmp1, i1 %cmp2
+ ret i1 %sel
+}
+
+define i1 @test_select_inverse_samesign_true_arm_rhsc_same_sign(i64 %x, i64 %y, i1 %cond) {
+; CHECK-LABEL: @test_select_inverse_samesign_true_arm_rhsc_same_sign(
+; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i64 [[X:%.*]], 10
+; CHECK-NEXT: [[SEL:%.*]] = xor i1 [[COND:%.*]], [[CMP2]]
+; CHECK-NEXT: ret i1 [[SEL]]
+;
+ %cmp1 = icmp samesign ult i64 %x, 11
+ %cmp2 = icmp ugt i64 %x, 10
+ %sel = select i1 %cond, i1 %cmp1, i1 %cmp2
+ ret i1 %sel
+}
+
+define i1 @test_select_inverse_samesign_both_rhsc_same_sign(i64 %x, i64 %y, i1 %cond) {
+; CHECK-LABEL: @test_select_inverse_samesign_both_rhsc_same_sign(
+; CHECK-NEXT: [[CMP2:%.*]] = icmp samesign ugt i64 [[X:%.*]], 10
+; CHECK-NEXT: [[SEL:%.*]] = xor i1 [[COND:%.*]], [[CMP2]]
+; CHECK-NEXT: ret i1 [[SEL]]
+;
+ %cmp1 = icmp samesign ult i64 %x, 11
+ %cmp2 = icmp samesign ugt i64 %x, 10
+ %sel = select i1 %cond, i1 %cmp1, i1 %cmp2
+ ret i1 %sel
+}
+
+define i1 @test_select_inverse_samesign_both_rhsc_diff_sign(i64 %x, i64 %y, i1 %cond) {
+; CHECK-LABEL: @test_select_inverse_samesign_both_rhsc_diff_sign(
+; CHECK-NEXT: [[CMP1:%.*]] = icmp samesign slt i64 [[X:%.*]], 0
+; CHECK-NEXT: [[CMP2:%.*]] = icmp samesign sgt i64 [[X]], -1
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND:%.*]], i1 [[CMP1]], i1 [[CMP2]]
+; CHECK-NEXT: ret i1 [[SEL]]
+;
+ %cmp1 = icmp samesign slt i64 %x, 0
+ %cmp2 = icmp samesign sgt i64 %x, -1
+ %sel = select i1 %cond, i1 %cmp1, i1 %cmp2
+ ret i1 %sel
+}
+
define i1 @sel_icmp_two_cmp(i1 %c, i32 %a1, i32 %a2, i32 %a3, i32 %a4) {
; CHECK-LABEL: @sel_icmp_two_cmp(
; CHECK-NEXT: [[CMP1:%.*]] = icmp ule i32 [[A1:%.*]], [[A2:%.*]]
|
@llvm/pr-subscribers-llvm-analysis Author: Yingwei Zheng (dtcxzyw) ChangesIn #93591 we introduced Alive2 link: https://alive2.llvm.org/ce/z/rj3EwQ (Please run it locally with this patch and AliveToolkit/alive2#1098). This approach is the most conservative way in my mind to address this problem. If Pros: No API breaking/fast I also tried the following solutions:
Fixes #112350. Full diff: https://github.com/llvm/llvm-project/pull/112390.diff 2 Files Affected:
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index c71d17011d7a0d..a3c4eee7d78a43 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -8526,14 +8526,26 @@ bool llvm::isKnownInversion(const Value *X, const Value *Y) {
!match(Y, m_c_ICmp(Pred2, m_Specific(A), m_Value(C))))
return false;
+ // If they are only differ in predicate. They must both have samesign flag or
+ // not.
if (B == C)
- return Pred1 == ICmpInst::getInversePredicate(Pred2);
+ return Pred1 == ICmpInst::getInversePredicate(Pred2) &&
+ (!cast<ICmpInst>(X)->hasSameSign() ||
+ cast<ICmpInst>(Y)->hasSameSign());
+ ;
// Try to infer the relationship from constant ranges.
const APInt *RHSC1, *RHSC2;
if (!match(B, m_APInt(RHSC1)) || !match(C, m_APInt(RHSC2)))
return false;
+ // They must both have samesign flag or not. And sign bits of two RHSCs should
+ // match.
+ if (cast<ICmpInst>(X)->hasSameSign() &&
+ (!cast<ICmpInst>(Y)->hasSameSign() ||
+ RHSC1->isNonNegative() != RHSC2->isNonNegative()))
+ return false;
+
const auto CR1 = ConstantRange::makeExactICmpRegion(Pred1, *RHSC1);
const auto CR2 = ConstantRange::makeExactICmpRegion(Pred2, *RHSC2);
diff --git a/llvm/test/Transforms/InstCombine/select-cmp.ll b/llvm/test/Transforms/InstCombine/select-cmp.ll
index 234815949d77d4..a2eabf1ede1994 100644
--- a/llvm/test/Transforms/InstCombine/select-cmp.ll
+++ b/llvm/test/Transforms/InstCombine/select-cmp.ll
@@ -480,6 +480,93 @@ define i1 @test_select_inverse_nonconst4(i64 %x, i64 %y, i64 %z, i1 %cond) {
ret i1 %sel
}
+define i1 @test_select_inverse_samesign_true_arm(i64 %x, i64 %y, i1 %cond) {
+; CHECK-LABEL: @test_select_inverse_samesign_true_arm(
+; CHECK-NEXT: [[CMP2:%.*]] = icmp uge i64 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[SEL:%.*]] = xor i1 [[COND:%.*]], [[CMP2]]
+; CHECK-NEXT: ret i1 [[SEL]]
+;
+ %cmp1 = icmp samesign ult i64 %x, %y
+ %cmp2 = icmp uge i64 %x, %y
+ %sel = select i1 %cond, i1 %cmp1, i1 %cmp2
+ ret i1 %sel
+}
+
+define i1 @test_select_inverse_samesign_false_arm(i64 %x, i64 %y, i1 %cond) {
+; CHECK-LABEL: @test_select_inverse_samesign_false_arm(
+; CHECK-NEXT: [[CMP1:%.*]] = icmp ult i64 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[CMP2:%.*]] = icmp samesign uge i64 [[X]], [[Y]]
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND:%.*]], i1 [[CMP1]], i1 [[CMP2]]
+; CHECK-NEXT: ret i1 [[SEL]]
+;
+ %cmp1 = icmp ult i64 %x, %y
+ %cmp2 = icmp samesign uge i64 %x, %y
+ %sel = select i1 %cond, i1 %cmp1, i1 %cmp2
+ ret i1 %sel
+}
+
+define i1 @test_select_inverse_samesign_both(i64 %x, i64 %y, i1 %cond) {
+; CHECK-LABEL: @test_select_inverse_samesign_both(
+; CHECK-NEXT: [[CMP2:%.*]] = icmp samesign uge i64 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[SEL:%.*]] = xor i1 [[COND:%.*]], [[CMP2]]
+; CHECK-NEXT: ret i1 [[SEL]]
+;
+ %cmp1 = icmp samesign ult i64 %x, %y
+ %cmp2 = icmp samesign uge i64 %x, %y
+ %sel = select i1 %cond, i1 %cmp1, i1 %cmp2
+ ret i1 %sel
+}
+
+define i1 @test_select_inverse_samesign_false_arm_rhsc_same_sign(i64 %x, i64 %y, i1 %cond) {
+; CHECK-LABEL: @test_select_inverse_samesign_false_arm_rhsc_same_sign(
+; CHECK-NEXT: [[CMP1:%.*]] = icmp ult i64 [[X:%.*]], 11
+; CHECK-NEXT: [[CMP2:%.*]] = icmp samesign ugt i64 [[X]], 10
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND:%.*]], i1 [[CMP1]], i1 [[CMP2]]
+; CHECK-NEXT: ret i1 [[SEL]]
+;
+ %cmp1 = icmp ult i64 %x, 11
+ %cmp2 = icmp samesign ugt i64 %x, 10
+ %sel = select i1 %cond, i1 %cmp1, i1 %cmp2
+ ret i1 %sel
+}
+
+define i1 @test_select_inverse_samesign_true_arm_rhsc_same_sign(i64 %x, i64 %y, i1 %cond) {
+; CHECK-LABEL: @test_select_inverse_samesign_true_arm_rhsc_same_sign(
+; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i64 [[X:%.*]], 10
+; CHECK-NEXT: [[SEL:%.*]] = xor i1 [[COND:%.*]], [[CMP2]]
+; CHECK-NEXT: ret i1 [[SEL]]
+;
+ %cmp1 = icmp samesign ult i64 %x, 11
+ %cmp2 = icmp ugt i64 %x, 10
+ %sel = select i1 %cond, i1 %cmp1, i1 %cmp2
+ ret i1 %sel
+}
+
+define i1 @test_select_inverse_samesign_both_rhsc_same_sign(i64 %x, i64 %y, i1 %cond) {
+; CHECK-LABEL: @test_select_inverse_samesign_both_rhsc_same_sign(
+; CHECK-NEXT: [[CMP2:%.*]] = icmp samesign ugt i64 [[X:%.*]], 10
+; CHECK-NEXT: [[SEL:%.*]] = xor i1 [[COND:%.*]], [[CMP2]]
+; CHECK-NEXT: ret i1 [[SEL]]
+;
+ %cmp1 = icmp samesign ult i64 %x, 11
+ %cmp2 = icmp samesign ugt i64 %x, 10
+ %sel = select i1 %cond, i1 %cmp1, i1 %cmp2
+ ret i1 %sel
+}
+
+define i1 @test_select_inverse_samesign_both_rhsc_diff_sign(i64 %x, i64 %y, i1 %cond) {
+; CHECK-LABEL: @test_select_inverse_samesign_both_rhsc_diff_sign(
+; CHECK-NEXT: [[CMP1:%.*]] = icmp samesign slt i64 [[X:%.*]], 0
+; CHECK-NEXT: [[CMP2:%.*]] = icmp samesign sgt i64 [[X]], -1
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND:%.*]], i1 [[CMP1]], i1 [[CMP2]]
+; CHECK-NEXT: ret i1 [[SEL]]
+;
+ %cmp1 = icmp samesign slt i64 %x, 0
+ %cmp2 = icmp samesign sgt i64 %x, -1
+ %sel = select i1 %cond, i1 %cmp1, i1 %cmp2
+ ret i1 %sel
+}
+
define i1 @sel_icmp_two_cmp(i1 %c, i32 %a1, i32 %a2, i32 %a3, i32 %a4) {
; CHECK-LABEL: @sel_icmp_two_cmp(
; CHECK-NEXT: [[CMP1:%.*]] = icmp ule i32 [[A1:%.*]], [[A2:%.*]]
|
Think this definetly makes sense in the case where both operands are non-constants. Its highly unlikely any analysis will add In the constant RHS case if the constants differ I could image a case where we apply |
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.
Alternative solution 3: Drop all poison-generating flags in InstCombine if
FalseVal
is an ICmp/Instruction
- Pros: simple/fast
- Cons: instruction-dependent/hard to extend
Out of curiosity, how come is it a cons if it happens to be instruction-dependant? It doesn't seem to be necessarily so to me? Current solution looks fine to me.
If we handle some complex patterns in |
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
In #93591 we introduced
isKnownInversion
and assumesX
is poison impliesY
is poison because they share common operands. But after introducingsamesign
this assumption no longer hold ifX
is an icmp hassamesign
flag.Alive2 link: https://alive2.llvm.org/ce/z/rj3EwQ (Please run it locally with this patch and AliveToolkit/alive2#1098).
This approach is the most conservative way in my mind to address this problem. If
X
hassamesign
flag, it will check ifY
also has this flag and make sure constant RHS operands have the same sign.Pros: No API breaking/fast
Cons: less optimization opportunities (But I believe such cases don't exist in real-world code)
I also tried the following solutions:
Alternative solution 1: Check
impliesPoison
explicitly and insertfreeze
on demandAlternative solution 2: Add an optional
DropFlags
parameter and drop flags in InstCombineAlternative solution 3: Drop all poison-generating flags in InstCombine if
FalseVal
is an ICmp/InstructionFixes #112350.