Bug type: miscompilation
Component: SLPVectorizer
Bug location: llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp: BoUpSLP::collectValuesToDemote
BoUpSLP::computeMinimumValueSizes
Issue title: [SLPVectorizer] Miscompile at O3
Labels: miscompilation
llvm:SLPVectorizer
Reported by: dtcxzyw

Reproducer: https://alive2.llvm.org/ce/z/_88AAB
```
; bin/opt -passes=slp-vectorizer reduced.ll -S
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

define i8 @func_129() {
entry:
  %conv4.i.i = zext i16 0 to i32
  %conv7.i.i = sext i16 0 to i32
  %cmp8.i.i = icmp slt i32 %conv7.i.i, %conv4.i.i
  %conv9.i.i = zext i1 %cmp8.i.i to i32
  %or10.i.i = or i32 %conv9.i.i, %conv4.i.i
  %cmp11.i.i = icmp eq i32 %or10.i.i, %conv4.i.i
  %sub.i.i79.peel.i = sub i16 0, 1
  %xor5.i81.peel.i = zext i16 %sub.i.i79.peel.i to i32
  %conv7.i84.peel.i = sext i16 0 to i32
  %cmp8.i85.peel.i = icmp slt i32 %conv7.i84.peel.i, %xor5.i81.peel.i
  %conv9.i86.peel.i = zext i1 %cmp8.i85.peel.i to i32
  %or10.i87.peel.i = or i32 %conv9.i86.peel.i, %xor5.i81.peel.i
  %cmp11.i88.peel.i = icmp eq i32 %or10.i87.peel.i, %xor5.i81.peel.i
  %conv13.i89.peel.i = zext i1 %cmp8.i85.peel.i to i8
  ret i8 %conv13.i89.peel.i
}
```
```
define i8 @func_129() {
entry:
  %sub.i.i79.peel.i = sub i16 0, 1
  %0 = insertelement <2 x i16> <i16 poison, i16 0>, i16 %sub.i.i79.peel.i, i32 0
  %1 = icmp slt <2 x i16> zeroinitializer, %0
  %2 = zext <2 x i1> %1 to <2 x i16>
  %3 = or <2 x i16> %2, %0
  %4 = icmp eq <2 x i16> %3, %0
  %5 = extractelement <2 x i1> %1, i32 0
  %conv13.i89.peel.i = zext i1 %5 to i8
  ret i8 %conv13.i89.peel.i
}
```
```
----------------------------------------
define i8 @src() {
entry:
  %sub.i.i79.peel.i = sub i16 0, 1
  %xor5.i81.peel.i = zext i16 %sub.i.i79.peel.i to i32
  %conv7.i84.peel.i = sext i16 0 to i32
  %cmp8.i85.peel.i = icmp slt i32 %conv7.i84.peel.i, %xor5.i81.peel.i
  %conv13.i89.peel.i = zext i1 %cmp8.i85.peel.i to i8
  ret i8 %conv13.i89.peel.i
}
=>
define i8 @tgt() {
entry:
  %sub.i.i79.peel.i = sub i16 0, 1
  %#0 = insertelement <2 x i16> { poison, 0 }, i16 %sub.i.i79.peel.i, i32 0
  %#1 = icmp slt <2 x i16> { 0, 0 }, %#0
  %#5 = extractelement <2 x i1> %#1, i32 0
  %conv13.i89.peel.i = zext i1 %#5 to i8
  ret i8 %conv13.i89.peel.i
}
Transformation doesn't verify!

ERROR: Value mismatch

Example:

Source:
i16 %sub.i.i79.peel.i = #xffff (65535, -1)
i32 %xor5.i81.peel.i = #x0000ffff (65535)
i32 %conv7.i84.peel.i = #x00000000 (0)
i1 %cmp8.i85.peel.i = #x1 (1)
i8 %conv13.i89.peel.i = #x01 (1)

Target:
i16 %sub.i.i79.peel.i = #xffff (65535, -1)
<2 x i16> %#0 = < #xffff (65535, -1), #x0000 (0) >
<2 x i1> %#1 = < #x0 (0), #x0 (0) >
i1 %#5 = #x0 (0)
i8 %conv13.i89.peel.i = #x00 (0)
Source value: #x01 (1)
Target value: #x00 (0)
```


