Bug type: miscompilation
Component: InstCombine
Bug location: llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp: InstCombinerImpl::SimplifyDemandedVectorElts
Issue title: [InstCombine] Replacement of binop with binop-of-shuffle incorrectly adds extra poison value
Labels: miscompilation
llvm:instcombine
Reported by: Benjins

The following IR is a minimal repro when run with `opt -passes=instcombine`:
```llvm
define i8 @src(<2 x i8> %x, <2 x i8> %y) {
  %xsplat = shufflevector <2 x i8> %x, <2 x i8> poison, <2 x i32> <i32 1, i32 0>
  %vv = mul <2 x i8> %xsplat, %y
  %m = mul <2 x i8> %x, %y   ;  <<<< Note that %m is later replaced with %vv in the output
  %msplat = shufflevector <2 x i8> %m, <2 x i8> poison, <2 x i32> <i32 0, i32 0>
  %res = add <2 x i8> %vv, %msplat
  %vget_lane = extractelement <2 x i8> %res, i64 1
  ret i8 %vget_lane
}
```
Which on 17.x or trunk will give:
```llvm
define i8 @src(<2 x i8> %x, <2 x i8> %y) {
  %xsplat = shufflevector <2 x i8> %x, <2 x i8> poison, <2 x i32> <i32 poison, i32 0>
  %vv = mul <2 x i8> %xsplat, %y
  %msplat = shufflevector <2 x i8> %vv, <2 x i8> poison, <2 x i32> <i32 poison, i32 0>
  %res = add <2 x i8> %vv, %msplat
  %vget_lane = extractelement <2 x i8> %res, i64 1
  ret i8 %vget_lane
}
```
However, this is not correct, because the added poisoned values end up observed in the final output. First, in InstCombine, the first element in each mask for the shuffles are first replaced with `poison` due to not being observed in the final `extractelement`. After that we run into this code  in `InstCombineSimplifyDemanded.cpp`:
https://github.com/llvm/llvm-project/blob/d8278b682386f51dfba204849c624672a3df40c7/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp#L1866-L1880

This is what replaces the `mul <2 x i8> %x, %y` with `mul <2 x i8> %xsplat, %y` (`m_ZeroMask` also matches poison). However in doing so, it introduces the extra poison from `%xsplat`, meaning that `%msplat` has both its elements poisoned, which ends up propagating to `%vget_lane`.

Here is a Godbolt link showing the test case on 16.x, 17.x, and trunk:
https://godbolt.org/z/5eeT7x4s3

And also an Alive2 example showing the bad transformation with a counter-example:
https://alive2.llvm.org/ce/z/RpBJEN

The code was first introduced in 3b090ff2bdb2828b53d6fec9c06b9d52fed09f42, which a bisect shows is when this first started repro'ing

I have verified that this still repros on latest trunk, 2de269a641e4ffbb7a44e559c4c0a91bb66df823

For priority/triage: this bug was found by a fuzzer meant to test SIMD codegen. It was not from manually written code

Comments: author: Benjins
body: A slightly smaller test case:
https://alive2.llvm.org/ce/z/6AMPWC
```llvm
define <2 x i8> @src(<2 x i8> %x, <2 x i8> %y) {
  %xsplat = shufflevector <2 x i8> %x, <2 x i8> poison, <2 x i32> <i32 poison, i32 0>
  %vv = mul <2 x i8> %xsplat, %y
  %m = mul <2 x i8> %x, %y
  %msplat = shufflevector <2 x i8> %m, <2 x i8> poison, <2 x i32> <i32 poison, i32 0>
  %res = add <2 x i8> %vv, %msplat
  ret <2 x i8> %res
}
```
This doesn't extract the vector, and instead manually specifies the poisonous elements in the shuffles. As Alive notes, the output for `src(<0, 0>, <0, 0>)` goes from `<poison, 0>` to `<poison, poison>`

