Bug type: miscompilation
Component: ValueTracking
Bug location: llvm/lib/Analysis/ValueTracking.cpp: llvm::isKnownInversion
Issue title: [InstCombine] `samesign` flag should be dropped
Labels: miscompilation
llvm:analysis
Reported by: dtcxzyw

Reproducer:
```
define i1 @src(i64 %x, i64 %y, i1 %cond) {
  %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 @tgt(i64 %x, i64 %y, i1 %cond) {
  %cmp2 = icmp samesign uge i64 %x, %y
  %sel = xor i1 %cond, %cmp2
  ret i1 %sel
}
```


Comments: author: antoniofrighetto
body: Don't we miss checking the flag in `hasPoisonGeneratingFlags`?
author: dtcxzyw
body: > Don't we miss checking the flag in `hasPoisonGeneratingFlags`?

Yeah. I am preparing a patch.

author: dtcxzyw
body: But adding support in `hasPoisonGeneratingFlags` doesn't address this issue :(

author: antoniofrighetto
body: > But adding support in `hasPoisonGeneratingFlags` doesn't address this issue :(

I assume we may need to `dropPoisonGeneratingFlags` in InstCombineCompares, where needed?

Fix patch:
commit aad3a1630e385a6834f92a5f1d6045451ba21e4e
Author: Yingwei Zheng <dtcxzyw2333@gmail.com>
Date:   Thu Oct 17 00:27:21 2024 +0800

    [ValueTracking] Respect `samesign` flag in `isKnownInversion` (#112390)
    
    In https://github.com/llvm/llvm-project/pull/93591 we introduced
    `isKnownInversion` and assumes `X` is poison implies `Y` is poison
    because they share common operands. But after introducing `samesign`
    this assumption no longer hold if `X` is an icmp has `samesign` flag.
    
    Alive2 link: https://alive2.llvm.org/ce/z/rj3EwQ (Please run it locally
    with this patch and https://github.com/AliveToolkit/alive2/pull/1098).
    
    This approach is the most conservative way in my mind to address this
    problem. If `X` has `samesign` flag, it will check if `Y` also has this
    flag and make sure constant RHS operands have the same sign.
    
    Fixes https://github.com/llvm/llvm-project/issues/112350.

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index eb8d17044a17..e9ed8b3c862b 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -8522,6 +8522,10 @@ bool llvm::isKnownInversion(const Value *X, const Value *Y) {
       !match(Y, m_c_ICmp(Pred2, m_Specific(A), m_Value(C))))
     return false;
 
+  // They must both have samesign flag or not.
+  if (cast<ICmpInst>(X)->hasSameSign() != cast<ICmpInst>(Y)->hasSameSign())
+    return false;
+
   if (B == C)
     return Pred1 == ICmpInst::getInversePredicate(Pred2);
 
@@ -8530,6 +8534,11 @@ bool llvm::isKnownInversion(const Value *X, const Value *Y) {
   if (!match(B, m_APInt(RHSC1)) || !match(C, m_APInt(RHSC2)))
     return false;
 
+  // Sign bits of two RHSCs should match.
+  if (cast<ICmpInst>(X)->hasSameSign() &&
+      RHSC1->isNonNegative() != RHSC2->isNonNegative())
+    return false;
+
   const auto CR1 = ConstantRange::makeExactICmpRegion(Pred1, *RHSC1);
   const auto CR2 = ConstantRange::makeExactICmpRegion(Pred2, *RHSC2);
 


Regression tests:
file: llvm/test/Transforms/InstCombine/select-cmp.ll
commands: opt < %s -passes=instcombine -S
tests: test_name: test_select_inverse_nonconst4
test_body: define i1 @test_select_inverse_nonconst4(i64 %x, i64 %y, i64 %z, i1 %cond) {
  %cmp1 = icmp ult i64 %x, %y
  %cmp2 = icmp uge i64 %z, %y
  %sel = select i1 %cond, i1 %cmp1, i1 %cmp2
  ret i1 %sel
}

test_name: test_select_inverse_samesign_both
test_body: define i1 @test_select_inverse_samesign_both(i64 %x, i64 %y, i1 %cond) {
  %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
}

test_name: test_select_inverse_samesign_false_arm_rhsc_same_sign
test_body: define i1 @test_select_inverse_samesign_false_arm_rhsc_same_sign(i64 %x, i64 %y, i1 %cond) {
  %cmp1 = icmp ult i64 %x, 11
  %cmp2 = icmp samesign ugt i64 %x, 10
  %sel = select i1 %cond, i1 %cmp1, i1 %cmp2
  ret i1 %sel
}

test_name: test_select_inverse_samesign_true_arm
test_body: define i1 @test_select_inverse_samesign_true_arm(i64 %x, i64 %y, i1 %cond) {
  %cmp1 = icmp samesign ult i64 %x, %y
  %cmp2 = icmp uge i64 %x, %y
  %sel = select i1 %cond, i1 %cmp1, i1 %cmp2
  ret i1 %sel
}

test_name: test_select_inverse_samesign_true_arm_rhsc_same_sign
test_body: define i1 @test_select_inverse_samesign_true_arm_rhsc_same_sign(i64 %x, i64 %y, i1 %cond) {
  %cmp1 = icmp samesign ult i64 %x, 11
  %cmp2 = icmp ugt i64 %x, 10
  %sel = select i1 %cond, i1 %cmp1, i1 %cmp2
  ret i1 %sel
}

test_name: test_select_inverse_samesign_both_rhsc_diff_sign
test_body: define i1 @test_select_inverse_samesign_both_rhsc_diff_sign(i64 %x, i64 %y, i1 %cond) {
  %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
}

test_name: test_select_inverse_samesign_both_rhsc_same_sign
test_body: define i1 @test_select_inverse_samesign_both_rhsc_same_sign(i64 %x, i64 %y, i1 %cond) {
  %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
}

test_name: test_select_inverse_samesign_false_arm
test_body: define i1 @test_select_inverse_samesign_false_arm(i64 %x, i64 %y, i1 %cond) {
  %cmp1 = icmp ult i64 %x, %y
  %cmp2 = icmp samesign uge i64 %x, %y
  %sel = select i1 %cond, i1 %cmp1, i1 %cmp2
  ret i1 %sel
}

test_name: sel_icmp_two_cmp
test_body: define i1 @sel_icmp_two_cmp(i1 %c, i32 %a1, i32 %a2, i32 %a3, i32 %a4) {
  %v1 = call i8 @llvm.ucmp.i8.i32(i32 %a1, i32 %a2)
  %v2 = call i8 @llvm.scmp.i8.i32(i32 %a3, i32 %a4)
  %sel = select i1 %c, i8 %v1, i8 %v2
  %cmp = icmp sle i8 %sel, 0
  ret i1 %cmp
}

; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i8 @llvm.scmp.i8.i32(i32, i32) #0

; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i8 @llvm.ucmp.i8.i32(i32, i32) #0

attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }