Bug type: crash
Component: SLPVectorizer
Bug location: llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp: BoUpSLP::buildTree_rec
Issue title: [SLPVectorizer] Assertion `OpIdx < Operands.size() && "Off bounds"' failed.
Labels: llvm:SLPVectorizer
crash-on-valid
Reported by: dtcxzyw

Reproducer: https://godbolt.org/z/8Es17cqcW
```
; 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 i16 @main() {
entry:
  %conv73.i.peel.i.i.i.i.i = xor i64 0, 0
  %and.i.i.peel.i.i.i.i.i = and i64 0, 0
  %xor2.i.i.peel.i.i.i.i.i = or i64 %and.i.i.peel.i.i.i.i.i, 0
  %sub.i.i.peel.i.i.i.i.i = or i64 %xor2.i.i.peel.i.i.i.i.i, 0
  %xor3.i.i.peel.i.i.i.i.i = xor i64 %sub.i.i.peel.i.i.i.i.i, %conv73.i.peel.i.i.i.i.i
  %and4.i.i.peel.i.i.i.i.i = and i64 %xor3.i.i.peel.i.i.i.i.i, 0
  %cmp.i.i.peel.i.i.i.i.i = icmp slt i64 %and4.i.i.peel.i.i.i.i.i, 0
  %0 = trunc i64 %conv73.i.peel.i.i.i.i.i to i16
  %1 = or i16 0, %0
  %conv73.i.i.i.i.i.i = xor i64 0, 0
  %and.i.i.i.i.i.i.i = and i64 0, 0
  %xor2.i.i.i.i.i.i.i = or i64 %and.i.i.i.i.i.i.i, 0
  %sub.i.i.i.i.i.i.i = or i64 %xor2.i.i.i.i.i.i.i, 0
  %xor3.i.i.i.i.i.i.i = xor i64 %sub.i.i.i.i.i.i.i, %conv73.i.i.i.i.i.i
  %and4.i.i.i.i.i.i.i = and i64 %xor3.i.i.i.i.i.i.i, 0
  %cmp.i.i.i.i.i.i.i = icmp slt i64 %and4.i.i.i.i.i.i.i, 0
  %2 = trunc i64 %conv73.i.i.i.i.i.i to i16
  %3 = or i16 0, %2
  %4 = select i1 %cmp.i.i.i.i.i.i.i, i16 0, i16 %3
  %5 = select i1 %cmp.i.i.peel.i.i.i.i.i, i16 0, i16 %1
  %6 = zext i32 0 to i64
  %add.i.i.peel.i369.i.i.i.i = or i64 %6, 0
  %or.i.peel.i371.i.i.i.i = or i64 %add.i.i.peel.i369.i.i.i.i, 0
  %conv72.i.peel.i372.i.i.i.i = shl i64 %or.i.peel.i371.i.i.i.i, 0
  %sext.i.peel.i373.i.i.i.i = ashr i64 %conv72.i.peel.i372.i.i.i.i, 0
  %conv73.i.peel.i374.i.i.i.i = xor i64 %sext.i.peel.i373.i.i.i.i, 0
  %and.i.i.peel.i376.i.i.i.i = and i64 0, 0
  %xor2.i.i.peel.i377.i.i.i.i = or i64 %and.i.i.peel.i376.i.i.i.i, 0
  %sub.i.i.peel.i378.i.i.i.i = or i64 %xor2.i.i.peel.i377.i.i.i.i, 0
  %xor3.i.i.peel.i379.i.i.i.i = xor i64 %sub.i.i.peel.i378.i.i.i.i, %conv73.i.peel.i374.i.i.i.i
  %and4.i.i.peel.i380.i.i.i.i = and i64 %xor3.i.i.peel.i379.i.i.i.i, 0
  %cmp.i.i.peel.i381.i.i.i.i = icmp slt i64 %and4.i.i.peel.i380.i.i.i.i, 0
  %7 = trunc i64 %conv73.i.peel.i374.i.i.i.i to i16
  %8 = or i16 0, %7
  %9 = select i1 %cmp.i.i.peel.i381.i.i.i.i, i16 0, i16 %8
  %conv76.i.i.i.i.i.i = and i16 %4, %5
  %conv76.i.peel.i384.i.i.i.i = and i16 %conv76.i.i.i.i.i.i, %9
  %10 = zext i32 0 to i64
  %add.i.i.peel.i417.i.i.i.i = or i64 %10, 0
  %or.i.peel.i419.i.i.i.i = or i64 %add.i.i.peel.i417.i.i.i.i, 0
  %conv72.i.peel.i420.i.i.i.i = shl i64 %or.i.peel.i419.i.i.i.i, 0
  %sext.i.peel.i421.i.i.i.i = ashr i64 %conv72.i.peel.i420.i.i.i.i, 0
  %conv73.i.peel.i422.i.i.i.i = xor i64 %sext.i.peel.i421.i.i.i.i, 0
  %and.i.i.peel.i424.i.i.i.i = and i64 0, 0
  %xor2.i.i.peel.i425.i.i.i.i = or i64 %and.i.i.peel.i424.i.i.i.i, 0
  %sub.i.i.peel.i426.i.i.i.i = or i64 %xor2.i.i.peel.i425.i.i.i.i, 0
  %xor3.i.i.peel.i427.i.i.i.i = xor i64 %sub.i.i.peel.i426.i.i.i.i, %conv73.i.peel.i422.i.i.i.i
  %and4.i.i.peel.i428.i.i.i.i = and i64 %xor3.i.i.peel.i427.i.i.i.i, 0
  %cmp.i.i.peel.i429.i.i.i.i = icmp slt i64 %and4.i.i.peel.i428.i.i.i.i, 0
  %11 = trunc i64 %conv73.i.peel.i422.i.i.i.i to i16
  %12 = or i16 0, %11
  %13 = select i1 %cmp.i.i.peel.i429.i.i.i.i, i16 0, i16 %12
  %conv76.i.peel.i432.i.i.i.i = and i16 %conv76.i.peel.i384.i.i.i.i, %13
  ret i16 %conv76.i.peel.i432.i.i.i.i
}
```
```
opt: /root/llvm-project/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp:3180: llvm::slpvectorizer::BoUpSLP::ValueList& llvm::slpvectorizer::BoUpSLP::TreeEntry::getOperand(unsigned int): Assertion `OpIdx < Operands.size() && "Off bounds"' failed.
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
Stack dump:
0.	Program arguments: /opt/compiler-explorer/clang-assertions-trunk/bin/opt -o /app/output.s -S --passes=slp-vectorizer <source>
1.	Running pass "function(slp-vectorizer)" on module "<source>"
2.	Running pass "slp-vectorizer" on function "main"
 #0 0x0000000005016bb8 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x5016bb8)
 #1 0x00000000050142fc SignalHandler(int) Signals.cpp:0:0
 #2 0x00007955e2242520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520)
 #3 0x00007955e22969fc pthread_kill (/lib/x86_64-linux-gnu/libc.so.6+0x969fc)
 #4 0x00007955e2242476 gsignal (/lib/x86_64-linux-gnu/libc.so.6+0x42476)
 #5 0x00007955e22287f3 abort (/lib/x86_64-linux-gnu/libc.so.6+0x287f3)
 #6 0x00007955e222871b (/lib/x86_64-linux-gnu/libc.so.6+0x2871b)
 #7 0x00007955e2239e96 (/lib/x86_64-linux-gnu/libc.so.6+0x39e96)
 #8 0x000000000406f2b8 llvm::slpvectorizer::BoUpSLP::TreeEntry::getOperand(unsigned int) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x406f2b8)
 #9 0x00000000040f675e llvm::slpvectorizer::BoUpSLP::getEntryCost(llvm::slpvectorizer::BoUpSLP::TreeEntry const*, llvm::ArrayRef<llvm::Value*>, llvm::SmallPtrSetImpl<llvm::Value*>&)::'lambda0'(llvm::function_ref<llvm::InstructionCost (unsigned int)>, llvm::function_ref<llvm::InstructionCost (llvm::InstructionCost)>)::operator()(llvm::function_ref<llvm::InstructionCost (unsigned int)>, llvm::function_ref<llvm::InstructionCost (llvm::InstructionCost)>) const SLPVectorizer.cpp:0:0
#10 0x00000000040f74aa llvm::slpvectorizer::BoUpSLP::getEntryCost(llvm::slpvectorizer::BoUpSLP::TreeEntry const*, llvm::ArrayRef<llvm::Value*>, llvm::SmallPtrSetImpl<llvm::Value*>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x40f74aa)
#11 0x0000000004114641 llvm::slpvectorizer::BoUpSLP::getTreeCost(llvm::ArrayRef<llvm::Value*>) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x4114641)
#12 0x00000000041198c8 (anonymous namespace)::HorizontalReduction::tryToReduce(llvm::slpvectorizer::BoUpSLP&, llvm::DataLayout const&, llvm::TargetTransformInfo*, llvm::TargetLibraryInfo const&) SLPVectorizer.cpp:0:0
#13 0x000000000411bfe1 llvm::SLPVectorizerPass::vectorizeHorReduction(llvm::PHINode*, llvm::Instruction*, llvm::BasicBlock*, llvm::slpvectorizer::BoUpSLP&, llvm::TargetTransformInfo*, llvm::SmallVectorImpl<llvm::WeakTrackingVH>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x411bfe1)
#14 0x0000000004120876 llvm::SLPVectorizerPass::vectorizeRootInstruction(llvm::PHINode*, llvm::Instruction*, llvm::BasicBlock*, llvm::slpvectorizer::BoUpSLP&, llvm::TargetTransformInfo*) (.constprop.0) SLPVectorizer.cpp:0:0
#15 0x0000000004124e16 llvm::SLPVectorizerPass::vectorizeChainsInBlock(llvm::BasicBlock*, llvm::slpvectorizer::BoUpSLP&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x4124e16)
#16 0x000000000412b2e1 llvm::SLPVectorizerPass::runImpl(llvm::Function&, llvm::ScalarEvolution*, llvm::TargetTransformInfo*, llvm::TargetLibraryInfo*, llvm::AAResults*, llvm::LoopInfo*, llvm::DominatorTree*, llvm::AssumptionCache*, llvm::DemandedBits*, llvm::OptimizationRemarkEmitter*) (.part.0) SLPVectorizer.cpp:0:0
#17 0x000000000412bf6b llvm::SLPVectorizerPass::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x412bf6b)
#18 0x0000000002f0034e llvm::detail::PassModel<llvm::Function, llvm::SLPVectorizerPass, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x2f0034e)
#19 0x0000000004e18ff8 llvm::PassManager<llvm::Function, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x4e18ff8)
#20 0x0000000000e06a0e llvm::detail::PassModel<llvm::Function, llvm::PassManager<llvm::Function, llvm::AnalysisManager<llvm::Function>>, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0xe06a0e)
#21 0x0000000004e17a1e llvm::ModuleToFunctionPassAdaptor::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x4e17a1e)
#22 0x0000000000e0621e llvm::detail::PassModel<llvm::Module, llvm::ModuleToFunctionPassAdaptor, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0xe0621e)
#23 0x0000000004e17450 llvm::PassManager<llvm::Module, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x4e17450)
#24 0x00000000008fce52 llvm::runPassPipeline(llvm::StringRef, llvm::Module&, llvm::TargetMachine*, llvm::TargetLibraryInfoImpl*, llvm::ToolOutputFile*, llvm::ToolOutputFile*, llvm::ToolOutputFile*, llvm::StringRef, llvm::ArrayRef<llvm::PassPlugin>, llvm::ArrayRef<std::function<void (llvm::PassBuilder&)>>, llvm::opt_tool::OutputKind, llvm::opt_tool::VerifierKind, bool, bool, bool, bool, bool, bool, bool) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x8fce52)
#25 0x00000000008efcdc optMain (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x8efcdc)
#26 0x00007955e2229d90 (/lib/x86_64-linux-gnu/libc.so.6+0x29d90)
#27 0x00007955e2229e40 __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e40)
#28 0x00000000008e77ae _start (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x8e77ae)
Program terminated with signal: SIGSEGV
Compiler returned: 139
```


