Bug type: crash
Component: DFAJumpThreading
Bug location: llvm/lib/Transforms/Scalar/DFAJumpThreading.cpp: unfold
Issue title: dfa-jump-threading crashing with "Assertion `Idx >= 0 && "Invalid basic block argument to remove!"' failed"
Labels: crash
llvm:transforms
generated by fuzzer
Reported by: mikaelholmen

llvm commit: 2ab02b6f561d
Reproduce with:
```opt -passes="dfa-jump-threading" bbi-110818.ll -o /dev/null```
Result:
```
opt: ../include/llvm/IR/Instructions.h:2795: Value *llvm::PHINode::removeIncomingValue(const BasicBlock *, bool): Assertion `Idx >= 0 && "Invalid basic block argument to remove!"' failed.
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace and instructions to reproduce the bug.
Stack dump:
0.	Program arguments: build-all/bin/opt -passes=dfa-jump-threading bbi-110818.ll -o /dev/null
1.	Running pass "function(dfa-jump-threading)" on module "bbi-110818.ll"
2.	Running pass "dfa-jump-threading" on function "func_2"
 #0 0x000055a37c34f4a6 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (build-all/bin/opt+0x4b714a6)
 #1 0x000055a37c34ca35 llvm::sys::RunSignalHandlers() (build-all/bin/opt+0x4b6ea35)
 #2 0x000055a37c350629 SignalHandler(int, siginfo_t*, void*) Signals.cpp:0:0
 #3 0x00007f3c662e2990 __restore_rt (/lib64/libpthread.so.0+0x12990)
 #4 0x00007f3c63c8252f raise (/lib64/libc.so.6+0x4e52f)
 #5 0x00007f3c63c55e65 abort (/lib64/libc.so.6+0x21e65)
 #6 0x00007f3c63c55d39 _nl_load_domain.cold.0 (/lib64/libc.so.6+0x21d39)
 #7 0x00007f3c63c7ae86 (/lib64/libc.so.6+0x46e86)
 #8 0x000055a37dc07096 (anonymous namespace)::DFAJumpThreading::run(llvm::Function&) DFAJumpThreading.cpp:0:0
 #9 0x000055a37dc00b60 llvm::DFAJumpThreadingPass::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (build-all/bin/opt+0x6422b60)
