Bug type: miscompilation
Component: SLPVectorizer
Bug location: llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp: BoUpSLP::collectValuesToDemote
Issue title: [SLP] SLP Vectorizer miscompile from reducing abs bitwidth
Labels: miscompilation
release:backport
llvm:SLPVectorizer
Reported by: danilaml

Bisected miscompile to this change https://github.com/llvm/llvm-project/commit/4a1c53f9fabbc18d436dcd4d5b572b82656fbbf9

Reduced internal test to this example:
https://godbolt.org/z/njWYP9exd
```llvm
define dso_local noundef i32 @test(i32 %n) local_unnamed_addr #0 {
entry:
  %n1 = add i32 %n, 1
  %zn1 = zext nneg i32 %n1 to i64
  %m1 = mul nuw nsw i64 %zn1, 273837369
  %a1 = call noundef i64 @llvm.abs.i64(i64 %m1, i1 true)
  %t1 = trunc i64 %a1 to i32
  %n2 = add i32 %n, 2
  %zn2 = zext nneg i32 %n2 to i64
  %m2 = mul nuw nsw i64 %zn2, 273837369
  %a2 = call noundef i64 @llvm.abs.i64(i64 %m2, i1 true)
  %t2 = trunc i64 %a2 to i32

  %res1 = add i32 %t1, %t2
  ret i32 %res1
}
```
Running slp vectorizer on it gives
```llvm
define dso_local noundef i32 @test(i32 %n) local_unnamed_addr {
  %0 = insertelement <2 x i32> poison, i32 %n, i32 0
  %1 = shufflevector <2 x i32> %0, <2 x i32> poison, <2 x i32> zeroinitializer
  %2 = add <2 x i32> %1, <i32 1, i32 2>
  %3 = mul <2 x i32> %2, <i32 273837369, i32 273837369>
  %4 = call <2 x i32> @llvm.abs.v2i32(<2 x i32> %3, i1 false)
  %5 = extractelement <2 x i32> %4, i32 0
  %6 = extractelement <2 x i32> %4, i32 1
  %res1 = add i32 %5, %6
  ret i32 %res1
}
```
which is incorrect. Counterexample by alive2:
https://alive2.llvm.org/ce/z/cEcY-u

Comments: author: AZero13
body: Do we plan on backporting this to 19.x @alexey-bataev 

Fix patch:
commit 709abacdc350d63c61888607edb28ce272daa0a0
Author: Alexey Bataev <a.bataev@outlook.com>
Date:   Fri Oct 18 13:54:30 2024 -0700

    [SLP]Check that operand of abs does not overflow before making it part of minbitwidth transformation
    
    Need to check that the operand of the abs intrinsic can be safely
    truncated before making it part of the minbitwidth transformation.
    
    Fixes #112577

diff --git a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
index e7b52fbdcc3b..e1aa6127ac03 100644
--- a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
+++ b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
@@ -17224,9 +17224,25 @@ bool BoUpSLP::collectValuesToDemote(
                 MaskedValueIsZero(I->getOperand(1), Mask, SimplifyQuery(*DL)));
       });
     };
+    auto AbsChecker = [&](unsigned BitWidth, unsigned OrigBitWidth) {
+      assert(BitWidth <= OrigBitWidth && "Unexpected bitwidths!");
+      return all_of(E.Scalars, [&](Value *V) {
+        auto *I = cast<Instruction>(V);
+        unsigned SignBits = OrigBitWidth - BitWidth;
+        APInt Mask = APInt::getBitsSetFrom(OrigBitWidth, BitWidth - 1);
+        unsigned Op0SignBits =
+            ComputeNumSignBits(I->getOperand(0), *DL, 0, AC, nullptr, DT);
+        return SignBits <= Op0SignBits &&
+               ((SignBits != Op0SignBits &&
+                 !isKnownNonNegative(I->getOperand(0), SimplifyQuery(*DL))) ||
+                MaskedValueIsZero(I->getOperand(0), Mask, SimplifyQuery(*DL)));
+      });
+    };
     if (ID != Intrinsic::abs) {
       Operands.push_back(getOperandEntry(&E, 1));
       CallChecker = CompChecker;
+    } else {
+      CallChecker = AbsChecker;
     }
     InstructionCost BestCost =
         std::numeric_limits<InstructionCost::CostType>::max();


Regression tests:
file: llvm/test/Transforms/SLPVectorizer/abs-overflow-incorrect-minbws.ll
commands: opt -S --passes=slp-vectorizer < %s
tests: test_name: test
test_body: define i32 @test(i32 %n) {
entry:
  %n1 = add i32 %n, 1
  %zn1 = zext nneg i32 %n1 to i64
  %m1 = mul nuw nsw i64 %zn1, 273837369
  %a1 = call i64 @llvm.abs.i64(i64 %m1, i1 true)
  %t1 = trunc i64 %a1 to i32
  %n2 = add i32 %n, 2
  %zn2 = zext nneg i32 %n2 to i64
  %m2 = mul nuw nsw i64 %zn2, 273837369
  %a2 = call i64 @llvm.abs.i64(i64 %m2, i1 true)
  %t2 = trunc i64 %a2 to i32
  %res1 = add i32 %t1, %t2
  ret i32 %res1
}

; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i64 @llvm.abs.i64(i64, i1 immarg) #0

attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }