Bug type: miscompilation
Component: InstCombine
Bug location: llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp: InstCombinerImpl::foldICmpAndConstConst
InstCombinerImpl::foldICmpAndShift
llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp: InstCombinerImpl::foldSelectEqualityTest
Issue title: [InstCombine] `samesign` flag should be dropped in `InstCombinerImpl::foldICmpAndConstConst`
Labels: miscompilation
llvm:instcombine
Reported by: dtcxzyw

Reproducer:
```
define i1 @src(i32 %x, i32 %y) {
  %shf = lshr i32 %x, %y
  %or = or i32 %shf, %x
  %and = and i32 %or, 1
  %ret = icmp samesign ne i32 %and, 0
  ret i1 %ret
}

define i1 @tgt(i32 %x, i32 %y) {
  %shf1 = shl nuw i32 1, %y
  %or2 = or i32 %shf1, 1
  %and3 = and i32 %x, %or2
  %ret = icmp samesign ne i32 %and3, 0
  ret i1 %ret
}
```
```
Transformation doesn't verify!

ERROR: Target is more poisonous than source

Example:
i32 %x = #x80000000 (2147483648, -2147483648)
i32 %y = #x0000001f (31)

Source:
i32 %shf = #x00000001 (1)
i32 %or = #x80000001 (2147483649, -2147483647)
i32 %and = #x00000001 (1)
i1 %ret = #x1 (1)

Target:
i32 %shf1 = #x80000000 (2147483648, -2147483648)
i32 %or2 = #x80000001 (2147483649, -2147483647)
i32 %and3 = #x80000000 (2147483648, -2147483648)
i1 %ret = poison
Source value: #x1 (1)
Target value: poison
```



Fix patch:
commit 09361953116770b646decf5820a9455ada2ba4fc
Author: Yingwei Zheng <dtcxzyw2333@gmail.com>
Date:   Wed Oct 16 19:13:52 2024 +0800

    [InstCombine] Drop `samesign` in InstCombine (#112480)
    
    Closes https://github.com/llvm/llvm-project/issues/112476.

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 7129499e0f8f..18a6fdcec172 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -1738,7 +1738,7 @@ Instruction *InstCombinerImpl::foldICmpAndShift(ICmpInst &Cmp,
 
     // Compute X & (C2 << Y).
     Value *NewAnd = Builder.CreateAnd(Shift->getOperand(0), NewShift);
-    return replaceOperand(Cmp, 0, NewAnd);
+    return new ICmpInst(Cmp.getPredicate(), NewAnd, Cmp.getOperand(1));
   }
 
   return nullptr;
@@ -1844,7 +1844,7 @@ Instruction *InstCombinerImpl::foldICmpAndConstConst(ICmpInst &Cmp,
                                                /*HasNUW=*/true),
                              One, Or->getName());
         Value *NewAnd = Builder.CreateAnd(A, NewOr, And->getName());
-        return replaceOperand(Cmp, 0, NewAnd);
+        return new ICmpInst(Cmp.getPredicate(), NewAnd, Cmp.getOperand(1));
       }
     }
   }
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index 8be2eeed84ad..623694663aa1 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -1448,6 +1448,7 @@ Instruction *InstCombinerImpl::foldSelectEqualityTest(SelectInst &Sel) {
              m_c_SpecificICmp(ICmpInst::ICMP_EQ, m_Specific(X), m_Specific(Y))))
     return nullptr;
 
+  cast<ICmpInst>(XeqY)->setSameSign(false);
   return replaceInstUsesWith(Sel, XeqY);
 }
 


Regression tests:
file: llvm/test/Transforms/InstCombine/icmp-and-shift.ll
commands: opt < %s -passes=instcombine -S
tests: test_name: test_shr_and_1_ne_0
test_body: define i1 @test_shr_and_1_ne_0(i32 %a, i32 %b) {
  %shr = lshr i32 %a, %b
  %and = and i32 %shr, 1
  %cmp = icmp ne i32 %and, 0
  ret i1 %cmp
}

