[llvm] [SLP] Drop nsw when add/sub interchange negates INT_MIN (PR #206558)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Jun 29 12:00:40 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-vectorizers
Author: Alexey Bataev (alexey-bataev)
<details>
<summary>Changes</summary>
BinOpSameOpcodeHelper unifies add and sub by negating a constant operand.
Negating INT_MIN overflows and yields INT_MIN, but add nsw X, INT_MIN and
sub nsw X, INT_MIN have opposite validity domains, so nsw is invalid on the
converted lane and must be dropped.
Fixes #<!-- -->206474.
---
Full diff: https://github.com/llvm/llvm-project/pull/206558.diff
2 Files Affected:
- (modified) llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp (+18)
- (modified) llvm/test/Transforms/SLPVectorizer/X86/add-sub-nsw-intmin.ll (+2-2)
``````````diff
diff --git a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
index d1fb8e2beea50..2ecca570f41fb 100644
--- a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
+++ b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
@@ -23336,6 +23336,24 @@ Value *BoUpSLP::vectorizeTree(TreeEntry *E) {
return !SI || isCommutative(SI);
})))
I->setHasNoUnsignedWrap(/*b=*/false);
+ // add nsw X, INT_MIN is not equivalent to sub nsw X, INT_MIN, because
+ // negating INT_MIN overflows. When an add/sub lane is converted to the
+ // opposite opcode its constant is negated, so nsw no longer holds and must
+ // be dropped.
+ if (!MinBWs.contains(E) &&
+ (Opcode == Instruction::Add || Opcode == Instruction::Sub) &&
+ any_of(UniqueInsts, [&](Value *V) {
+ auto *SI = cast<Instruction>(V);
+ if (SI->getOpcode() == Opcode ||
+ !is_contained({Instruction::Add, Instruction::Sub},
+ SI->getOpcode()))
+ return false;
+ return any_of(SI->operands(), [](Value *Op) {
+ const auto *CI = dyn_cast<ConstantInt>(Op);
+ return CI && CI->getValue().isMinSignedValue();
+ });
+ }))
+ I->setHasNoSignedWrap(/*b=*/false);
if (auto *ICmp = dyn_cast<ICmpInst>(I); ICmp && It == MinBWs.end())
ICmp->setSameSign(/*B=*/false);
return I;
diff --git a/llvm/test/Transforms/SLPVectorizer/X86/add-sub-nsw-intmin.ll b/llvm/test/Transforms/SLPVectorizer/X86/add-sub-nsw-intmin.ll
index 351c87fcf6fff..f9782dc7785ff 100644
--- a/llvm/test/Transforms/SLPVectorizer/X86/add-sub-nsw-intmin.ll
+++ b/llvm/test/Transforms/SLPVectorizer/X86/add-sub-nsw-intmin.ll
@@ -8,7 +8,7 @@ define void @add_intmin_converted_to_sub(ptr %p, i32 %x, i32 %y) {
; CHECK-LABEL: @add_intmin_converted_to_sub(
; CHECK-NEXT: [[TMP1:%.*]] = insertelement <2 x i32> poison, i32 [[X:%.*]], i32 0
; CHECK-NEXT: [[TMP2:%.*]] = insertelement <2 x i32> [[TMP1]], i32 [[Y:%.*]], i32 1
-; CHECK-NEXT: [[TMP3:%.*]] = sub nsw <2 x i32> [[TMP2]], <i32 -2147483648, i32 7>
+; CHECK-NEXT: [[TMP3:%.*]] = sub <2 x i32> [[TMP2]], <i32 -2147483648, i32 7>
; CHECK-NEXT: store <2 x i32> [[TMP3]], ptr [[P:%.*]], align 4
; CHECK-NEXT: ret void
;
@@ -26,7 +26,7 @@ define void @sub_intmin_converted_to_add(ptr %p, i32 %x, i32 %y) {
; CHECK-LABEL: @sub_intmin_converted_to_add(
; CHECK-NEXT: [[TMP1:%.*]] = insertelement <2 x i32> poison, i32 [[X:%.*]], i32 0
; CHECK-NEXT: [[TMP2:%.*]] = insertelement <2 x i32> [[TMP1]], i32 [[Y:%.*]], i32 1
-; CHECK-NEXT: [[TMP3:%.*]] = add nsw <2 x i32> [[TMP2]], <i32 -2147483648, i32 7>
+; CHECK-NEXT: [[TMP3:%.*]] = add <2 x i32> [[TMP2]], <i32 -2147483648, i32 7>
; CHECK-NEXT: store <2 x i32> [[TMP3]], ptr [[P:%.*]], align 4
; CHECK-NEXT: ret void
;
``````````
</details>
https://github.com/llvm/llvm-project/pull/206558
More information about the llvm-commits
mailing list