Bug type: miscompilation
Component: LICM
Bug location: llvm/lib/Transforms/Scalar/LICM.cpp: hoistMulAddAssociation
Issue title: Miscompile with opt -passes="loop-unroll,loop-mssa(licm,indvars)" -unroll-count=4 
Labels: miscompilation
llvm:transforms
Reported by: mikaelholmen

llvm commit: e76b257483e6c
Reproduce with:
```opt -passes="loop-unroll,loop-mssa(licm,indvars)" -unroll-count=4 bbi-95405.ll -S -o -```

The input function returns 32768, but after running the passes as above we get
```
define i16 @foo() {
entry:
  br label %loop

loop:                                             ; preds = %loop, %entry
  br i1 false, label %loop, label %end, !llvm.loop !0

end:                                              ; preds = %loop
  ret i16 8192
}

!0 = distinct !{!0, !1}
!1 = !{!"llvm.loop.unroll.disable"}
```
So now the function returns 8192 instead.

If I extract the IR after loop-unroll or licm and run the rest of the passes instead I get the correct result.

[bbi-95405.ll.gz](https://github.com/llvm/llvm-project/files/15294896/bbi-95405.ll.gz)


Comments: author: mikaelholmen
body: This starts happening with https://github.com/llvm/llvm-project/pull/67736 , commit 2dd52046816
```
Recommit "[LICM] Support integer mul/add in hoistFPAssociation. (#67736)"

With a fix for build bot failure. I was accessing the type of a deleted
Instruction.

Original message:

The reassociation this is trying to repair can happen for integer types
too.

This patch adds support for integer mul/add to hoistFPAssociation. The
function has been renamed to hoistMulAddAssociation. I've used separate
statistics and limits for integer to allow tuning flexibility.
```

author: nikic
body: Without looking into this in detail, it's possible that the way the LICM code modifies instructions in place results incorrect cached SCEV results.
author: mikaelholmen
body: > Without looking into this in detail, it's possible that the way the LICM code modifies instructions in place results incorrect cached SCEV results.

I added some printouts and see that when IndVarSimplify run rewriteLoopExitValues we do
```
const SCEV *ExitValue = SE->getSCEVAtScope(Inst, L->getParentLoop());
```
for Inst being
```
%mul.n = mul i16 %mul, 8
```
and then we get the SCEV
```
(2 * %mul)
```
which looks wrong.

If I split up the pipeline in several opt runs we instead get the SCEV
```
(8 * %mul)
```
which looks more reasonable (and then we also get the correct result).
LICM changed
```
%mul.n = mul i16 %mul, 2
```
to
```
%mul.n = mul i16 %mul, 8
```
so yeah, it should perhaps do something (more) about SCEV.
author: mikaelholmen
body: @antoniofrighetto : nice that you work on this!

But oh it's annoying that I don't get any emails from github about that there was activity here with a linked PR with a fix etc even if I wrote this issue. :( I do get emails when there are comments but not when the issue was assigned or the PR was created. :(
author: antoniofrighetto
body: @mikaelholmen This is a bit inconvenient indeed, GH doesn’t seem to send notifications wrt that :( Closed this, thanks for reporting!
author: mikaelholmen
body: > @mikaelholmen This is a bit inconvenient indeed, GH doesn’t seem to send notifications wrt that :( Closed this, thanks for reporting!

Thank you!

Fix patch:
commit 70091dc943ade280d75cea1e5ea5e93d9a8f934a
Author: Antonio Frighetto <me@antoniofrighetto.com>
Date:   Sat May 18 16:52:17 2024 +0200

    [LICM] Invalidate cached SCEV results in `hoistMulAddAssociation`
    
    While reassociating expressions, LICM is required to invalidate SCEV
    results, as otherwise subsequent passes in the pipeline that leverage
    LICM foldings (e.g. IndVars), may reason on invalid expressions; thus
    miscompiling. This is achieved by rewriting the reassociable
    instruction from scratch.
    
    Fixes: https://github.com/llvm/llvm-project/issues/91957.

diff --git a/llvm/lib/Transforms/Scalar/LICM.cpp b/llvm/lib/Transforms/Scalar/LICM.cpp
index 6aa4188d1cc4..5eccf7b4adb6 100644
--- a/llvm/lib/Transforms/Scalar/LICM.cpp
+++ b/llvm/lib/Transforms/Scalar/LICM.cpp
@@ -2751,7 +2751,7 @@ static bool hoistMulAddAssociation(Instruction &I, Loop &L,
   IRBuilder<> Builder(Preheader->getTerminator());
   for (auto *U : Changes) {
     assert(L.isLoopInvariant(U->get()));
-    Instruction *Ins = cast<Instruction>(U->getUser());
+    auto *Ins = cast<BinaryOperator>(U->getUser());
     Value *Mul;
     if (I.getType()->isIntOrIntVectorTy()) {
       Mul = Builder.CreateMul(U->get(), Factor, "factor.op.mul");
@@ -2759,8 +2759,20 @@ static bool hoistMulAddAssociation(Instruction &I, Loop &L,
       Ins->dropPoisonGeneratingFlags();
     } else
       Mul = Builder.CreateFMulFMF(U->get(), Factor, Ins, "factor.op.fmul");
-    U->set(Mul);
+
+    // Rewrite the reassociable instruction.
+    unsigned OpIdx = U->getOperandNo();
+    auto *LHS = OpIdx == 0 ? Mul : Ins->getOperand(0);
+    auto *RHS = OpIdx == 1 ? Mul : Ins->getOperand(1);
+    auto *NewBO = BinaryOperator::Create(Ins->getOpcode(), LHS, RHS,
+                                         Ins->getName() + ".reass", Ins);
+    NewBO->copyIRFlags(Ins);
+    if (VariantOp == Ins)
+      VariantOp = NewBO;
+    Ins->replaceAllUsesWith(NewBO);
+    eraseInstruction(*Ins, SafetyInfo, MSSAU);
   }
+
   I.replaceAllUsesWith(VariantOp);
   eraseInstruction(I, SafetyInfo, MSSAU);
   return true;


Regression tests:
file: llvm/test/Transforms/LICM/update-scev-after-hoist.ll
commands: opt -S -passes='loop-unroll,loop-mssa(licm,indvars)' -unroll-count=4 < %s 2>&1
tests: test_name: main
test_body: define i16 @main() {
entry:
  br label %loop

loop:                                             ; preds = %loop, %entry
  %mul = phi i16 [ 1, %entry ], [ %mul.n, %loop ]
  %div = phi i16 [ 32767, %entry ], [ %div.n, %loop ]
  %mul.n = mul i16 %mul, 2
  %div.n = sdiv i16 %div, 2
  %cmp = icmp sgt i16 %div, 0
  br i1 %cmp, label %loop, label %end

end:                                              ; preds = %loop
  ret i16 %mul
}

additional_args: -src-unroll=16 -tgt-unroll=16