Bug type: crash
Component: SLPVectorizer
Bug location: llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp: BoUpSLP::vectorizeTree
tryToReduce
Issue title: Assertion `(I->use_empty() || all_of(I->uses(), [&](Use &U) { return isDeleted( cast<Instruction>(U.getUser())); })) && "trying to erase instruction with users."' failed.
Labels: llvm:SLPVectorizer
crash
Reported by: TatyanaDoubts

To reproduce run the following test with -passes=slp-vectorizer
```
; ModuleID = './reduced.ll'
source_filename = "./reduced.ll"
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-ni:1-p2:32:8:8:32-ni:2"
target triple = "x86_64-unknown-linux-gnu"

define void @wombat(i32 %arg) gc "statepoint-example" {
bb:
  br label %bb1

bb1:                                              ; preds = %bb1, %bb
  %phi = phi i32 [ 0, %bb ], [ %add27, %bb1 ]
  %phi2 = phi i32 [ 0, %bb ], [ %add24, %bb1 ]
  %phi3 = phi i32 [ 0, %bb ], [ %xor26, %bb1 ]
  %add = add i32 %phi2, 0
  %add4 = add i32 %phi2, 0
  %xor = xor i32 %add, %add4
  %add5 = add i32 %phi, 0
  %add6 = add i32 %phi2, 0
  %add7 = add i32 %phi2, 0
  %xor8 = xor i32 %add6, %xor
  %xor9 = xor i32 %xor8, %add5
  %xor10 = xor i32 %xor9, %add7
  %add11 = add i32 %phi, 0
  %add12 = add i32 %phi2, 0
  %add13 = add i32 %phi2, 0
  %xor14 = xor i32 %add12, %xor10
  %xor15 = xor i32 %xor14, %add11
  %xor16 = xor i32 %xor15, %add13
  %add17 = add i32 %phi, 0
  %add18 = add i32 %phi2, 0
  %add19 = add i32 %phi2, 0
  %xor20 = xor i32 %add18, %xor16
  %xor21 = xor i32 %xor20, %add17
  %xor22 = xor i32 %xor21, %add19
  %add23 = add i32 %phi2, 0
  %add24 = add i32 %arg, 0
  %xor25 = xor i32 %add23, %xor22
  %xor26 = xor i32 %xor25, %add24
  %add27 = add i32 1, 0
  %icmp = icmp ult i32 %add27, 0
  br label %bb1
}
```
Reproducer: https://godbolt.org/z/77hKre61P

Stack dump:
```
opt: /root/llvm-project/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp:2663: void llvm::slpvectorizer::BoUpSLP::removeInstructionsAndOperands(llvm::ArrayRef<U*>) [with T = llvm::Instruction]: Assertion `(I->use_empty() || all_of(I->uses(), [&](Use &U) { return isDeleted( cast<Instruction>(U.getUser())); })) && "trying to erase instruction with users."' 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 "wombat"
 #0 0x00000000050106b8 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x50106b8)
 #1 0x000000000500ddfc SignalHandler(int) Signals.cpp:0:0
 #2 0x0000737fffa42520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520)
 #3 0x0000737fffa969fc pthread_kill (/lib/x86_64-linux-gnu/libc.so.6+0x969fc)
 #4 0x0000737fffa42476 gsignal (/lib/x86_64-linux-gnu/libc.so.6+0x42476)
 #5 0x0000737fffa287f3 abort (/lib/x86_64-linux-gnu/libc.so.6+0x287f3)
 #6 0x0000737fffa2871b (/lib/x86_64-linux-gnu/libc.so.6+0x2871b)
 #7 0x0000737fffa39e96 (/lib/x86_64-linux-gnu/libc.so.6+0x39e96)
 #8 0x00000000040b91cf void llvm::slpvectorizer::BoUpSLP::removeInstructionsAndOperands<llvm::Instruction>(llvm::ArrayRef<llvm::Instruction*>) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x40b91cf)
 #9 0x000000000410b80a llvm::slpvectorizer::BoUpSLP::vectorizeTree(llvm::MapVector<llvm::Value*, llvm::SmallVector<llvm::Instruction*, 2u>, llvm::DenseMap<llvm::Value*, unsigned int, llvm::DenseMapInfo<llvm::Value*, void>, llvm::detail::DenseMapPair<llvm::Value*, unsigned int>>, llvm::SmallVector<std::pair<llvm::Value*, llvm::SmallVector<llvm::Instruction*, 2u>>, 0u>> const&, llvm::SmallVectorImpl<std::pair<llvm::Value*, llvm::Value*>>&, llvm::Instruction*) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x410b80a)
#10 0x0000000004115bed (anonymous namespace)::HorizontalReduction::tryToReduce(llvm::slpvectorizer::BoUpSLP&, llvm::DataLayout const&, llvm::TargetTransformInfo*, llvm::TargetLibraryInfo const&) SLPVectorizer.cpp:0:0
#11 0x0000000004118321 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+0x4118321)
#12 0x000000000411cb18 llvm::SLPVectorizerPass::vectorizeRootInstruction(llvm::PHINode*, llvm::Instruction*, llvm::BasicBlock*, llvm::slpvectorizer::BoUpSLP&, llvm::TargetTransformInfo*) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x411cb18)
#13 0x00000000041212fa llvm::SLPVectorizerPass::vectorizeChainsInBlock(llvm::BasicBlock*, llvm::slpvectorizer::BoUpSLP&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x41212fa)
#14 0x0000000004127719 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
#15 0x00000000041283cb llvm::SLPVectorizerPass::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x41283cb)
#16 0x0000000002efda4e 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+0x2efda4e)
#17 0x0000000004e12948 llvm::PassManager<llvm::Function, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x4e12948)
#18 0x0000000000e06f8e 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+0xe06f8e)
#19 0x0000000004e1136e llvm::ModuleToFunctionPassAdaptor::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x4e1136e)
#20 0x0000000000e0679e 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+0xe0679e)
#21 0x0000000004e10da0 llvm::PassManager<llvm::Module, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x4e10da0)
#22 0x00000000008fb642 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+0x8fb642)
#23 0x00000000008ee4cc optMain (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x8ee4cc)
#24 0x0000737fffa29d90 (/lib/x86_64-linux-gnu/libc.so.6+0x29d90)
#25 0x0000737fffa29e40 __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e40)
#26 0x00000000008e5f9e _start (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x8e5f9e)
Program terminated with signal: SIGSEGV
Compiler returned: 139
```


