[llvm] [SLP] Use the correct identity when combining binary opcodes with AND/MUL (PR #180457)

via llvm-commits llvm-commits at lists.llvm.org
Sun Feb 8 19:35:10 PST 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Ryan Buchner (bababuck)

<details>
<summary>Changes</summary>

Fixes #<!-- -->180456

Fix the following SLP lowering:
```
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
```
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> -> updates to <i16 1, i16 5, i16 2, i16 3>
store <4 x i16> %tmp1, ptr %s, align 2
ret void
}
```

---
Full diff: https://github.com/llvm/llvm-project/pull/180457.diff


2 Files Affected:

- (modified) llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp (+15-11) 
- (added) llvm/test/Transforms/SLPVectorizer/semanticly-same.ll (+111) 


``````````diff
diff --git a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
index f89c22fafcf04..a987d394608eb 100644
--- a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
+++ b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
@@ -1072,6 +1072,16 @@ class BinOpSameOpcodeHelper {
     }
 
     SmallVector<Value *> getOperand(const Instruction *To) const {
+      auto GetIdentityValue = [](unsigned Opcode, unsigned BitWidth) -> APInt {
+        switch (Opcode) {
+        case Instruction::Mul:
+          return APInt::getOneBitSet(BitWidth, 0);
+        case Instruction::And:
+          return APInt::getAllOnes(BitWidth);
+        default:
+          return APInt::getZero(BitWidth);
+        }
+      };
       unsigned ToOpcode = To->getOpcode();
       unsigned FromOpcode = I->getOpcode();
       if (FromOpcode == ToOpcode)
@@ -1088,9 +1098,7 @@ class BinOpSameOpcodeHelper {
                                           FromCIValue.getZExtValue());
         } else {
           assert(FromCIValue.isZero() && "Cannot convert the instruction.");
-          ToCIValue = ToOpcode == Instruction::And
-                          ? APInt::getAllOnes(FromCIValueBitWidth)
-                          : APInt::getZero(FromCIValueBitWidth);
+          ToCIValue = GetIdentityValue(ToOpcode, FromCIValueBitWidth);
         }
         break;
       case Instruction::Mul:
@@ -1099,15 +1107,13 @@ class BinOpSameOpcodeHelper {
           ToCIValue = APInt(FromCIValueBitWidth, FromCIValue.logBase2());
         } else {
           assert(FromCIValue.isOne() && "Cannot convert the instruction.");
-          ToCIValue = ToOpcode == Instruction::And
-                          ? APInt::getAllOnes(FromCIValueBitWidth)
-                          : APInt::getZero(FromCIValueBitWidth);
+          ToCIValue = GetIdentityValue(ToOpcode, FromCIValueBitWidth);
         }
         break;
       case Instruction::Add:
       case Instruction::Sub:
         if (FromCIValue.isZero()) {
-          ToCIValue = APInt::getZero(FromCIValueBitWidth);
+          ToCIValue = GetIdentityValue(ToOpcode, FromCIValueBitWidth);
         } else {
           assert(is_contained({Instruction::Add, Instruction::Sub}, ToOpcode) &&
                  "Cannot convert the instruction.");
@@ -1117,13 +1123,11 @@ class BinOpSameOpcodeHelper {
         break;
       case Instruction::And:
         assert(FromCIValue.isAllOnes() && "Cannot convert the instruction.");
-        ToCIValue = ToOpcode == Instruction::Mul
-                        ? APInt::getOneBitSet(FromCIValueBitWidth, 0)
-                        : APInt::getZero(FromCIValueBitWidth);
+        ToCIValue = GetIdentityValue(ToOpcode, FromCIValueBitWidth);
         break;
       default:
         assert(FromCIValue.isZero() && "Cannot convert the instruction.");
-        ToCIValue = APInt::getZero(FromCIValueBitWidth);
+        ToCIValue = GetIdentityValue(ToOpcode, FromCIValueBitWidth);
         break;
       }
       Value *LHS = I->getOperand(1 - Pos);
diff --git a/llvm/test/Transforms/SLPVectorizer/semanticly-same.ll b/llvm/test/Transforms/SLPVectorizer/semanticly-same.ll
new file mode 100644
index 0000000000000..c434fb5c97c07
--- /dev/null
+++ b/llvm/test/Transforms/SLPVectorizer/semanticly-same.ll
@@ -0,0 +1,111 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: %if x86-registered-target %{ opt -S --passes=slp-vectorizer -mtriple=x86_64-unknown-linux-gnu -slp-threshold=-50 < %s | FileCheck %s %}
+; RUN: %if aarch64-registered-target %{ opt -S --passes=slp-vectorizer -mtriple=aarch64-unknown-linux-gnu -slp-threshold=-50 < %s | FileCheck %s %}
+
+; Don't care about the profitability with these tests, just want to demonstrate the ability
+; to combine opcodes
+
+define void @sub_mul(ptr %p, ptr %s) {
+; CHECK-LABEL: define void @sub_mul(
+; CHECK-SAME: ptr [[P:%.*]], ptr [[S:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = load <4 x i16>, ptr [[P]], align 2
+; CHECK-NEXT:    [[TMP1:%.*]] = mul <4 x i16> [[TMP0]], <i16 1, i16 5, i16 2, i16 3>
+; CHECK-NEXT:    store <4 x i16> [[TMP1]], ptr [[S]], align 2
+; CHECK-NEXT:    ret void
+;
+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
+}
+
+define void @add_mul(ptr %p, ptr %s) {
+; CHECK-LABEL: define void @add_mul(
+; CHECK-SAME: ptr [[P:%.*]], ptr [[S:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = load <4 x i16>, ptr [[P]], align 2
+; CHECK-NEXT:    [[TMP1:%.*]] = mul <4 x i16> [[TMP0]], <i16 1, i16 5, i16 2, i16 3>
+; CHECK-NEXT:    store <4 x i16> [[TMP1]], ptr [[S]], align 2
+; CHECK-NEXT:    ret void
+;
+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 = 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
+  store i16 %mul1, ptr %s1
+  store i16 %mul2, ptr %s2
+  store i16 %mul3, ptr %s3
+  ret void
+}
+
+define void @sub_and(ptr %p, ptr %s) {
+; CHECK-LABEL: define void @sub_and(
+; CHECK-SAME: ptr [[P:%.*]], ptr [[S:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = load <4 x i16>, ptr [[P]], align 2
+; CHECK-NEXT:    [[TMP1:%.*]] = and <4 x i16> [[TMP0]], <i16 -1, i16 5, i16 2, i16 3>
+; CHECK-NEXT:    store <4 x i16> [[TMP1]], ptr [[S]], align 2
+; CHECK-NEXT:    ret void
+;
+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 = 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
+  store i16 %mul1, ptr %s1
+  store i16 %mul2, ptr %s2
+  store i16 %mul3, ptr %s3
+  ret void
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/180457


More information about the llvm-commits mailing list