Bug type: crash
Component: ScalarEvolution
Bug location: llvm/lib/Analysis/ScalarEvolution.cpp: ScalarEvolution::visitAndClearUsers
Issue title: Assertion Failure in `getSCEV` during loop unroll
Labels: llvm:crash
llvm:SCEV
Reported by: Nirhar

```
; ModuleID = '<stdin>'
source_filename = "<stdin>"
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 @ham() {
bb:
  br label %bb1

bb1:                                              ; preds = %bb3, %bb
  %phi = phi i32 [ 0, %bb ], [ %phi4, %bb3 ]
  br label %bb5

bb2:                                              ; preds = %bb7
  %call = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 %add8, i32 -2)
  %extractvalue = extractvalue { i32, i1 } %call, 0
  br label %bb3

bb3:                                              ; preds = %bb3, %bb2
  %phi4 = phi i32 [ %add, %bb3 ], [ 0, %bb2 ]
  %add = add i32 %extractvalue, %phi4
  br i1 false, label %bb3, label %bb1

bb5:                                              ; preds = %bb7, %bb1
  %phi6 = phi i32 [ 1, %bb1 ], [ 0, %bb7 ]
  %icmp = icmp eq i32 %phi, 0
  br i1 %icmp, label %bb9, label %bb7

bb7:                                              ; preds = %bb5
  %load = load i32, ptr addrspace(1) null, align 4
  %add8 = add i32 %load, 1
  br i1 false, label %bb2, label %bb5

bb9:                                              ; preds = %bb5
  ret void
}

; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare { i32, i1 } @llvm.smul.with.overflow.i32(i32, i32) #0

attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
```
crashes with the below backtrace when run with `opt --passes="print<scalar-evolution>,loop-unroll<no-partial;peeling;no-runtime;no-upperbound;no-profile-peeling;full-unroll-max=0;O3> <filename.ll>"` 
```
opt: /home/manishkh/Workspace7/orca/llvm/lib/Analysis/ScalarEvolution.cpp:3678: const llvm::SCEV* llvm::ScalarEvolution::getAddRecExpr(llvm::SmallVectorImpl<const llvm::SCEV*>&, const llvm::Loop*, llvm::SCEV::NoWrapFlags): Assertion `isAvailableAtLoopEntry(Operands[i], L) && "SCEVAddRecExpr operand is not available at loop entry!"' failed.
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
Stack dump:
0.	Program arguments: /home/manishkh/Workspace7/orcaInstall/installDA/bin/opt --passes=print<scalar-evolution>,loop-unroll<no-partial;peeling;no-runtime;no-upperbound;no-profile-peeling;full-unroll-max=0;O3> reduced2.ll -disable-output
 #0 0x00007f272f49aa72 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) /home/manishkh/Workspace7/orca/llvm/lib/Support/Unix/Signals.inc:723:0
 #1 0x00007f272f49aeaf PrintStackTraceSignalHandler(void*) /home/manishkh/Workspace7/orca/llvm/lib/Support/Unix/Signals.inc:798:0
 #2 0x00007f272f498793 llvm::sys::RunSignalHandlers() /home/manishkh/Workspace7/orca/llvm/lib/Support/Signals.cpp:105:0
 #3 0x00007f272f49a459 SignalHandler(int) /home/manishkh/Workspace7/orca/llvm/lib/Support/Unix/Signals.inc:413:0
 #4 0x00007f2738fd7630 __restore_rt sigaction.c:0:0
 #5 0x00007f272b893387 raise (/lib64/libc.so.6+0x36387)
 #6 0x00007f272b894a78 abort (/lib64/libc.so.6+0x37a78)
 #7 0x00007f272b88c1a6 __assert_fail_base (/lib64/libc.so.6+0x2f1a6)
 #8 0x00007f272b88c252 (/lib64/libc.so.6+0x2f252)
 #9 0x00007f2732031da6 llvm::ScalarEvolution::getAddRecExpr(llvm::SmallVectorImpl<llvm::SCEV const*>&, llvm::Loop const*, llvm::SCEV::NoWrapFlags) /home/manishkh/Workspace7/orca/llvm/lib/Analysis/ScalarEvolution.cpp:3677:0