Fix patch:
commit 1e3536ef3141d6429f3616af624b81b6d6ab2959
Author: Alexey Bataev <a.bataev@outlook.com>
Date:   Fri Sep 13 15:40:22 2024 -0700

    [SLP]Fix PR108620: Need to check, if the reduced value was transformed
    
    Before trying to include the scalar into the list of
    ExternallyUsedValues, need to check, if it was transformed in previous
    iteration and use the transformed value, not the original one, to avoid
    compiler crash when building external uses.
    
    Fixes https://github.com/llvm/llvm-project/issues/108620

diff --git a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
index 0afe02fc08ff..5f2bf082fb87 100644
--- a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
+++ b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
@@ -1283,11 +1283,8 @@ public:
   /// Vectorize the tree but with the list of externally used values \p
   /// ExternallyUsedValues. Values in this MapVector can be replaced but the
   /// generated extractvalue instructions.
-  /// \param ReplacedExternals containd list of replaced external values
-  /// {scalar, replace} after emitting extractelement for external uses.
   Value *
   vectorizeTree(const ExtraValueToDebugLocsMap &ExternallyUsedValues,
-                SmallVectorImpl<std::pair<Value *, Value *>> &ReplacedExternals,
                 Instruction *ReductionRoot = nullptr);
 
   /// \returns the cost incurred by unwanted spills and fills, caused by
