Bug type: miscompilation
Component: SLPVectorizer
Bug location: llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp: getOperand
Issue title: [SLP] Binary combines with MUL and AND may use the wrong identity value
Labels: miscompilation
llvm:SLPVectorizer
Reported by: bababuck

The following lowering currently occurs: 
`opt -S --passes=slp-vectorizer -mtriple=aarch64-unknown-linux-gnu -slp-threshold=-50`
```llvm
define void @sub_mul(ptr %p, ptr %s) {
entry:
  %p1 = getelementptr i16, ptr %p, i64 1
  %p2 = getelementptr i16, ptr %p, i64 2
  %p3 = getelementptr i16, ptr %p, i64 3

  %l0 = load i16, ptr %p
  %l1 = load i16, ptr %p1
  %l2 = load i16, ptr %p2
  %l3 = load i16, ptr %p3

  %mul0 = sub i16 %l0, 0
  %mul1 = mul i16 %l1, 5
  %mul2 = mul i16 %l2, 2
  %mul3 = mul i16 %l3, 3

  %s1 = getelementptr i16, ptr %s, i64 1
  %s2 = getelementptr i16, ptr %s, i64 2
  %s3 = getelementptr i16, ptr %s, i64 3

  store i16 %mul0, ptr %s
  store i16 %mul1, ptr %s1
  store i16 %mul2, ptr %s2
  store i16 %mul3, ptr %s3
  ret void
}
```
to
```llvm
define void @sub_mul(ptr %p, ptr %s) {
entry:
%tmp0 = load <4 x i16>, ptr %p, align 2
%tmp1 = mul <4 x i16> %tmp0, <i16 0, i16 5, i16 2, i16 3>
store <4 x i16> %tmp1, ptr %s, align 2
ret void
}
```

Which is an incorrect transformation: https://alive2.llvm.org/ce/z/BgqGWV