Fix patch:
commit f564a48f0ea4d2100c0cadfa6e6f20f97244025e
Author: Alexey Bataev <a.bataev@outlook.com>
Date:   Mon Sep 16 09:19:42 2024 -0700

    [SLP]Fix PR108700: correctly identify id of the operand node
    
    If the operand node for truncs is not created during construction, but
    one of the previous ones is reused instead, need to correctly identify
    its index, to correctly emit the code.
    
    Fixes https://github.com/llvm/llvm-project/issues/108700

diff --git a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
index 5f2bf082fb87..282bb8eac7e2 100644
--- a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
+++ b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
@@ -7481,7 +7481,16 @@ void BoUpSLP::buildTree_rec(ArrayRef<Value *> VL, unsigned Depth,
                 PrevMaxBW),
             std::min<unsigned>(DL->getTypeSizeInBits(VL0->getType()),
                                PrevMinBW));
-        ExtraBitWidthNodes.insert(VectorizableTree.size() + 1);
+      }
+      TreeEntry *TE = newTreeEntry(VL, Bundle /*vectorized*/, S, UserTreeIdx,
+                                   ReuseShuffleIndices);
+      LLVM_DEBUG(dbgs() << "SLP: added a vector of casts.\n");
+
+      TE->setOperandsInOrder();
+      for (unsigned I : seq<unsigned>(0, VL0->getNumOperands()))
+        buildTree_rec(TE->getOperand(I), Depth + 1, {TE, I});
+      if (ShuffleOrOp == Instruction::Trunc) {
+        ExtraBitWidthNodes.insert(getOperandEntry(TE, 0)->Idx);
       } else if (ShuffleOrOp == Instruction::SIToFP ||
                  ShuffleOrOp == Instruction::UIToFP) {
         unsigned NumSignBits =
@@ -7492,15 +7501,8 @@ void BoUpSLP::buildTree_rec(ArrayRef<Value *> VL, unsigned Depth,
         }
         if (NumSignBits * 2 >=
             DL->getTypeSizeInBits(VL0->getOperand(0)->getType()))
-          ExtraBitWidthNodes.insert(VectorizableTree.size() + 1);
+          ExtraBitWidthNodes.insert(getOperandEntry(TE, 0)->Idx);
       }
