Bug type: miscompilation
Component: IR
InstCombine
InstructionSimplify
Bug location: llvm/lib/Analysis/InstructionSimplify.cpp: simplifyDivRem
llvm/lib/IR/ConstantFold.cpp: llvm::ConstantFoldBinaryInstruction
llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp: InstCombinerImpl::commonIDivTransforms
InstCombinerImpl::commonIRemTransforms
foldIDivShl
Issue title: [InstCombine] miscompilation of `or` of vectors
Labels: miscompilation
llvm:instcombine
llvm:analysis
Reported by: bongjunj

Alive2 report: https://alive2.llvm.org/ce/z/LyM8sY

```llvm
----------------------------------------
define <2 x i32> @or_sext_to_sel_constant_vec.3(<2 x i1> %y) {
#0:
  %sext = sext <2 x i1> %y to <2 x i32>
  %#1 = or <2 x i32> %sext, { 0, 1 }
  %#2 = udiv <2 x i32> { 42, 4294967289 }, %#1
  ret <2 x i32> %#2
}
=>
define <2 x i32> @or_sext_to_sel_constant_vec.3(<2 x i1> %y) {
#0:
  ret <2 x i32> { 0, 0 }
}
Transformation doesn't verify!

ERROR: Value mismatch

Example:
<2 x i1> %y = < #x1 (1), #x0 (0) >

Source:
<2 x i32> %sext = < #xffffffff (4294967295, -1), #x00000000 (0) >
<2 x i32> %#1 = < #xffffffff (4294967295, -1), #x00000001 (1) >
<2 x i32> %#2 = < #x00000000 (0), #xfffffff9 (4294967289, -7) >

Target:
Source value: < #x00000000 (0), #xfffffff9 (4294967289, -7) >
Target value: < #x00000000 (0), #x00000000 (0) >


----------------------------------------
define <2 x i32> @or_sext_to_sel_constant_vec.2(<2 x i1> %y) {
#0:
  %sext = sext <2 x i1> %y to <2 x i32>
  %#1 = or <2 x i32> %sext, { 0, 1 }
  %#2 = sdiv <2 x i32> { 42, 4294967289 }, %#1
  %#3 = udiv exact <2 x i32> %#2, %#2
  %#4 = or <2 x i32> %#2, %#2
  ret <2 x i32> %#4
}
=>
define <2 x i32> @or_sext_to_sel_constant_vec.2(<2 x i1> %y) {
#0:
  ret <2 x i32> { 4294967254, 7 }
}
Transformation doesn't verify!

ERROR: Value mismatch

Example:
<2 x i1> %y = < #x1 (1), #x0 (0) >

Source:
<2 x i32> %sext = < #xffffffff (4294967295, -1), #x00000000 (0) >
<2 x i32> %#1 = < #xffffffff (4294967295, -1), #x00000001 (1) >
<2 x i32> %#2 = < #xffffffd6 (4294967254, -42), #xfffffff9 (4294967289, -7) >
<2 x i32> %#3 = < #x00000001 (1), #x00000001 (1) >
<2 x i32> %#4 = < #xffffffd6 (4294967254, -42), #xfffffff9 (4294967289, -7) >

Target:
Source value: < #xffffffd6 (4294967254, -42), #xfffffff9 (4294967289, -7) >
Target value: < #xffffffd6 (4294967254, -42), #x00000007 (7) >

Summary:
  0 correct transformations
  2 incorrect transformations
  0 failed-to-prove transformations
  0 Alive2 errors
```

Comments: author: dtcxzyw
body: @bongjunj Feel free to ping me if you find new InstCombine miscompilation bugs without involving undef values/NaN payloads.
author: dtcxzyw
body: https://github.com/llvm/llvm-project/blob/eac2c182c6f852fc187af9952250a43d6fb17b28/llvm/lib/Analysis/InstructionSimplify.cpp#L1098-L1109

We cannot do this simplification when threading udiv over selects.
 