Comments: author: fhahn
body: cc @alexey-bataev 


Fix patch:
commit d1a7225076218ce224cd29c74259b715b393dc9d
Author: Alexey Bataev <a.bataev@outlook.com>
Date:   Mon Dec 16 07:52:32 2024 -0800

    [SLP]Check if the node must keep its original bitwidth
    
    Need to check if during previous analysis the node has requested to keep
    its original bitwidth to avoid incorrect codegen.
    
    Fixes #120076

diff --git a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
index 3bd983ee6e12..d967813075bb 100644
--- a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
+++ b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
@@ -2931,13 +2931,11 @@ private:
   /// truncation. We collect the entries that will be demoted in ToDemote.
   /// \param E Node for analysis
   /// \param ToDemote indices of the nodes to be demoted.
-  bool collectValuesToDemote(const TreeEntry &E, bool IsProfitableToDemoteRoot,
-                             unsigned &BitWidth,
-                             SmallVectorImpl<unsigned> &ToDemote,
-                             DenseSet<const TreeEntry *> &Visited,
-                             unsigned &MaxDepthLevel,
-                             bool &IsProfitableToDemote,
-                             bool IsTruncRoot) const;
+  bool collectValuesToDemote(
+      const TreeEntry &E, bool IsProfitableToDemoteRoot, unsigned &BitWidth,
+      SmallVectorImpl<unsigned> &ToDemote, DenseSet<const TreeEntry *> &Visited,
+      const SmallDenseSet<unsigned, 8> &NodesToKeepBWs, unsigned &MaxDepthLevel,
+      bool &IsProfitableToDemote, bool IsTruncRoot) const;
 
   /// Check if the operands on the edges \p Edges of the \p UserTE allows
   /// reordering (i.e. the operands can be reordered because they have only one