#10 0x00007f2732031b29 llvm::ScalarEvolution::getAddRecExpr(llvm::SCEV const*, llvm::SCEV const*, llvm::Loop const*, llvm::SCEV::NoWrapFlags) /home/manishkh/Workspace7/orca/llvm/lib/Analysis/ScalarEvolution.cpp:3660:0
#11 0x00007f2732039c11 llvm::ScalarEvolution::createSimpleAffineAddRec(llvm::PHINode*, llvm::Value*, llvm::Value*) /home/manishkh/Workspace7/orca/llvm/lib/Analysis/ScalarEvolution.cpp:5776:0
#12 0x00007f2732039f65 llvm::ScalarEvolution::createAddRecFromPHI(llvm::PHINode*) /home/manishkh/Workspace7/orca/llvm/lib/Analysis/ScalarEvolution.cpp:5831:0
#13 0x00007f273203aadf llvm::ScalarEvolution::createNodeForPHI(llvm::PHINode*) /home/manishkh/Workspace7/orca/llvm/lib/Analysis/ScalarEvolution.cpp:6025:0
#14 0x00007f273204562a llvm::ScalarEvolution::createSCEV(llvm::Value*) /home/manishkh/Workspace7/orca/llvm/lib/Analysis/ScalarEvolution.cpp:8069:0
#15 0x00007f2732042338 llvm::ScalarEvolution::createSCEVIter(llvm::Value*) /home/manishkh/Workspace7/orca/llvm/lib/Analysis/ScalarEvolution.cpp:7447:0
#16 0x00007f2732035a05 llvm::ScalarEvolution::getSCEV(llvm::Value*) /home/manishkh/Workspace7/orca/llvm/lib/Analysis/ScalarEvolution.cpp:4572:0
#17 0x00007f273203ab71 llvm::ScalarEvolution::createNodeForPHI(llvm::PHINode*) /home/manishkh/Workspace7/orca/llvm/lib/Analysis/ScalarEvolution.cpp:6029:0
#18 0x00007f273204562a llvm::ScalarEvolution::createSCEV(llvm::Value*) /home/manishkh/Workspace7/orca/llvm/lib/Analysis/ScalarEvolution.cpp:8069:0
#19 0x00007f2732042338 llvm::ScalarEvolution::createSCEVIter(llvm::Value*) /home/manishkh/Workspace7/orca/llvm/lib/Analysis/ScalarEvolution.cpp:7447:0
#20 0x00007f2732035a05 llvm::ScalarEvolution::getSCEV(llvm::Value*) /home/manishkh/Workspace7/orca/llvm/lib/Analysis/ScalarEvolution.cpp:4572:0
#21 0x00007f2732039fcf llvm::ScalarEvolution::createAddRecFromPHI(llvm::PHINode*) /home/manishkh/Workspace7/orca/llvm/lib/Analysis/ScalarEvolution.cpp:5840:0
#22 0x00007f273203aadf llvm::ScalarEvolution::createNodeForPHI(llvm::PHINode*) /home/manishkh/Workspace7/orca/llvm/lib/Analysis/ScalarEvolution.cpp:6025:0
#23 0x00007f273204562a llvm::ScalarEvolution::createSCEV(llvm::Value*) /home/manishkh/Workspace7/orca/llvm/lib/Analysis/ScalarEvolution.cpp:8069:0
#24 0x00007f2732042338 llvm::ScalarEvolution::createSCEVIter(llvm::Value*) /home/manishkh/Workspace7/orca/llvm/lib/Analysis/ScalarEvolution.cpp:7447:0
#25 0x00007f2732035a05 llvm::ScalarEvolution::getSCEV(llvm::Value*) /home/manishkh/Workspace7/orca/llvm/lib/Analysis/ScalarEvolution.cpp:4572:0
#26 0x00007f273204a26a llvm::ScalarEvolution::computeExitLimitFromICmp(llvm::Loop const*, llvm::ICmpInst*, bool, bool, bool) /home/manishkh/Workspace7/orca/llvm/lib/Analysis/ScalarEvolution.cpp:9117:0
#27 0x00007f273204971e llvm::ScalarEvolution::computeExitLimitFromCondImpl(llvm::ScalarEvolution::ExitLimitCache&, llvm::Loop const*, llvm::Value*, bool, bool, bool) /home/manishkh/Workspace7/orca/llvm/lib/Analysis/ScalarEvolution.cpp:8975:0
#28 0x00007f273204955b llvm::ScalarEvolution::computeExitLimitFromCondCached(llvm::ScalarEvolution::ExitLimitCache&, llvm::Loop const*, llvm::Value*, bool, bool, bool) /home/manishkh/Workspace7/orca/llvm/lib/Analysis/ScalarEvolution.cpp:8958:0
#29 0x00007f27320491a4 llvm::ScalarEvolution::computeExitLimitFromCond(llvm::Loop const*, llvm::Value*, bool, bool, bool) /home/manishkh/Workspace7/orca/llvm/lib/Analysis/ScalarEvolution.cpp:8914:0
#30 0x00007f2732048f49 llvm::ScalarEvolution::computeExitLimit(llvm::Loop const*, llvm::BasicBlock*, bool) /home/manishkh/Workspace7/orca/llvm/lib/Analysis/ScalarEvolution.cpp:8888:0
#31 0x00007f27320488db llvm::ScalarEvolution::computeBackedgeTakenCount(llvm::Loop const*, bool) /home/manishkh/Workspace7/orca/llvm/lib/Analysis/ScalarEvolution.cpp:8797:0
#32 0x00007f27320469fb llvm::ScalarEvolution::getBackedgeTakenInfo(llvm::Loop const*) /home/manishkh/Workspace7/orca/llvm/lib/Analysis/ScalarEvolution.cpp:8358:0
#33 0x00007f27320464ab llvm::ScalarEvolution::getExitCount(llvm::Loop const*, llvm::BasicBlock const*, llvm::ScalarEvolution::ExitCountKind) /home/manishkh/Workspace7/orca/llvm/lib/Analysis/ScalarEvolution.cpp:8270:0
#34 0x00007f27320460c6 llvm::ScalarEvolution::getSmallConstantTripCount(llvm::Loop const*, llvm::BasicBlock const*) /home/manishkh/Workspace7/orca/llvm/lib/Analysis/ScalarEvolution.cpp:8203:0
#35 0x00007f273146141a tryToUnrollLoop(llvm::Loop*, llvm::DominatorTree&, llvm::LoopInfo*, llvm::ScalarEvolution&, llvm::TargetTransformInfo const&, llvm::AssumptionCache&, llvm::OptimizationRemarkEmitter&, llvm::BlockFrequencyInfo*, llvm::ProfileSummaryInfo*, bool, int, bool, bool, bool, std::optional<unsigned int>, std::optional<unsigned int>, std::optional<bool>, std::optional<bool>, std::optional<bool>, std::optional<bool>, std::optional<bool>, std::optional<unsigned int>, llvm::AAResults*) /home/manishkh/Workspace7/orca/llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp:1259:0
#36 0x00007f2731462e2c llvm::LoopUnrollPass::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) /home/manishkh/Workspace7/orca/llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp:1658:0
#37 0x00007f27339727ac llvm::detail::PassModel<llvm::Function, llvm::LoopUnrollPass, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) /home/manishkh/Workspace7/orca/llvm/include/llvm/IR/PassManagerInternal.h:91:0
#38 0x00007f272f831f82 llvm::PassManager<llvm::Function, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) /home/manishkh/Workspace7/orca/llvm/include/llvm/IR/PassManager.h:228:0
#39 0x00007f27331d1e32 llvm::detail::PassModel<llvm::Function, llvm::PassManager<llvm::Function, llvm::AnalysisManager<llvm::Function>>, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) /home/manishkh/Workspace7/orca/llvm/include/llvm/IR/PassManagerInternal.h:91:0
#40 0x00007f272f830d12 llvm::ModuleToFunctionPassAdaptor::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) /home/manishkh/Workspace7/orca/llvm/lib/IR/PassManager.cpp:128:0
#41 0x00007f27331d1d96 llvm::detail::PassModel<llvm::Module, llvm::ModuleToFunctionPassAdaptor, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) /home/manishkh/Workspace7/orca/llvm/include/llvm/IR/PassManagerInternal.h:91:0
#42 0x00007f272f831c5a llvm::PassManager<llvm::Module, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) /home/manishkh/Workspace7/orca/llvm/include/llvm/IR/PassManager.h:228:0
#43 0x00000000004acc3e 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) /home/manishkh/Workspace7/orca/llvm/tools/opt/NewPMDriver.cpp:559:0
#44 0x0000000000477a33 optMain /home/manishkh/Workspace7/orca/llvm/tools/opt/optdriver.cpp:756:0
#45 0x000000000047540f main /home/manishkh/Workspace7/orca/llvm/tools/opt/opt.cpp:25:0
#46 0x00007f272b87f555 __libc_start_main (/lib64/libc.so.6+0x22555)
#47 0x00000000004752c9 _start (/home/manishkh/Workspace7/orcaInstall/installDA/bin/opt+0x4752c9)
```

as demonstrated by this godbolt example: https://godbolt.org/z/be3a4Yr9e

Comments: author: Nirhar
body: I did some preliminary analysis on this bug, and here is what I found:
The IR at the point of crash looks like this:
```
define zing void @ham() {
bb:
  br label %bb1

bb1.loopexit:                                     ; preds = %bb3
  %phi4.lcssa = phi i32 [ %phi4, %bb3 ]
  br label %bb1

bb1:                                              ; preds = %bb1.loopexit, %bb
  %phi = phi i32 [ 0, %bb ], [ %phi4.lcssa, %bb1.loopexit ]
  br label %bb5.peel.begin

bb5.peel.begin:                                   ; preds = %bb1
  br label %bb5.peel

bb5.peel:                                         ; preds = %bb5.peel.begin, exiting
  %icmp.peel = icmp eq i32 %phi, 0
  br i1 %icmp.peel, label %bb9.loopexit2, label %bb7.peel

bb7.peel:                                         ; preds = %bb5.peel
  %load.peel = load i32, ptr addrspace(1) null, align 4
  %add8.peel = add i32 %load.peel, 1
  br i1 false, label %bb2, label %bb5.peel.next

bb5.peel.next:                                    ; preds = %bb7.peel
  br label %bb5.peel.next1

bb5.peel.next1:                                   ; preds = %bb5.peel.next
  br label %bb1.peel.newph

bb1.peel.newph:                                   ; preds = %bb5.peel.next1
  br label %bb5

bb2.loopexit:                                     ; preds = %bb7
  %add8.lcssa.ph = phi i32 [ %add8, %bb7 ]
  br label %bb2

bb2:                                              ; preds = %bb2.loopexit, %bb7.peel
  %add8.lcssa = phi i32 [ %add8.peel, %bb7.peel ], [ %add8.lcssa.ph, %bb2.loopexit ]
  %call = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 %add8.lcssa, i32 -2)
  %extractvalue = extractvalue { i32, i1 } %call, 0
  br label %bb3