-      TreeEntry *TE = newTreeEntry(VL, Bundle /*vectorized*/, S, UserTreeIdx,
-                                   ReuseShuffleIndices);
-      LLVM_DEBUG(dbgs() << "SLP: added a vector of casts.\n");
-
-      TE->setOperandsInOrder();
-      for (unsigned I : seq<unsigned>(0, VL0->getNumOperands()))
-        buildTree_rec(TE->getOperand(I), Depth + 1, {TE, I});
       return;
     }
     case Instruction::ICmp:


Regression tests:
file: llvm/test/Transforms/SLPVectorizer/X86/minbitwidth-user-not-min.ll
commands: opt -S --passes=slp-vectorizer -mtriple=x86_64-unknown-linux-gnu < %s
tests: test_name: test
test_body: define void @test(ptr %block, ptr noalias %pixels, i1 %b) {
entry:
  %0 = load i16, ptr %block, align 2
  %tobool.not.i78 = icmp ult i16 %0, 0
  %conv.i80 = sext i1 true to i8
  %conv1.i81 = trunc i16 %0 to i8
  %retval.0.i82 = select i1 %tobool.not.i78, i8 %conv1.i81, i8 %conv.i80
  store i8 %retval.0.i82, ptr %pixels, align 1
  %arrayidx2 = getelementptr i8, ptr %block, i64 2
  %1 = load i16, ptr %arrayidx2, align 2
  %tobool.not.i73 = icmp ult i16 %1, 0
  %conv.i75 = sext i1 %b to i8
  %conv1.i76 = trunc i16 %1 to i8
  %retval.0.i77 = select i1 %tobool.not.i73, i8 %conv1.i76, i8 %conv.i75
  %arrayidx5 = getelementptr i8, ptr %pixels, i64 1
  store i8 %retval.0.i77, ptr %arrayidx5, align 1
  %arrayidx6 = getelementptr i8, ptr %block, i64 4
  %2 = load i16, ptr %arrayidx6, align 2
  %tobool.not.i68 = icmp ult i16 %2, 0
  %conv.i70 = sext i1 false to i8
  %conv1.i71 = trunc i16 %2 to i8
  %retval.0.i72 = select i1 %tobool.not.i68, i8 %conv1.i71, i8 %conv.i70
  %arrayidx9 = getelementptr i8, ptr %pixels, i64 2
  store i8 %retval.0.i72, ptr %arrayidx9, align 1
  %arrayidx10 = getelementptr i8, ptr %block, i64 6
  %3 = load i16, ptr %arrayidx10, align 2
  %tobool.not.i63 = icmp ult i16 %3, 0
  %conv.i65 = sext i1 false to i8
  %conv1.i66 = trunc i16 %3 to i8
  %retval.0.i67 = select i1 %tobool.not.i63, i8 %conv1.i66, i8 %conv.i65
  %arrayidx13 = getelementptr i8, ptr %pixels, i64 3
  store i8 %retval.0.i67, ptr %arrayidx13, align 1
  ret void
}