Fix patch:
commit a77dedcacb4c5eb221395b69877981dd6ad98989
Author: Yingwei Zheng <dtcxzyw2333@gmail.com>
Date:   Fri Nov 1 22:56:22 2024 +0800

    [InstSimplify][InstCombine][ConstantFold] Move vector div/rem by zero fold to InstCombine (#114280)
    
    Previously we fold `div/rem X, C` into `poison` if any element of the
    constant divisor `C` is zero or undef. However, it is incorrect when
    threading udiv over an vector select:
    https://alive2.llvm.org/ce/z/3Ninx5
    ```
    define <2 x i32> @vec_select_udiv_poison(<2 x i1> %x) {
      %sel = select <2 x i1> %x, <2 x i32> <i32 -1, i32 -1>, <2 x i32> <i32 0, i32 1>
      %div = udiv <2 x i32> <i32 42, i32 -7>, %sel
      ret <2 x i32> %div
    }
    ```
    In this case, `threadBinOpOverSelect` folds `udiv <i32 42, i32 -7>, <i32
    -1, i32 -1>` and `udiv <i32 42, i32 -7>, <i32 0, i32 1>` into
    `zeroinitializer` and `poison`, respectively. One solution is to
    introduce a new flag indicating that we are threading over a vector
    select. But it requires to modify both `InstSimplify` and
    `ConstantFold`.
    
    However, this optimization doesn't provide benefits to real-world
    programs:
    
    https://dtcxzyw.github.io/llvm-opt-benchmark/coverage/data/zyw/opt-ci/actions-runner/_work/llvm-opt-benchmark/llvm-opt-benchmark/llvm/llvm-project/llvm/lib/IR/ConstantFold.cpp.html#L908
    
    https://dtcxzyw.github.io/llvm-opt-benchmark/coverage/data/zyw/opt-ci/actions-runner/_work/llvm-opt-benchmark/llvm-opt-benchmark/llvm/llvm-project/llvm/lib/Analysis/InstructionSimplify.cpp.html#L1107
    
    This patch moves the fold into InstCombine to avoid breaking numerous
    existing tests.
    
    Fixes #114191 and #113866 (only poison-safety issue).

diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index d08be1e55c85..2cb2612bf611 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -1095,19 +1095,6 @@ static Value *simplifyDivRem(Instruction::BinaryOps Opcode, Value *Op0,
   if (match(Op1, m_Zero()))
     return PoisonValue::get(Ty);
 
-  // If any element of a constant divisor fixed width vector is zero or undef
-  // the behavior is undefined and we can fold the whole op to poison.
-  auto *Op1C = dyn_cast<Constant>(Op1);
-  auto *VTy = dyn_cast<FixedVectorType>(Ty);
-  if (Op1C && VTy) {
-    unsigned NumElts = VTy->getNumElements();
-    for (unsigned i = 0; i != NumElts; ++i) {
-      Constant *Elt = Op1C->getAggregateElement(i);
-      if (Elt && (Elt->isNullValue() || Q.isUndefValue(Elt)))
-        return PoisonValue::get(Ty);
-    }
-  }
-
   // poison / X -> poison
   // poison % X -> poison
   if (isa<PoisonValue>(Op0))
diff --git a/llvm/lib/IR/ConstantFold.cpp b/llvm/lib/IR/ConstantFold.cpp
index 07dfbc41e79b..c2780faee403 100644
--- a/llvm/lib/IR/ConstantFold.cpp
+++ b/llvm/lib/IR/ConstantFold.cpp
@@ -902,11 +902,6 @@ Constant *llvm::ConstantFoldBinaryInstruction(unsigned Opcode, Constant *C1,
         Constant *ExtractIdx = ConstantInt::get(Ty, i);
         Constant *LHS = ConstantExpr::getExtractElement(C1, ExtractIdx);
         Constant *RHS = ConstantExpr::getExtractElement(C2, ExtractIdx);
-
-        // If any element of a divisor vector is zero, the whole op is poison.
-        if (Instruction::isIntDivRem(Opcode) && RHS->isNullValue())
-          return PoisonValue::get(VTy);
-
         Constant *Res = ConstantExpr::isDesirableBinOp(Opcode)
                             ? ConstantExpr::get(Opcode, LHS, RHS)
                             : ConstantFoldBinaryInstruction(Opcode, LHS, RHS);
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
index 7a060cdab2d3..adbd9186c59c 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
+++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
@@ -102,6 +102,7 @@ public:
   Instruction *visitSRem(BinaryOperator &I);
   Instruction *visitFRem(BinaryOperator &I);
   bool simplifyDivRemOfSelectWithZeroOp(BinaryOperator &I);
+  Instruction *commonIDivRemTransforms(BinaryOperator &I);
   Instruction *commonIRemTransforms(BinaryOperator &I);
   Instruction *commonIDivTransforms(BinaryOperator &I);
   Instruction *visitUDiv(BinaryOperator &I);
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
index b9c165da906d..f85a3c936513 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
@@ -1158,29 +1158,39 @@ static Value *foldIDivShl(BinaryOperator &I, InstCombiner::BuilderTy &Builder) {
   return nullptr;
 }
 
-/// This function implements the transforms common to both integer division
-/// instructions (udiv and sdiv). It is called by the visitors to those integer
-/// division instructions.
-/// Common integer divide transforms
-Instruction *InstCombinerImpl::commonIDivTransforms(BinaryOperator &I) {
-  if (Instruction *Phi = foldBinopWithPhiOperands(I))
-    return Phi;
-
+/// Common integer divide/remainder transforms
+Instruction *InstCombinerImpl::commonIDivRemTransforms(BinaryOperator &I) {
+  assert(I.isIntDivRem() && "Unexpected instruction");
   Value *Op0 = I.getOperand(0), *Op1 = I.getOperand(1);
-  bool IsSigned = I.getOpcode() == Instruction::SDiv;
+
+  // If any element of a constant divisor fixed width vector is zero or undef
+  // the behavior is undefined and we can fold the whole op to poison.
+  auto *Op1C = dyn_cast<Constant>(Op1);
   Type *Ty = I.getType();
+  auto *VTy = dyn_cast<FixedVectorType>(Ty);
+  if (Op1C && VTy) {
+    unsigned NumElts = VTy->getNumElements();
+    for (unsigned i = 0; i != NumElts; ++i) {
+      Constant *Elt = Op1C->getAggregateElement(i);
+      if (Elt && (Elt->isNullValue() || isa<UndefValue>(Elt)))
+        return replaceInstUsesWith(I, PoisonValue::get(Ty));
+    }
+  }
+
+  if (Instruction *Phi = foldBinopWithPhiOperands(I))
+    return Phi;
 
   // The RHS is known non-zero.
   if (Value *V = simplifyValueKnownNonZero(I.getOperand(1), *this, I))
     return replaceOperand(I, 1, V);
 
-  // Handle cases involving: [su]div X, (select Cond, Y, Z)
-  // This does not apply for fdiv.
+  // Handle cases involving: div/rem X, (select Cond, Y, Z)
   if (simplifyDivRemOfSelectWithZeroOp(I))
     return &I;
 
   // If the divisor is a select-of-constants, try to constant fold all div ops:
-  // C / (select Cond, TrueC, FalseC) --> select Cond, (C / TrueC), (C / FalseC)
+  // C div/rem (select Cond, TrueC, FalseC) --> select Cond, (C div/rem TrueC),
+  // (C div/rem FalseC)
   // TODO: Adapt simplifyDivRemOfSelectWithZeroOp to allow this and other folds.
   if (match(Op0, m_ImmConstant()) &&
       match(Op1, m_Select(m_Value(), m_ImmConstant(), m_ImmConstant()))) {
@@ -1189,6 +1199,21 @@ Instruction *InstCombinerImpl::commonIDivTransforms(BinaryOperator &I) {
       return R;
   }
 
+  return nullptr;
+}
+
+/// This function implements the transforms common to both integer division
+/// instructions (udiv and sdiv). It is called by the visitors to those integer
+/// division instructions.
+/// Common integer divide transforms
+Instruction *InstCombinerImpl::commonIDivTransforms(BinaryOperator &I) {
+  if (Instruction *Res = commonIDivRemTransforms(I))
+    return Res;
+
+  Value *Op0 = I.getOperand(0), *Op1 = I.getOperand(1);
+  bool IsSigned = I.getOpcode() == Instruction::SDiv;
+  Type *Ty = I.getType();
+
   const APInt *C2;
   if (match(Op1, m_APInt(C2))) {
     Value *X;
@@ -2138,29 +2163,11 @@ static Instruction *simplifyIRemMulShl(BinaryOperator &I,
 /// remainder instructions.
 /// Common integer remainder transforms
 Instruction *InstCombinerImpl::commonIRemTransforms(BinaryOperator &I) {
-  if (Instruction *Phi = foldBinopWithPhiOperands(I))
-    return Phi;
+  if (Instruction *Res = commonIDivRemTransforms(I))
+    return Res;
 
   Value *Op0 = I.getOperand(0), *Op1 = I.getOperand(1);
 
-  // The RHS is known non-zero.
-  if (Value *V = simplifyValueKnownNonZero(I.getOperand(1), *this, I))
-    return replaceOperand(I, 1, V);
-
-  // Handle cases involving: rem X, (select Cond, Y, Z)
-  if (simplifyDivRemOfSelectWithZeroOp(I))
-    return &I;
-
-  // If the divisor is a select-of-constants, try to constant fold all rem ops:
-  // C % (select Cond, TrueC, FalseC) --> select Cond, (C % TrueC), (C % FalseC)
-  // TODO: Adapt simplifyDivRemOfSelectWithZeroOp to allow this and other folds.
-  if (match(Op0, m_ImmConstant()) &&
-      match(Op1, m_Select(m_Value(), m_ImmConstant(), m_ImmConstant()))) {
-    if (Instruction *R = FoldOpIntoSelect(I, cast<SelectInst>(Op1),
-                                          /*FoldWithMultiUse*/ true))
-      return R;
-  }
-
   if (isa<Constant>(Op1)) {
     if (Instruction *Op0I = dyn_cast<Instruction>(Op0)) {
       if (SelectInst *SI = dyn_cast<SelectInst>(Op0I)) {


Regression tests:
file: llvm/test/Transforms/InstCombine/div.ll
commands: opt < %s -passes=instcombine -S
tests: test_name: sdiv_constant_dividend_select_of_constants_divisor_vec
test_body: define <2 x i8> @sdiv_constant_dividend_select_of_constants_divisor_vec(i1 %b) {
  %s = select i1 %b, <2 x i8> <i8 12, i8 -5>, <2 x i8> <i8 -4, i8 4>
  %r = sdiv <2 x i8> <i8 42, i8 -42>, %s
  ret <2 x i8> %r
}

test_name: udiv_constant_dividend_select_of_constants_divisor_vec
test_body: define <2 x i8> @udiv_constant_dividend_select_of_constants_divisor_vec(i1 %b) {
  %s = select i1 %b, <2 x i8> <i8 12, i8 -5>, <2 x i8> <i8 -4, i8 4>
  %r = udiv <2 x i8> <i8 42, i8 -42>, %s
  ret <2 x i8> %r
}

test_name: sdiv_constant_dividend_select_of_constants_divisor_vec_ub1
test_body: define <2 x i8> @sdiv_constant_dividend_select_of_constants_divisor_vec_ub1(i1 %b) {
  %s = select i1 %b, <2 x i8> <i8 0, i8 -5>, <2 x i8> <i8 -4, i8 4>
  %r = sdiv <2 x i8> <i8 42, i8 -42>, %s
  ret <2 x i8> %r
}

test_name: udiv_constant_dividend_select_of_constants_divisor_vec_ub1
test_body: define <2 x i8> @udiv_constant_dividend_select_of_constants_divisor_vec_ub1(i1 %b) {
  %s = select i1 %b, <2 x i8> <i8 0, i8 -5>, <2 x i8> <i8 -4, i8 4>
  %r = udiv <2 x i8> <i8 42, i8 -42>, %s
  ret <2 x i8> %r
}

file: llvm/test/Transforms/InstCombine/rem.ll
commands: opt < %s -passes=instcombine -S
tests: test_name: urem_constant_dividend_select_of_constants_divisor_vec_ub1
test_body: define <2 x i8> @urem_constant_dividend_select_of_constants_divisor_vec_ub1(i1 %b) {
  %s = select i1 %b, <2 x i8> <i8 0, i8 -5>, <2 x i8> <i8 -4, i8 4>
  %r = urem <2 x i8> <i8 42, i8 -42>, %s
  ret <2 x i8> %r
}

test_name: urem_constant_dividend_select_of_constants_divisor_vec
test_body: define <2 x i8> @urem_constant_dividend_select_of_constants_divisor_vec(i1 %b) {
  %s = select i1 %b, <2 x i8> <i8 12, i8 -5>, <2 x i8> <i8 -4, i8 4>
  %r = urem <2 x i8> <i8 42, i8 -42>, %s
  ret <2 x i8> %r
}

file: llvm/test/Transforms/InstCombine/vector-udiv.ll
commands: opt < %s -passes=instcombine -S
tests: test_name: vec_select_udiv_poison
test_body: define <2 x i32> @vec_select_udiv_poison(<2 x i1> %x) {
  %sel = select <2 x i1> %x, <2 x i32> splat (i32 -1), <2 x i32> <i32 0, i32 1>
  %div = udiv <2 x i32> <i32 42, i32 -7>, %sel
  ret <2 x i32> %div
}

test_name: test_v4i32_zext_shl_const_pow2
test_body: define <4 x i32> @test_v4i32_zext_shl_const_pow2(<4 x i32> %a0, <4 x i16> %a1) {
  %1 = shl <4 x i16> <i16 4, i16 8, i16 16, i16 32>, %a1
  %2 = zext <4 x i16> %1 to <4 x i32>
  %3 = udiv <4 x i32> %a0, %2
  ret <4 x i32> %3
}

file: llvm/test/Transforms/InstSimplify/div.ll
commands: opt < %s -passes=instsimplify -S
tests: test_name: udiv_zero_elt_vec
test_body: define <2 x i8> @udiv_zero_elt_vec(<2 x i8> %x) {
  %div = udiv <2 x i8> %x, <i8 0, i8 42>
  ret <2 x i8> %div
}

test_name: sdiv_zero_elt_vec
test_body: define <2 x i8> @sdiv_zero_elt_vec(<2 x i8> %x) {
  %div = sdiv <2 x i8> %x, <i8 -42, i8 0>
  ret <2 x i8> %div
}

test_name: udiv_poison_elt_vec
test_body: define <2 x i8> @udiv_poison_elt_vec(<2 x i8> %x) {
  %div = udiv <2 x i8> %x, <i8 poison, i8 42>
  ret <2 x i8> %div
}

test_name: sdiv_zero_elt_vec_constfold
test_body: define <2 x i8> @sdiv_zero_elt_vec_constfold(<2 x i8> %x) {
  %div = sdiv <2 x i8> <i8 1, i8 2>, <i8 0, i8 -42>
  ret <2 x i8> %div
}

test_name: sdiv_poison_elt_vec
test_body: define <2 x i8> @sdiv_poison_elt_vec(<2 x i8> %x) {
  %div = sdiv <2 x i8> %x, <i8 -42, i8 poison>
  ret <2 x i8> %div
}

test_name: udiv_zero_elt_vec_constfold
test_body: define <2 x i8> @udiv_zero_elt_vec_constfold(<2 x i8> %x) {
  %div = udiv <2 x i8> <i8 1, i8 2>, <i8 42, i8 0>
  ret <2 x i8> %div
}

test_name: zero_dividend_vector_poison_elt
test_body: define <2 x i32> @zero_dividend_vector_poison_elt(<2 x i32> %A) {
  %B = sdiv <2 x i32> <i32 0, i32 poison>, %A
  ret <2 x i32> %B
}

file: llvm/test/Transforms/InstSimplify/rem.ll
commands: opt < %s -passes=instsimplify -S
tests: test_name: srem_zero_elt_vec_constfold
test_body: define <2 x i8> @srem_zero_elt_vec_constfold(<2 x i8> %x) {
  %rem = srem <2 x i8> <i8 1, i8 2>, <i8 0, i8 -42>
  ret <2 x i8> %rem
}

test_name: urem_zero_elt_vec
test_body: define <2 x i8> @urem_zero_elt_vec(<2 x i8> %x) {
  %rem = urem <2 x i8> %x, <i8 0, i8 42>
  ret <2 x i8> %rem
}

test_name: urem_zero_elt_vec_constfold
test_body: define <2 x i8> @urem_zero_elt_vec_constfold(<2 x i8> %x) {
  %rem = urem <2 x i8> <i8 1, i8 2>, <i8 42, i8 0>
  ret <2 x i8> %rem
}

test_name: srem_undef_elt_vec
test_body: define <2 x i8> @srem_undef_elt_vec(<2 x i8> %x) {
  %rem = srem <2 x i8> %x, <i8 -42, i8 undef>
  ret <2 x i8> %rem
}

test_name: urem_undef_elt_vec
test_body: define <2 x i8> @urem_undef_elt_vec(<2 x i8> %x) {
  %rem = urem <2 x i8> %x, <i8 undef, i8 42>
  ret <2 x i8> %rem
}

test_name: zero_dividend_vector_poison_elt
test_body: define <2 x i32> @zero_dividend_vector_poison_elt(<2 x i32> %A) {
  %B = urem <2 x i32> <i32 poison, i32 0>, %A
  ret <2 x i32> %B
}

test_name: srem_zero_elt_vec
test_body: define <2 x i8> @srem_zero_elt_vec(<2 x i8> %x) {
  %rem = srem <2 x i8> %x, <i8 -42, i8 0>
  ret <2 x i8> %rem
}