bb3:                                              ; preds = %bb3, %bb2
  %phi4 = phi i32 [ %add, %bb3 ], [ 0, %bb2 ]
  %add = add i32 %extractvalue, %phi4
  br i1 false, label %bb3, label %bb1.loopexit

bb5:                                              ; preds = %bb7, %bb1.peel.newph, exiting
  br i1 false, label %bb9.loopexit, label %bb7

bb7:                                              ; preds = %bb5
  %load = load i32, ptr addrspace(1) null, align 4 
  %add8 = add i32 %load, 1
  br i1 false, label %bb2.loopexit, label %bb5, !llvm.loop !0

bb9.loopexit:                                     ; preds = %bb5
  br label %bb9

bb9.loopexit2:                                    ; preds = %bb5.peel
  br label %bb9

bb9:                                              ; preds = %bb9.loopexit2, %bb9.loopexit
  ret void
}

; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare { i32, i1 } @llvm.smul.with.overflow.i32(i32, i32) #0

attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
```
We have 3 loops in this this IR(1 outer and 2 inner, out of which the bb5-bb7 loop has been peeled.):
```
Loop at depth 1 containing: %bb1<header>,%bb5<exiting>,%bb7,%bb2,%bb3,%bb1.loopexit<latch>,%bb5.peel.begin,%bb5.peel.next,%bb1.peel.newph,%bb5.peel<exiting>,%bb7.peel,%bb5.peel.next1
    Loop at depth 2 containing: %bb5<header><exiting>,%bb7<latch><exiting>
    Loop at depth 2 containing: %bb3<header><latch><exiting>