file: llvm/test/Transforms/SLPVectorizer/X86/trunc-node-reused.ll
commands: opt -S --passes=slp-vectorizer -mtriple=x86_64-unknown-linux-gnu < %s
tests: test_name: test
test_body: define i16 @test() {
entry:
  %conv73 = xor i64 0, 0
  %and.i = and i64 0, 0
  %xor2.i = or i64 %and.i, 0
  %sub.i = or i64 %xor2.i, 0
  %xor3.i = xor i64 %sub.i, %conv73
  %and4.i = and i64 %xor3.i, 0
  %cmp.i = icmp slt i64 %and4.i, 0
  %0 = trunc i64 %conv73 to i16
  %1 = or i16 0, %0
  %conv73i = xor i64 0, 0
  %andi.i = and i64 0, 0
  %xor2i.i = or i64 %andi.i, 0
  %subi.i = or i64 %xor2i.i, 0
  %xor3i.i = xor i64 %subi.i, %conv73i
  %and4i.i = and i64 %xor3i.i, 0
  %cmpi.i = icmp slt i64 %and4i.i, 0
  %2 = trunc i64 %conv73i to i16
  %3 = or i16 0, %2
  %4 = select i1 %cmpi.i, i16 0, i16 %3
  %5 = select i1 %cmp.i, i16 0, i16 %1
  %6 = zext i32 0 to i64
  %add.ip = or i64 %6, 0
  %orp = or i64 %add.ip, 0
  %conv72p = shl i64 %orp, 0
  %sextp = ashr i64 %conv72p, 0
  %conv73p = xor i64 %sextp, 0
  %and.ip = and i64 0, 0
  %xor2.ip = or i64 %and.ip, 0
  %sub.ip = or i64 %xor2.ip, 0
  %xor3.ip = xor i64 %sub.ip, %conv73p
  %and4.ip = and i64 %xor3.ip, 0
  %cmp.ip = icmp slt i64 %and4.ip, 0
  %7 = trunc i64 %conv73p to i16
  %8 = or i16 0, %7
  %9 = select i1 %cmp.ip, i16 0, i16 %8
  %conv76i = and i16 %4, %5
  %conv76p = and i16 %conv76i, %9
  %10 = zext i32 0 to i64
  %add.ip1 = or i64 %10, 0
  %orp1 = or i64 %add.ip1, 0
  %conv72p1 = shl i64 %orp1, 0
  %sextp1 = ashr i64 %conv72p1, 0
  %conv73p1 = xor i64 %sextp1, 0
  %and.ip1 = and i64 0, 0
  %xor2.ip1 = or i64 %and.ip1, 0
  %sub.ip1 = or i64 %xor2.ip1, 0
  %xor3.ip1 = xor i64 %sub.ip1, %conv73p1
  %and4.ip1 = and i64 %xor3.ip1, 0
  %cmp.ip1 = icmp slt i64 %and4.ip1, 0
  %11 = trunc i64 %conv73p1 to i16
  %12 = or i16 0, %11
  %13 = select i1 %cmp.ip1, i16 0, i16 %12
  %conv76p2 = and i16 %conv76p, %13
  ret i16 %conv76p2
}