Fix patch:
commit 95ef1a5c3139c44bb171df225cb2b0aa17b50f1d
Author: Ryan Buchner <rbuchner@qti.qualcomm.com>
Date:   Thu Feb 12 09:34:44 2026 -0800

    [SLP] Use the correct identity when combining binary opcodes with AND/MUL (#180457)
    
    Fixes #180456
    
    Fix bug in the following SLP lowering:
    ```
    define void @sub_mul(ptr %p, ptr %s) {
    entry:
      %p1 = getelementptr i16, ptr %p, i64 1
    
      %l0 = load i16, ptr %p
      %l1 = load i16, ptr %p1
    
      %mul0 = sub i16 %l0, 0
      %mul1 = mul i16 %l1, 5
    
      %s1 = getelementptr i16, ptr %s, i64 1
    
      store i16 %mul0, ptr %s
      store i16 %mul1, ptr %s1
      ret void
    }
    ```
    to
    ```
    define void @sub_mul(ptr %p, ptr %s) {
    entry:
    %tmp0 = load <2 x i16>, ptr %p, align 2
    %tmp1 = mul <2 x i16> %tmp0, <i16 0, i16 5> -> updates to <i16 1, i16 5>
    store <2 x i16> %tmp1, ptr %s, align 2
    ret void
    }
    ```

diff --git a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
index 8fb88a1fac0e..324c5729c3f5 100644
--- a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
+++ b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
@@ -1080,55 +1080,56 @@ class BinOpSameOpcodeHelper {
       auto [CI, Pos] = isBinOpWithConstantInt(I);
       const APInt &FromCIValue = CI->getValue();
       unsigned FromCIValueBitWidth = FromCIValue.getBitWidth();
-      APInt ToCIValue;
+      Type *RHSType = I->getOperand(Pos)->getType();
+      Constant *RHS;
       switch (FromOpcode) {
       case Instruction::Shl:
         if (ToOpcode == Instruction::Mul) {
-          ToCIValue = APInt::getOneBitSet(FromCIValueBitWidth,
-                                          FromCIValue.getZExtValue());
+          RHS = ConstantInt::get(
+              RHSType, APInt::getOneBitSet(FromCIValueBitWidth,
+                                           FromCIValue.getZExtValue()));
         } else {
           assert(FromCIValue.isZero() && "Cannot convert the instruction.");
-          ToCIValue = ToOpcode == Instruction::And
-                          ? APInt::getAllOnes(FromCIValueBitWidth)
-                          : APInt::getZero(FromCIValueBitWidth);
+          RHS = ConstantExpr::getBinOpIdentity(ToOpcode, RHSType,
+                                               /*AllowRHSConstant=*/true);
         }
         break;
       case Instruction::Mul:
         assert(FromCIValue.isPowerOf2() && "Cannot convert the instruction.");
         if (ToOpcode == Instruction::Shl) {
-          ToCIValue = APInt(FromCIValueBitWidth, FromCIValue.logBase2());
+          RHS = ConstantInt::get(
+              RHSType, APInt(FromCIValueBitWidth, FromCIValue.logBase2()));
         } else {
           assert(FromCIValue.isOne() && "Cannot convert the instruction.");
-          ToCIValue = ToOpcode == Instruction::And
-                          ? APInt::getAllOnes(FromCIValueBitWidth)
-                          : APInt::getZero(FromCIValueBitWidth);
+          RHS = ConstantExpr::getBinOpIdentity(ToOpcode, RHSType,
+                                               /*AllowRHSConstant=*/true);
         }
         break;
       case Instruction::Add:
       case Instruction::Sub:
         if (FromCIValue.isZero()) {
-          ToCIValue = APInt::getZero(FromCIValueBitWidth);
+          RHS = ConstantExpr::getBinOpIdentity(ToOpcode, RHSType,
+                                               /*AllowRHSConstant=*/true);
         } else {
           assert(is_contained({Instruction::Add, Instruction::Sub}, ToOpcode) &&
                  "Cannot convert the instruction.");
-          ToCIValue = FromCIValue;
-          ToCIValue.negate();
+          APInt NegatedVal = APInt(FromCIValue);
+          NegatedVal.negate();
+          RHS = ConstantInt::get(RHSType, NegatedVal);
         }
         break;
       case Instruction::And:
         assert(FromCIValue.isAllOnes() && "Cannot convert the instruction.");
-        ToCIValue = ToOpcode == Instruction::Mul
-                        ? APInt::getOneBitSet(FromCIValueBitWidth, 0)
-                        : APInt::getZero(FromCIValueBitWidth);
+        RHS = ConstantExpr::getBinOpIdentity(ToOpcode, RHSType,
+                                             /*AllowRHSConstant=*/true);
         break;
       default:
         assert(FromCIValue.isZero() && "Cannot convert the instruction.");
-        ToCIValue = APInt::getZero(FromCIValueBitWidth);
+        RHS = ConstantExpr::getBinOpIdentity(ToOpcode, RHSType,
+                                             /*AllowRHSConstant=*/true);
         break;
       }
       Value *LHS = I->getOperand(1 - Pos);
-      Constant *RHS =
-          ConstantInt::get(I->getOperand(Pos)->getType(), ToCIValue);
       // constant + x cannot be -constant - x
       // instead, it should be x - -constant
       if (Pos == 1 ||


Regression tests:
file: llvm/test/Transforms/SLPVectorizer/semanticly-same.ll
commands: opt -S --passes=slp-vectorizer -mtriple=x86_64-unknown-linux-gnu -slp-threshold=-50 < %s
opt -S --passes=slp-vectorizer -mtriple=aarch64-unknown-linux-gnu -slp-threshold=-50 < %s
tests: test_name: sub_mul
test_body: define void @sub_mul(ptr %p, ptr %s) {
entry:
  %p1 = getelementptr i16, ptr %p, i64 1
  %p2 = getelementptr i16, ptr %p, i64 2
  %p3 = getelementptr i16, ptr %p, i64 3
  %l0 = load i16, ptr %p, align 2
  %l1 = load i16, ptr %p1, align 2
  %l2 = load i16, ptr %p2, align 2
  %l3 = load i16, ptr %p3, align 2
  %mul0 = sub i16 %l0, 0
  %mul1 = mul i16 %l1, 5
  %mul2 = mul i16 %l2, 2
  %mul3 = mul i16 %l3, 3
  %s1 = getelementptr i16, ptr %s, i64 1
  %s2 = getelementptr i16, ptr %s, i64 2
  %s3 = getelementptr i16, ptr %s, i64 3
  store i16 %mul0, ptr %s, align 2
  store i16 %mul1, ptr %s1, align 2
  store i16 %mul2, ptr %s2, align 2
  store i16 %mul3, ptr %s3, align 2
  ret void
}

test_name: sub_and
test_body: define void @sub_and(ptr %p, ptr %s) {
entry:
  %p1 = getelementptr i16, ptr %p, i64 1
  %p2 = getelementptr i16, ptr %p, i64 2
  %p3 = getelementptr i16, ptr %p, i64 3
  %l0 = load i16, ptr %p, align 2
  %l1 = load i16, ptr %p1, align 2
  %l2 = load i16, ptr %p2, align 2
  %l3 = load i16, ptr %p3, align 2
  %mul0 = add i16 %l0, 0
  %mul1 = and i16 %l1, 5
  %mul2 = and i16 %l2, 2
  %mul3 = and i16 %l3, 3
  %s1 = getelementptr i16, ptr %s, i64 1
  %s2 = getelementptr i16, ptr %s, i64 2
  %s3 = getelementptr i16, ptr %s, i64 3
  store i16 %mul0, ptr %s, align 2
  store i16 %mul1, ptr %s1, align 2
  store i16 %mul2, ptr %s2, align 2
  store i16 %mul3, ptr %s3, align 2
  ret void
}

test_name: add_mul
test_body: define void @add_mul(ptr %p, ptr %s) {
entry:
  %p1 = getelementptr i16, ptr %p, i64 1
  %p2 = getelementptr i16, ptr %p, i64 2
  %p3 = getelementptr i16, ptr %p, i64 3
  %l0 = load i16, ptr %p, align 2
  %l1 = load i16, ptr %p1, align 2
  %l2 = load i16, ptr %p2, align 2
  %l3 = load i16, ptr %p3, align 2
  %mul0 = add i16 %l0, 0
  %mul1 = mul i16 %l1, 5
  %mul2 = mul i16 %l2, 2
  %mul3 = mul i16 %l3, 3
  %s1 = getelementptr i16, ptr %s, i64 1
  %s2 = getelementptr i16, ptr %s, i64 2
  %s3 = getelementptr i16, ptr %s, i64 3
  store i16 %mul0, ptr %s, align 2
  store i16 %mul1, ptr %s1, align 2
  store i16 %mul2, ptr %s2, align 2
  store i16 %mul3, ptr %s3, align 2
  ret void
}