``` 
Here is the CFG for this IR:
![Screenshot 2024-07-03 at 8 24 52 PM](https://github.com/llvm/llvm-project/assets/46352931/02e57740-9ef6-4945-868e-62a645325552)

The assertion fails because SCEV expects bb7 to properly dominate bb3, which it does in the old IR, but after peeling, it does not. This seems to arise from the fact that SCEVs in %bb2 have not been invalidated after the peeling. I see that the SCEV for `%extractvalue` comes from the ValueExprMap instead of being recomputed, following the peel, and the use of this invalid SCEV is what I think is causing the problem.



author: Nirhar
body: Perhaps these updates to SE are not enough?
https://github.com/llvm/llvm-project/blob/86d456ad8f2aef6edcc36d44d26119025331981e/llvm/lib/Transforms/Utils/LoopPeel.cpp#L1088-L1090
author: Nirhar
body: @nikic @fhahn @RKSimon  is `forgetTopmostLoop` supposed to forget SCEV of all SSA values inside the loop(ie, header, latch and other blocks) ? I see that its implementation does not do that currently.
author: nikic
body: @Nirhar Generally no. It forgets information about induction variables (things based on loop header phis), as well as information pertaining to the loop (such as BECounts, exit values etc), but not of literally everything defined in the loop.
author: nikic
body: From a super quick look, the issue here might be that the exit phi forgetValue() calls don't invalidate enough values due to the condition at https://github.com/llvm/llvm-project/blob/e55585fd7bddf5bb3824a53cbe2971206d3c20c6/llvm/lib/Analysis/ScalarEvolution.cpp#L8415, which means that we will stop at the aggregate results of with.overflow intrinsics, even though SCEV can look through them. It might be that they need special handling.
author: v01dXYZ
body: When replacing the `smul.with.overflow + extractvalue` by a `mul`, it doesn't abort.
When removing the SCEVable check, it doesn't abort.

The function that allows to traverse unSCEVable when creating the SCEV is `MatchBinaryOp` (cf `createSimpleAffineRec`). We could make this function returns a subset of the use-def to SCEVable or have another function that do the reverse with a def-use matching.
author: Nirhar
body: I think that the issue might be something else. From my analysis, here is how the control flow goes:
1. When we hit `forgetTopmostLoop`, we try to forget the SCEV values of the loop:
```
Loop at depth 1 containing: %bb1<header>,%bb5<exiting>,%bb7,%bb2,%bb3,%bb1.loopexit<latch>,%bb5.peel.begin,%bb5.peel.next,%bb1.peel.newph,%bb5.peel<exiting>,%bb7.peel,%bb5.peel.next1
    Loop at depth 2 containing: %bb5<header><exiting>,%bb7<latch><exiting>
    Loop at depth 2 containing: %bb3<header><latch><exiting>
