[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