@@ -17515,8 +17513,8 @@ unsigned BoUpSLP::getVectorElementSize(Value *V) {
 bool BoUpSLP::collectValuesToDemote(
     const TreeEntry &E, bool IsProfitableToDemoteRoot, unsigned &BitWidth,
     SmallVectorImpl<unsigned> &ToDemote, DenseSet<const TreeEntry *> &Visited,
-    unsigned &MaxDepthLevel, bool &IsProfitableToDemote,
-    bool IsTruncRoot) const {
+    const SmallDenseSet<unsigned, 8> &NodesToKeepBWs, unsigned &MaxDepthLevel,
+    bool &IsProfitableToDemote, bool IsTruncRoot) const {
   // We can always demote constants.
   if (all_of(E.Scalars, IsaPred<Constant>))
     return true;
@@ -17528,6 +17526,10 @@ bool BoUpSLP::collectValuesToDemote(
     return true;
   }
 
+  // Check if the node was analyzed already and must keep its original bitwidth.
+  if (NodesToKeepBWs.contains(E.Idx))
+    return false;
+
   // If the value is not a vectorized instruction in the expression and not used
   // by the insertelement instruction and not used in multiple vector nodes, it
   // cannot be demoted.
@@ -17623,8 +17625,8 @@ bool BoUpSLP::collectValuesToDemote(
     for (const TreeEntry *Op : Operands) {
       unsigned Level = InitLevel;
       if (!collectValuesToDemote(*Op, IsProfitableToDemoteRoot, BitWidth,
-                                 ToDemote, Visited, Level, IsProfitableToDemote,
-                                 IsTruncRoot)) {
+                                 ToDemote, Visited, NodesToKeepBWs, Level,
+                                 IsProfitableToDemote, IsTruncRoot)) {
         if (!IsProfitableToDemote)
           return false;
         NeedToExit = true;
@@ -17926,6 +17928,7 @@ void BoUpSLP::computeMinimumValueSizes() {
   bool IsTruncRoot = false;
   bool IsProfitableToDemoteRoot = !IsStoreOrInsertElt;
   SmallVector<unsigned> RootDemotes;
+  SmallDenseSet<unsigned, 8> NodesToKeepBWs;
   if (NodeIdx != 0 &&
       VectorizableTree[NodeIdx]->State == TreeEntry::Vectorize &&
       VectorizableTree[NodeIdx]->getOpcode() == Instruction::Trunc) {
@@ -17949,6 +17952,7 @@ void BoUpSLP::computeMinimumValueSizes() {
     // Check if the root is trunc and the next node is gather/buildvector, then
     // keep trunc in scalars, which is free in most cases.
     if (E.isGather() && IsTruncRoot && E.UserTreeIndices.size() == 1 &&
+        !NodesToKeepBWs.contains(E.Idx) &&
         E.Idx > (IsStoreOrInsertElt ? 2u : 1u) &&
         all_of(E.Scalars, [&](Value *V) {
           return V->hasOneUse() || isa<Constant>(V) ||
@@ -18071,8 +18075,8 @@ void BoUpSLP::computeMinimumValueSizes() {
     bool NeedToDemote = IsProfitableToDemote;
 
     if (!collectValuesToDemote(E, IsProfitableToDemoteRoot, MaxBitWidth,
-                               ToDemote, Visited, MaxDepthLevel, NeedToDemote,
-                               IsTruncRoot) ||
+                               ToDemote, Visited, NodesToKeepBWs, MaxDepthLevel,
+                               NeedToDemote, IsTruncRoot) ||
         (MaxDepthLevel <= Limit &&
          !(((Opcode == Instruction::SExt || Opcode == Instruction::ZExt) &&
             (!IsTopRoot || !(IsStoreOrInsertElt || UserIgnoreList) ||
@@ -18206,7 +18210,7 @@ void BoUpSLP::computeMinimumValueSizes() {
                  });
     }
 
-    // If the maximum bit width we compute is less than the with of the roots'
+    // If the maximum bit width we compute is less than the width of the roots'
     // type, we can proceed with the narrowing. Otherwise, do nothing.
     if (MaxBitWidth == 0 ||
         MaxBitWidth >=
@@ -18214,6 +18218,7 @@ void BoUpSLP::computeMinimumValueSizes() {
                 ->getBitWidth()) {
       if (UserIgnoreList)
         AnalyzedMinBWVals.insert(TreeRoot.begin(), TreeRoot.end());
+      NodesToKeepBWs.insert(ToDemote.begin(), ToDemote.end());
       continue;
     }
 


Regression tests:
file: llvm/test/Transforms/SLPVectorizer/X86/minbw-node-used-twice.ll
commands: opt -S --passes=slp-vectorizer -mtriple=x86_64-unknown-linux < %s
tests: test_name: test
test_body: define i8 @test() {
entry:
  %conv4.i.i = zext i16 0 to i32
  %conv7.i.i = sext i16 0 to i32
  %cmp8.i.i = icmp slt i32 %conv7.i.i, %conv4.i.i
  %conv9.i.i = zext i1 %cmp8.i.i to i32
  %or10.i.i = or i32 %conv9.i.i, %conv4.i.i
  %cmp11.i.i = icmp eq i32 %or10.i.i, %conv4.i.i
  %sub.i.i79.peel.i = sub i16 0, 1
  %xor5.i81.peel.i = zext i16 %sub.i.i79.peel.i to i32
  %conv7.i84.peel.i = sext i16 0 to i32
  %cmp8.i85.peel.i = icmp slt i32 %conv7.i84.peel.i, %xor5.i81.peel.i
  %conv9.i86.peel.i = zext i1 %cmp8.i85.peel.i to i32
  %or10.i87.peel.i = or i32 %conv9.i86.peel.i, %xor5.i81.peel.i
  %cmp11.i88.peel.i = icmp eq i32 %or10.i87.peel.i, %xor5.i81.peel.i
  %conv13.i89.peel.i = zext i1 %cmp8.i85.peel.i to i8
  ret i8 %conv13.i89.peel.i
}