#10 0x000055a37d81b81d llvm::detail::PassModel<llvm::Function, llvm::DFAJumpThreadingPass, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) PassBuilderPipelines.cpp:0:0
#11 0x000055a37c56c965 llvm::PassManager<llvm::Function, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (build-all/bin/opt+0x4d8e965)
#12 0x000055a37d81bc5d llvm::detail::PassModel<llvm::Function, llvm::PassManager<llvm::Function, llvm::AnalysisManager<llvm::Function>>, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) PassBuilderPipelines.cpp:0:0
#13 0x000055a37c57126e llvm::ModuleToFunctionPassAdaptor::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (build-all/bin/opt+0x4d9326e)
#14 0x000055a37d7aa6ad llvm::detail::PassModel<llvm::Module, llvm::ModuleToFunctionPassAdaptor, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) NewPMDriver.cpp:0:0
#15 0x000055a37c56b635 llvm::PassManager<llvm::Module, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (build-all/bin/opt+0x4d8d635)
#16 0x000055a37d7a33f0 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, bool) (build-all/bin/opt+0x5fc53f0)
#17 0x000055a37c2f0718 optMain (build-all/bin/opt+0x4b12718)
#18 0x00007f3c63c6e7e5 __libc_start_main (/lib64/libc.so.6+0x3a7e5)
#19 0x000055a37c2ee12e _start (build-all/bin/opt+0x4b1012e)
Abort (core dumped)
```

[bbi-110818.ll.gz](https://github.com/user-attachments/files/22488497/bbi-110818.ll.gz)

Comments: author: mikaelholmen
body: This crash seems to be related to https://github.com/llvm/llvm-project/pull/96127 and the fix (https://github.com/llvm/llvm-project/pull/109511) for a crash I reported on that PR in https://github.com/llvm/llvm-project/pull/96127#issuecomment-2324800391

Fix patch:
commit a05e004b285af92f9bcef12d5ab5537c36002c13
Author: Hongyu Chen <xxs_chy@outlook.com>
Date:   Wed Oct 1 22:06:08 2025 +0800

    [DFAJumpThreading] Unfold select to the incoming block of phi user (#160987)
    
    Fixes #160250
    We previously assumed the select to unfold is defined in the incoming
    block of phi user, as `isValidSelectInst` filters other cases at the
    initial stage. However, the selects not defined in the incoming block
    may occur after unfolding the arms of the unfolded select.
    This patch sinks the select into the incoming block of the phi user and
    unfolds it at the incoming block.

diff --git a/llvm/lib/Transforms/Scalar/DFAJumpThreading.cpp b/llvm/lib/Transforms/Scalar/DFAJumpThreading.cpp
index 944b253e0f5e..e9a3e983bc1e 100644
--- a/llvm/lib/Transforms/Scalar/DFAJumpThreading.cpp
+++ b/llvm/lib/Transforms/Scalar/DFAJumpThreading.cpp
@@ -190,12 +190,12 @@ void unfold(DomTreeUpdater *DTU, LoopInfo *LI, SelectInstToUnfold SIToUnfold,
             std::vector<BasicBlock *> *NewBBs) {
   SelectInst *SI = SIToUnfold.getInst();
   PHINode *SIUse = SIToUnfold.getUse();
-  BasicBlock *StartBlock = SI->getParent();
+  assert(SI->hasOneUse());
+  // The select may come indirectly, instead of from where it is defined.
+  BasicBlock *StartBlock = SIUse->getIncomingBlock(*SI->use_begin());
   BranchInst *StartBlockTerm =
       dyn_cast<BranchInst>(StartBlock->getTerminator());
-
   assert(StartBlockTerm);
-  assert(SI->hasOneUse());
 
   if (StartBlockTerm->isUnconditional()) {
     BasicBlock *EndBlock = StartBlock->getUniqueSuccessor();
@@ -332,7 +332,7 @@ void unfold(DomTreeUpdater *DTU, LoopInfo *LI, SelectInstToUnfold SIToUnfold,
   }
 
   // Preserve loop info
-  if (Loop *L = LI->getLoopFor(SI->getParent())) {
+  if (Loop *L = LI->getLoopFor(StartBlock)) {
     for (BasicBlock *NewBB : *NewBBs)
       L->addBasicBlockToLoop(NewBB, *LI);
   }
@@ -533,6 +533,8 @@ private:
       return false;
 
     // Only fold the select coming from directly where it is defined.
+    // TODO: We have dealt with the select coming indirectly now. This
+    // constraint can be relaxed.
     PHINode *PHIUser = dyn_cast<PHINode>(SIUse);
     if (PHIUser && PHIUser->getIncomingBlock(*SI->use_begin()) != SIBB)
       return false;


Regression tests:
file: llvm/test/Transforms/DFAJumpThreading/dfa-jump-threading-transform.ll
commands: opt -S -passes=dfa-jump-threading %s
tests: test_name: pr106083_select_dead_uses
test_body: define void @pr106083_select_dead_uses(i1 %cmp1, i1 %not, ptr %p) {
bb:
  %spec.select = select i1 %cmp1, i32 0, i32 1
  br label %.loopexit6

.loopexit6:                                       ; preds = %select.unfold, %bb
  br i1 %not, label %select.unfold, label %bb1

bb1:                                              ; preds = %.loopexit6
  %i = load i32, ptr %p, align 4
  %not2 = icmp eq i32 0, 0
  %spec.select7 = select i1 %not2, i32 %spec.select, i32 0
  br label %select.unfold

select.unfold:                                    ; preds = %bb1, %.loopexit6
  %_2 = phi i32 [ 0, %.loopexit6 ], [ %spec.select7, %bb1 ]
  switch i32 %_2, label %bb2 [
    i32 0, label %.preheader.preheader
    i32 1, label %.loopexit6
  ]

.preheader.preheader:                             ; preds = %select.unfold
  ret void

bb2:                                              ; preds = %select.unfold
  unreachable
}

test_name: pr106083_invalidBBarg_fold
test_body: define void @pr106083_invalidBBarg_fold(i1 %cmp1, i1 %cmp2, i1 %not, ptr %d) {
bb:
  %sel = select i1 %cmp1, i32 0, i32 1
  br label %BB1

BB1:                                              ; preds = %BB1.backedge, %BB7, %bb
  %i = phi i16 [ 0, %BB1.backedge ], [ 0, %bb ], [ 1, %BB7 ]
  br i1 %not, label %BB7, label %BB2

BB2:                                              ; preds = %BB1
  store i16 0, ptr %d, align 2
  %spec.select = select i1 %cmp2, i32 %sel, i32 0
  br label %BB7

BB7:                                              ; preds = %BB2, %BB1
  %d.promoted4 = phi i16 [ 0, %BB1 ], [ 1, %BB2 ]
  %_3 = phi i32 [ 0, %BB1 ], [ %spec.select, %BB2 ]
  switch i32 %_3, label %BB1.backedge [
    i32 0, label %BB1
    i32 1, label %BB8
  ]

BB1.backedge:                                     ; preds = %BB7
  br label %BB1

BB8:                                              ; preds = %BB7
  ret void
}

file: llvm/test/Transforms/DFAJumpThreading/dfa-unfold-select.ll
commands: opt -S -passes=dfa-jump-threading -dfa-early-exit-heuristic=false %s
tests: test_name: pr160250
test_body: define i16 @pr160250() {
entry:
  %.5 = select i1 false, i32 0, i32 0
  %.6 = select i1 false, i32 0, i32 0
  br label %for.cond48

for.cond48:                                       ; preds = %for.cond48.backedge, %entry
  br i1 false, label %cleanup87, label %if.else

if.else:                                          ; preds = %for.cond48
  %spec.select1 = select i1 false, i32 %.5, i32 0
  %spec.select = select i1 false, i32 %.6, i32 %spec.select1
  br label %cleanup87

cleanup87:                                        ; preds = %if.else, %for.cond48
  %cleanup.dest.slot.3 = phi i32 [ 0, %for.cond48 ], [ %spec.select, %if.else ]
  switch i32 %cleanup.dest.slot.3, label %for.cond48.backedge [
    i32 0, label %for.cond48.backedge
    i32 1, label %for.cond48.backedge
  ]

for.cond48.backedge:                              ; preds = %cleanup87, %cleanup87, %cleanup87
  br label %for.cond48
}