@@ -14222,14 +14219,12 @@ Value *BoUpSLP::vectorizeTree(TreeEntry *E, bool PostponedPHIs) {
 
 Value *BoUpSLP::vectorizeTree() {
   ExtraValueToDebugLocsMap ExternallyUsedValues;
-  SmallVector<std::pair<Value *, Value *>> ReplacedExternals;
-  return vectorizeTree(ExternallyUsedValues, ReplacedExternals);
+  return vectorizeTree(ExternallyUsedValues);
 }
 
-Value *BoUpSLP::vectorizeTree(
-    const ExtraValueToDebugLocsMap &ExternallyUsedValues,
-    SmallVectorImpl<std::pair<Value *, Value *>> &ReplacedExternals,
-    Instruction *ReductionRoot) {
+Value *
+BoUpSLP::vectorizeTree(const ExtraValueToDebugLocsMap &ExternallyUsedValues,
+                       Instruction *ReductionRoot) {
   // All blocks must be scheduled before any instructions are inserted.
   for (auto &BSIter : BlocksSchedules) {
     scheduleBlock(BSIter.second.get());
@@ -14373,6 +14368,7 @@ Value *BoUpSLP::vectorizeTree(
   SmallDenseSet<Value *, 4> UsedInserts;
   DenseMap<std::pair<Value *, Type *>, Value *> VectorCasts;
   SmallDenseSet<Value *, 4> ScalarsWithNullptrUser;
+  SmallDenseSet<ExtractElementInst *, 4> IgnoredExtracts;
   // Extract all of the elements with the external uses.
   for (const auto &ExternalUse : ExternalUses) {
     Value *Scalar = ExternalUse.Scalar;
@@ -14426,11 +14422,16 @@ Value *BoUpSLP::vectorizeTree(
           if (ReplaceInst) {
             // Leave the instruction as is, if it cheaper extracts and all
             // operands are scalar.
-            auto *CloneInst = Inst->clone();
-            CloneInst->insertBefore(Inst);
-            if (Inst->hasName())
-              CloneInst->takeName(Inst);
-            Ex = CloneInst;
+            if (auto *EE = dyn_cast<ExtractElementInst>(Inst)) {
+              IgnoredExtracts.insert(EE);
+              Ex = EE;
+            } else {
+              auto *CloneInst = Inst->clone();
+              CloneInst->insertBefore(Inst);
+              if (Inst->hasName())
+                CloneInst->takeName(Inst);
+              Ex = CloneInst;
+            }
           } else if (auto *ES = dyn_cast<ExtractElementInst>(Scalar);
                      ES && isa<Instruction>(Vec)) {
             Value *V = ES->getVectorOperand();
@@ -14530,8 +14531,12 @@ Value *BoUpSLP::vectorizeTree(
       }
       Value *NewInst = ExtractAndExtendIfNeeded(Vec);
       // Required to update internally referenced instructions.
-      Scalar->replaceAllUsesWith(NewInst);
-      ReplacedExternals.emplace_back(Scalar, NewInst);
+      if (Scalar != NewInst) {
+        assert((!isa<ExtractElementInst>(Scalar) ||
+                !IgnoredExtracts.contains(cast<ExtractElementInst>(Scalar))) &&
+               "Extractelements should not be replaced.");
+        Scalar->replaceAllUsesWith(NewInst);
+      }
       continue;
     }
 
@@ -14757,6 +14762,9 @@ Value *BoUpSLP::vectorizeTree(
       if (Entry->getOpcode() == Instruction::GetElementPtr &&
           !isa<GetElementPtrInst>(Scalar))
         continue;
+      if (auto *EE = dyn_cast<ExtractElementInst>(Scalar);
+          EE && IgnoredExtracts.contains(EE))
+        continue;
 #ifndef NDEBUG
       Type *Ty = Scalar->getType();
       if (!Ty->isVoidTy()) {
@@ -17660,7 +17668,6 @@ public:
     // because of the vectorization.
     DenseMap<Value *, WeakTrackingVH> TrackedVals(ReducedVals.size() *
                                                   ReducedVals.front().size());
-    SmallVector<std::pair<Value *, Value *>> ReplacedExternals;
 
     // The compare instruction of a min/max is the insertion point for new
     // instructions and may be replaced with a new compare instruction.
@@ -17956,6 +17963,8 @@ public:
           if (Cnt >= Pos && Cnt < Pos + ReduxWidth)
             continue;
           Value *RdxVal = Candidates[Cnt];
+          if (auto It = TrackedVals.find(RdxVal); It != TrackedVals.end())
+            RdxVal = It->second;
           if (!Visited.insert(RdxVal).second)
             continue;
           // Check if the scalar was vectorized as part of the vectorization
@@ -18024,8 +18033,8 @@ public:
           InsertPt = GetCmpForMinMaxReduction(RdxRootInst);
 
         // Vectorize a tree.
-        Value *VectorizedRoot = V.vectorizeTree(LocalExternallyUsedValues,
-                                                ReplacedExternals, InsertPt);
+        Value *VectorizedRoot =
+            V.vectorizeTree(LocalExternallyUsedValues, InsertPt);
 
         Builder.SetInsertPoint(InsertPt);
 


Regression tests:
file: llvm/test/Transforms/SLPVectorizer/X86/reduced-val-extracted-and-externally-used.ll
commands: opt -S --passes=slp-vectorizer -mtriple=x86_64-unknown-linux-gnu < %s
tests: test_name: test
test_body: define void @test(i32 %arg) {
bb:
  br label %bb1

bb1:                                              ; preds = %bb1, %bb
  %phi = phi i32 [ 0, %bb ], [ %add27, %bb1 ]
  %phi2 = phi i32 [ 0, %bb ], [ %add24, %bb1 ]
  %phi3 = phi i32 [ 0, %bb ], [ %xor26, %bb1 ]
  %add = add i32 %phi2, 0
  %add4 = add i32 %phi2, 0
  %xor = xor i32 %add, %add4
  %add5 = add i32 %phi, 0
  %add6 = add i32 %phi2, 0
  %add7 = add i32 %phi2, 0
  %xor8 = xor i32 %add6, %xor
  %xor9 = xor i32 %xor8, %add5
  %xor10 = xor i32 %xor9, %add7
  %add11 = add i32 %phi, 0
  %add12 = add i32 %phi2, 0
  %add13 = add i32 %phi2, 0
  %xor14 = xor i32 %add12, %xor10
  %xor15 = xor i32 %xor14, %add11
  %xor16 = xor i32 %xor15, %add13
  %add17 = add i32 %phi, 0
  %add18 = add i32 %phi2, 0
  %add19 = add i32 %phi2, 0
  %xor20 = xor i32 %add18, %xor16
  %xor21 = xor i32 %xor20, %add17
  %xor22 = xor i32 %xor21, %add19
  %add23 = add i32 %phi2, 0
  %add24 = add i32 %arg, 0
  %xor25 = xor i32 %add23, %xor22
  %xor26 = xor i32 %xor25, %add24
  %add27 = add i32 1, 0
  %icmp = icmp ult i32 %add27, 0
  br label %bb1
}