```
We first take the phi node in the header `%phi = phi i32 [ 0, %bb ], [ %phi4.lcssa, %bb1.loopexit ]` and start the DFS from there. From here, we go to the following nodes:(` %icmp.peel = icmp eq i32 %phi, 0` , ` br i1 %icmp.peel, label %bb9.loopexit2, label %bb7.peel` and now non existent `%icmp = icmp eq i32 %phi, 0` , `br i1 %icmp, label %bb9, label %bb7`. The DFS for the outer loop ends here.

2. Next we traverse the inner loop:
```
Loop at depth 2 containing: %bb3<header><latch><exiting>
```
Here we start with phi node in the header `%phi4 = phi i32 [ %add, %bb3 ], [ 0, %bb2 ]` and then traverse through the nodes: `%add = add i32 %extractvalue, %phi4` and `%phi4.lcssa = phi i32 [ %phi4, %bb3 ]`, after which we quit traversal.

3. Finally we visit the inner loop:
```
Loop at depth 2 containing: %bb5<header><exiting>,%bb7<latch><exiting>
```
We start and end DFS at (now non-existent)`%phi6 = phi i32 [ 0, %bb1.peel.newph ], [ 0, %bb7 ]`.

**_TLDR_**; Throughout the entire traversal, we never visit the basic block bb2, and never invalidate its SCEV values. We don't even visit smul intrinsic call to forget its SCEV. Perhaps one needs to consider use-def chains to forget SCEV values, similar to the now considered def-use chain.

Note: The IR at the point of crash is slightly different from the IR after the previous loop-peel. I've used the term `now non-existent` to mean that that node is not present in the IR at the crash point.
author: nikic
body: @Nirhar The forgetTopmostLoop() call is not what is responsible for the invalidation here, it's the forgetValue() call on the LCSSA phis in https://github.com/llvm/llvm-project/blob/27ccc8835e5163484234549fa6128eeb00d7432d/llvm/lib/Transforms/Utils/LoopPeel.cpp#L862
author: Nirhar
body: Aah sorry, I missed that. Thanks for pointing it out!

Fix patch:
commit cff8d716bdf017a2af8eb8623257fd33ee43f30e
Author: v01dXYZ <14996868+v01dXYZ@users.noreply.github.com>
Date:   Tue Jul 9 09:14:33 2024 +0200

    [SCEV] forgetValue: support (with-overflow-inst op0, op1) (#98015)
    
    The use-def walk in forgetValue() was skipping instructions with
    non-SCEVable types. However, SCEV may look past with.overflow
    intrinsics returning aggregates.
    
    Fixes #97586.

diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 430e1c6d8f8c..51cffac80876 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -8412,7 +8412,7 @@ void ScalarEvolution::visitAndClearUsers(
     SmallVectorImpl<const SCEV *> &ToForget) {
   while (!Worklist.empty()) {
     Instruction *I = Worklist.pop_back_val();
-    if (!isSCEVable(I->getType()))
+    if (!isSCEVable(I->getType()) && !isa<WithOverflowInst>(I))
       continue;
 
     ValueExprMapType::iterator It =


Regression tests:
file: llvm/test/Transforms/LoopUnroll/peel-loop-scev-invalidate-with-overflow-inst.ll
commands: opt < %s -S  -passes='print<scalar-evolution>,loop-unroll<peeling;full-unroll-max=0>,print<scalar-evolution>' 2>&1
tests: test_name: <module>
test_body: ;
; This test ensures that (extractvalue 0 (with-overflow-inst op0, op1))
; is invalidated by LoopPeel when the operands of with-overflow-inst
; are changed.
;
; In the following case, LoopPeel modifies the CFG into another one
; with %bb7 not dominating %bb2 and %bb3 although %extractvalue is
; still the step for the %bb3 loop. %call has been modified and uses
; different operands but the SCEV value for %extractvalue has not been
; invalidated and still refers to %load in its SCEV operands
; (SCEV(%extractvalue) := -2 + -2 * %load).
;
; When LoopUnroll tries to compute the SCEV for the %bb3 Phi, the
; stale data for %extractvalue is used whereas %load is not available
; in %bb3 which is wrong.
;
; for more details and nice pictures: https://github.com/llvm/llvm-project/issues/97586
;
; Although the reason for the bug was in forgetValue, it is still relevant to
; test if LoopPeel invalidates %extractvalue after changing %call.
;
; forgetValue only walks the users, so calling it on the IV Phis does not
; invalidate %extractvalue (thus forgetLoop does not invalidate it too).
; It has to be done by LoopPeel itself.


define void @loop_peeling_smul_with_overflow() {
; before loop-unroll
; after loop-unroll
;
bb:
  br label %bb1

bb1:                                              ; preds = %bb3, %bb
  %phi = phi i32 [ 0, %bb ], [ %phi4, %bb3 ]
  br label %bb5

bb2:                                              ; preds = %bb7
  %call = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 %add8, i32 -2)
  %extractvalue = extractvalue { i32, i1 } %call, 0
  br label %bb3

bb3:                                              ; preds = %bb3, %bb2
  %phi4 = phi i32 [ %add, %bb3 ], [ 0, %bb2 ]
  %add = add i32 %extractvalue, %phi4
  br i1 false, label %bb3, label %bb1

bb5:                                              ; preds = %bb7, %bb1
  %phi6 = phi i32 [ 1, %bb1 ], [ 0, %bb7 ]
  %icmp = icmp eq i32 %phi, 0
  br i1 %icmp, label %bb9, label %bb7

bb7:                                              ; preds = %bb5
  %load = load i32, ptr addrspace(1) null, align 4
  %add8 = add i32 %load, 1
  br i1 false, label %bb2, label %bb5

bb9:                                              ; preds = %bb5
  ret void
}