[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