Fix patch:
commit d2a26a7bd5fc7cc5752337b7f4f999642feb37dc
Author: Antonio Frighetto <me@antoniofrighetto.com>
Date:   Sun Feb 18 18:10:41 2024 +0100

    [InstCombine] Do not perform binop-of-shuffle when mask is poison
    
    A miscompilation issue has been addressed with refined checking.
    Shuffle masks operand may be turned into `poison` if this does
    not lead to observable changes. This however may not guarantee
    binop to binop-of-shuffle replacement to be sound anymore.
    
    Fixes: https://github.com/llvm/llvm-project/issues/82052.

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
index 5f13454089e5..97ae980a7cba 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
@@ -1870,14 +1870,16 @@ Value *InstCombinerImpl::SimplifyDemandedVectorElts(Value *V,
         Value *ShufOp = MatchShufAsOp0 ? X : Y;
         Value *OtherOp = MatchShufAsOp0 ? Y : X;
         for (User *U : OtherOp->users()) {
-          auto Shuf = m_Shuffle(m_Specific(ShufOp), m_Value(), m_ZeroMask());
+          ArrayRef<int> Mask;
+          auto Shuf = m_Shuffle(m_Specific(ShufOp), m_Value(), m_Mask(Mask));
           if (BO->isCommutative()
                   ? match(U, m_c_BinOp(Opcode, Shuf, m_Specific(OtherOp)))
                   : MatchShufAsOp0
                         ? match(U, m_BinOp(Opcode, Shuf, m_Specific(OtherOp)))
                         : match(U, m_BinOp(Opcode, m_Specific(OtherOp), Shuf)))
-            if (DT.dominates(U, I))
-              return U;
+            if (match(Mask, m_ZeroMask()) && Mask[0] != PoisonMaskElem)
+              if (DT.dominates(U, I))
+                return U;
         }
         return nullptr;
       };


Regression tests:
file: llvm/test/Transforms/InstCombine/vec_demanded_elts.ll
commands: opt < %s -passes=instcombine -S
tests: test_name: common_binop_demand_via_extelt_op0_mismatch_elt1
test_body: target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"

declare void @use(<2 x i4>)

define i4 @common_binop_demand_via_extelt_op0_mismatch_elt1(<2 x i4> %x, <2 x i4> %y) {
  %xshuf = shufflevector <2 x i4> %x, <2 x i4> poison, <2 x i32> zeroinitializer
  %b_xshuf_y = sub <2 x i4> %xshuf, %y
  %b_xy = sub nsw <2 x i4> %x, %y
  %b_xy0 = extractelement <2 x i4> %b_xy, i32 1
  call void @use(<2 x i4> %b_xshuf_y)
  ret i4 %b_xy0
}

test_name: common_binop_demand_via_splat_mask_poison
test_body: target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"

define <2 x i8> @common_binop_demand_via_splat_mask_poison(<2 x i8> %x, <2 x i8> %y) {
  %ysplat = shufflevector <2 x i8> %y, <2 x i8> poison, <2 x i32> <i32 0, i32 poison>
  %vv = add <2 x i8> %x, %ysplat
  %m = add <2 x i8> %x, %y
  %msplat = shufflevector <2 x i8> %m, <2 x i8> poison, <2 x i32> zeroinitializer
  %res = add <2 x i8> %vv, %msplat
  ret <2 x i8> %res
}

test_name: common_binop_demand_via_splat_mask_poison_2
test_body: target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"

define <2 x i8> @common_binop_demand_via_splat_mask_poison_2(<2 x i8> %x, <2 x i8> %y) {
  %ysplat = shufflevector <2 x i8> %y, <2 x i8> poison, <2 x i32> <i32 poison, i32 0>
  %vv = add <2 x i8> %x, %ysplat
  %m = add <2 x i8> %x, %y
  %msplat = shufflevector <2 x i8> %m, <2 x i8> %y, <2 x i32> <i32 0, i32 2>
  %res = add <2 x i8> %vv, %msplat
  ret <2 x i8> %res
}

test_name: common_binop_demand_via_splat_mask_poison_3
test_body: target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"

define <2 x i8> @common_binop_demand_via_splat_mask_poison_3(<2 x i8> %x, <2 x i8> %y) {
  %ysplat = shufflevector <2 x i8> %y, <2 x i8> poison, <2 x i32> <i32 poison, i32 0>
  %vv = add <2 x i8> %x, %ysplat
  %m = add <2 x i8> %x, %y
  %msplat = shufflevector <2 x i8> %m, <2 x i8> poison, <2 x i32> zeroinitializer
  %res = add <2 x i8> %vv, %msplat
  ret <2 x i8> %res
}