file: llvm/test/Transforms/InstCombine/icmp-equality-test.ll
commands: opt < %s -passes=instcombine -S
tests: test_name: icmp_equality_test_swift_optional_pointers
test_body: define i1 @icmp_equality_test_swift_optional_pointers(i64 %X, i64 %Y) {
entry:
  %XeqC = icmp eq i64 %X, 0
  %YeqC = icmp eq i64 %Y, 0
  %either = select i1 %XeqC, i1 true, i1 %YeqC
  %both = select i1 %XeqC, i1 %YeqC, i1 false
  %XeqY = icmp eq i64 %X, %Y
  %equal = select i1 %either, i1 %both, i1 %XeqY
  ret i1 %equal
}

test_name: icmp_equality_test_wrong_equal
test_body: define i1 @icmp_equality_test_wrong_equal(i64 %X, i64 %Y, i64 %Z) {
entry:
  %XeqZ = icmp eq i64 %X, %Z
  %YeqZ = icmp eq i64 %Y, %Z
  %XeqY = icmp eq i64 %X, %Y
  %not.YeqZ = xor i1 %YeqZ, true
  %and = select i1 %not.YeqZ, i1 %XeqY, i1 false
  %equal = select i1 %XeqZ, i1 %and, i1 %YeqZ
  ret i1 %equal
}

test_name: icmp_equality_test_commute_icmp1
test_body: define i1 @icmp_equality_test_commute_icmp1(i64 %X, i64 %Y, i64 %Z) {
entry:
  %XeqZ = icmp eq i64 %Z, %X
  %YeqZ = icmp eq i64 %Z, %Y
  %XeqY = icmp eq i64 %Y, %X
  %not.YeqZ = xor i1 %YeqZ, true
  %and = select i1 %not.YeqZ, i1 %XeqY, i1 false
  %equal = select i1 %XeqZ, i1 %YeqZ, i1 %and
  ret i1 %equal
}

test_name: icmp_equality_test_wrong_cmp
test_body: define i1 @icmp_equality_test_wrong_cmp(i64 %X, i64 %Y, i64 %Z) {
entry:
  %XeqZ = icmp eq i64 %X, %Z
  %YeqZ = icmp eq i64 %Y, %Z
  %XeqY = icmp eq i64 %X, 999
  %not.YeqZ = xor i1 %YeqZ, true
  %and = select i1 %not.YeqZ, i1 %XeqY, i1 false
  %equal = select i1 %XeqZ, i1 %YeqZ, i1 %and
  ret i1 %equal
}

test_name: icmp_equality_test_wrong_constant
test_body: define i1 @icmp_equality_test_wrong_constant(i64 %X, i64 %Y) {
entry:
  %XeqC = icmp eq i64 %X, 0
  %YeqC = icmp eq i64 %Y, 999
  %XeqY = icmp eq i64 %X, %Y
  %not.YeqC = xor i1 %YeqC, true
  %and = select i1 %not.YeqC, i1 %XeqY, i1 false
  %equal = select i1 %XeqC, i1 %YeqC, i1 %and
  ret i1 %equal
}

test_name: icmp_equality_test_vector
test_body: define <2 x i1> @icmp_equality_test_vector(<2 x i64> %X, <2 x i64> %Y) {
entry:
  %XeqC = icmp eq <2 x i64> %X, <i64 123, i64 456>
  %YeqC = icmp eq <2 x i64> %Y, <i64 123, i64 456>
  %XeqY = icmp eq <2 x i64> %X, %Y
  %not.YeqC = xor <2 x i1> %YeqC, splat (i1 true)
  %and = select <2 x i1> %not.YeqC, <2 x i1> %XeqY, <2 x i1> zeroinitializer
  %equal = select <2 x i1> %XeqC, <2 x i1> %YeqC, <2 x i1> %and
  ret <2 x i1> %equal
}

test_name: icmp_equality_test_commute_select2
test_body: define i1 @icmp_equality_test_commute_select2(i64 %X, i64 %Y, i64 %Z) {
entry:
  %XeqZ = icmp eq i64 %X, %Z
  %YeqZ = icmp eq i64 %Y, %Z
  %XeqY = icmp eq i64 %X, %Y
  %not.XeqZ = xor i1 %XeqZ, true
  %and = select i1 %YeqZ, i1 false, i1 %XeqY
  %equal = select i1 %not.XeqZ, i1 %and, i1 %YeqZ
  ret i1 %equal
}

test_name: icmp_equality_test_constant
test_body: define i1 @icmp_equality_test_constant(i42 %X, i42 %Y) {
entry:
  %XeqC = icmp eq i42 %X, -42
  %YeqC = icmp eq i42 %Y, -42
  %XeqY = icmp eq i42 %X, %Y
  %not.YeqC = xor i1 %YeqC, true
  %and = select i1 %not.YeqC, i1 %XeqY, i1 false
  %equal = select i1 %XeqC, i1 %YeqC, i1 %and
  ret i1 %equal
}

test_name: icmp_equality_test_wrong_and
test_body: define i1 @icmp_equality_test_wrong_and(i64 %X, i64 %Y, i64 %Z) {
entry:
  %XeqZ = icmp eq i64 %X, %Z
  %YeqZ = icmp eq i64 %Y, %Z
  %XeqY = icmp eq i64 %X, %Y
  %not.YeqZ = xor i1 %YeqZ, true
  %and = select i1 %not.YeqZ, i1 false, i1 %XeqY
  %equal = select i1 %XeqZ, i1 %YeqZ, i1 %and
  ret i1 %equal
}

test_name: icmp_equality_test
test_body: define i1 @icmp_equality_test(i64 %X, i64 %Y, i64 %Z) {
entry:
  %XeqZ = icmp eq i64 %X, %Z
  %YeqZ = icmp eq i64 %Y, %Z
  %XeqY = icmp eq i64 %X, %Y
  %not.YeqZ = xor i1 %YeqZ, true
  %and = select i1 %not.YeqZ, i1 %XeqY, i1 false
  %equal = select i1 %XeqZ, i1 %YeqZ, i1 %and
  ret i1 %equal
}

test_name: icmp_equality_test_missing_not
test_body: define i1 @icmp_equality_test_missing_not(i64 %X, i64 %Y, i64 %Z) {
  %XeqZ = icmp eq i64 %X, %Z
  %YeqZ = icmp eq i64 %Y, %Z
  %XeqY = icmp eq i64 %X, %Y
  %and = select i1 %YeqZ, i1 %XeqY, i1 false
  %equal = select i1 %XeqZ, i1 %YeqZ, i1 %and
  ret i1 %equal
}

test_name: icmp_equality_test_constant_samesign
test_body: define i1 @icmp_equality_test_constant_samesign(i42 %X, i42 %Y) {
entry:
  %XeqC = icmp eq i42 %X, -42
  %YeqC = icmp eq i42 %Y, -42
  %XeqY = icmp samesign eq i42 %X, %Y
  %not.YeqC = xor i1 %YeqC, true
  %and = select i1 %not.YeqC, i1 %XeqY, i1 false
  %equal = select i1 %XeqC, i1 %YeqC, i1 %and
  ret i1 %equal
}

test_name: icmp_equality_test_commute_icmp2
test_body: define i1 @icmp_equality_test_commute_icmp2(i64 %X, i64 %Y, i64 %Z) {
  %XeqZ = icmp eq i64 %Z, %X
  %YeqZ = icmp eq i64 %Y, %Z
  %XeqY = icmp eq i64 %Y, %X
  %not.YeqZ = xor i1 %YeqZ, true
  %and = select i1 %not.YeqZ, i1 %XeqY, i1 false
  %equal = select i1 %XeqZ, i1 %YeqZ, i1 %and
  ret i1 %equal
}

test_name: icmp_equality_test_commute_select1
test_body: define i1 @icmp_equality_test_commute_select1(i64 %X, i64 %Y, i64 %Z) {
entry:
  %XeqZ = icmp eq i64 %X, %Z
  %YeqZ = icmp eq i64 %Y, %Z
  %XeqY = icmp eq i64 %X, %Y
  %and = select i1 %YeqZ, i1 false, i1 %XeqY
  %equal = select i1 %XeqZ, i1 %YeqZ, i1 %and
  ret i1 %equal
}

file: llvm/test/Transforms/InstCombine/icmp.ll
commands: opt < %s -passes=instcombine -S
tests: test_name: icmp_and_or_lshr
test_body: target datalayout = "e-p:64:64:64-p1:16:16:16-p2:32:32:32-p3:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"

define i1 @icmp_and_or_lshr(i32 %x, i32 %y) {
  %shf = lshr i32 %x, %y
  %or = or i32 %shf, %x
  %and = and i32 %or, 1
  %ret = icmp ne i32 %and, 0
  ret i1 %ret
}