Bug type: miscompilation
Component: ScalarEvolution
Bug location: llvm/lib/Analysis/ScalarEvolution.cpp: ScalarEvolution::createNodeForPHI
Issue title: WRONG code: GVN? Loop opts?
Labels: miscompilation
llvm:SCEV
Reported by: JonPsson1

cat wrong0.i
```
int printf(const char *, ...);
unsigned int IntArr[6];
unsigned int GlobIntONE = 1, GlobIntZERO = 0;
const unsigned int *GlobIntPtr = 0;
unsigned long Res;
unsigned long *ResPtr = &Res;
const unsigned int **GlobIntPtrPTr = &GlobIntPtr;
unsigned int *func() {
  int *GlobIntONEPtr = &GlobIntONE;
  for (int Idx = 0; Idx <= 7; Idx += 1) {
    int Idx2 = 1;
    if (Idx > 0) {
      for (; Idx2 <= 7; Idx2 += 1)
        ;
      return GlobIntONEPtr;
    }
  }
  0 != &GlobIntONEPtr;
  return &GlobIntZERO;
}

int main() {
  IntArr[GlobIntZERO] = GlobIntZERO;
  *GlobIntPtrPTr = func();
  unsigned char Byte = *GlobIntPtr;
  *ResPtr = Byte;
  printf("checksum = %X\n", Res);
}
```


In the above file, func() should obviously should return GlobIntONEPtr, but it does not if compiled with '-fno-inline  -mllvm -unroll-full-max-count=1'.

```
clang -O0 -march=z16 wrong0.i -o a.out -w -fno-inline  ; ./a.out
checksum = 1

clang -O3 -march=z16 wrong0.i -o a.out -w -fno-inline  ; ./a.out
checksum = 1

clang -O3 -march=z16 wrong0.i -o a.out -w -fno-inline  -mllvm -unroll-full-max-count=1 ; ./a.out
checksum = 0

```
It may be that GVN does something wrong, as after it @func() is always returning ptr undef. Or it could very well be some of the loop optimizations that is run just before it.

A bisect shows that

```
d77067d08a3f56dc2d0e6c95bd2852c943df743a is the first bad commit
commit d77067d08a3f56dc2d0e6c95bd2852c943df743a
Author: Nikita Popov <npopov@redhat.com>
Date:   Wed Dec 6 14:17:18 2023 +0100

    [ValueTracking] Add dominating condition support in computeKnownBits() (#73662)

```
@nikic 

Comments: author: dtcxzyw
body: Bisected to LoopUnrollFullPass:
```
; bin/opt -passes=loop-unroll-full -unroll-full-max-count=1 test.ll -S

@GlobIntONE = dso_local global i32 1, align 4
@GlobIntZERO = dso_local local_unnamed_addr global i32 0, align 4

define i32 @main() local_unnamed_addr {
entry:
  br label %for.body

for.body:                                         ; preds = %entry, %cleanup
  %retval.0 = phi ptr [ undef, %entry ], [ %retval.2, %cleanup ]
  %cmp1.not = phi i1 [ true, %entry ], [ false, %cleanup ]
  br i1 %cmp1.not, label %cleanup, label %for.cond2.preheader

for.cond2.preheader:                              ; preds = %for.body
  br label %cleanup.loopexit

cleanup.loopexit:                                 ; preds = %for.cond2.preheader
  br label %cleanup

cleanup:                                          ; preds = %cleanup.loopexit, %for.body
  %retval.2 = phi ptr [ %retval.0, %for.body ], [ @GlobIntONE, %cleanup.loopexit ]
  br i1 %cmp1.not, label %for.body, label %cleanup7

cleanup7:                                         ; preds = %cleanup
  %retval.2.lcssa = phi ptr [ %retval.2, %cleanup ]
  %res = load i32, ptr %retval.2.lcssa, align 4
  ret i32 %res
}
```
```
source_filename = "test.ll"

@GlobIntONE = dso_local global i32 1, align 4
@GlobIntZERO = dso_local local_unnamed_addr global i32 0, align 4

define i32 @main() local_unnamed_addr {
entry:
  br label %for.body.peel.begin

for.body.peel.begin:                              ; preds = %entry
  br label %for.body.peel

for.body.peel:                                    ; preds = %for.body.peel.begin
  br i1 true, label %cleanup.peel, label %for.cond2.preheader.peel

for.cond2.preheader.peel:                         ; preds = %for.body.peel
  br label %cleanup.loopexit.peel

cleanup.loopexit.peel:                            ; preds = %for.cond2.preheader.peel
  br label %cleanup.peel

cleanup.peel:                                     ; preds = %cleanup.loopexit.peel, %for.body.peel
  %retval.2.peel = phi ptr [ undef, %for.body.peel ], [ @GlobIntONE, %cleanup.loopexit.peel ]
  br i1 true, label %for.body.peel.next, label %cleanup7

for.body.peel.next:                               ; preds = %cleanup.peel
  br label %for.body.peel.next1

for.body.peel.next1:                              ; preds = %for.body.peel.next
  br label %entry.peel.newph

entry.peel.newph:                                 ; preds = %for.body.peel.next1
  br label %for.body

for.body:                                         ; preds = %cleanup, %entry.peel.newph
  br i1 false, label %cleanup, label %for.cond2.preheader

for.cond2.preheader:                              ; preds = %for.body
  br label %cleanup.loopexit

cleanup.loopexit:                                 ; preds = %for.cond2.preheader
  br label %cleanup

cleanup:                                          ; preds = %cleanup.loopexit, %for.body
  br i1 false, label %for.body, label %cleanup7.loopexit, !llvm.loop !0

cleanup7.loopexit:                                ; preds = %cleanup
  %retval.2.lcssa.ph = phi ptr [ %retval.2.peel, %cleanup ]
  br label %cleanup7

cleanup7:                                         ; preds = %cleanup7.loopexit, %cleanup.peel
  %retval.2.lcssa = phi ptr [ %retval.2.peel, %cleanup.peel ], [ %retval.2.lcssa.ph, %cleanup7.loopexit ]
  %res = load i32, ptr %retval.2.lcssa, align 4
  ret i32 %res
}

!0 = distinct !{!0, !1}
!1 = !{!"llvm.loop.peeled.count", i32 1}
```
author: dtcxzyw
body: `simplifyLoopAfterUnroll -> simplifyLoopIVs` causes the miscompilation: https://alive2.llvm.org/ce/z/yiBsZt
I will post a fix later.

author: dtcxzyw
body: The root cause is:
1. `simplifyInstruction(%retval.2.peel)` returns `@GlobIntONE`. Thus, `ScalarEvolution::createNodeForPHI` returns SCEV expr `@GlobIntONE` for `%retval.2.peel`.
2.  `SimplifyIndvar::replaceIVUserWithLoopInvariant` tries to replace the use of `%retval.2.peel` in `%retval.2.lcssa.ph` with `@GlobIntONE`.
3. `SCEVExpander::expand` reuses `%retval.2.peel = phi ptr [ undef, %for.body.peel ], [ @GlobIntONE, %cleanup.loopexit.peel ]` to generate code for `@GlobIntONE`. It is incorrect.

Two possible solutions:
1. Set `CanUseUndef` to false in `simplifyInstruction`.
2. Block this case in `ScalarEvolution::canReuseInstruction`.

@nikic @preames Any thoughts?

author: nikic
body: I think setting `CanUseUndef=false` would be the correct thing to do. The reverse replacement here is particularly fishy, but more generally, if we make an assumption about the value of undef at a particular use, we have to be consistent about that assumption, and SCEV cannot really guarantee that this is the case.

Fix patch:
commit 0b9f1cc024ca6c7e8d60524be07c0ddfcd08b23c
Author: Yingwei Zheng <dtcxzyw2333@gmail.com>
Date:   Thu Nov 7 15:53:51 2024 +0800

    [SCEV] Disallow simplifying phi(undef, X) to X (#115109)
    
    See the following case:
    ```
    @GlobIntONE = global i32 0, align 4
    
    define ptr @src() {
    entry:
      br label %for.body.peel.begin
    
    for.body.peel.begin:                              ; preds = %entry
      br label %for.body.peel
    
    for.body.peel:                                    ; preds = %for.body.peel.begin
      br i1 true, label %cleanup.peel, label %cleanup.loopexit.peel
    
    cleanup.loopexit.peel:                            ; preds = %for.body.peel
      br label %cleanup.peel
    
    cleanup.peel:                                     ; preds = %cleanup.loopexit.peel, %for.body.peel
      %retval.2.peel = phi ptr [ undef, %for.body.peel ], [ @GlobIntONE, %cleanup.loopexit.peel ]
      br i1 true, label %for.body.peel.next, label %cleanup7
    
    for.body.peel.next:                               ; preds = %cleanup.peel
      br label %for.body.peel.next1
    
    for.body.peel.next1:                              ; preds = %for.body.peel.next
      br label %entry.peel.newph
    
    entry.peel.newph:                                 ; preds = %for.body.peel.next1
      br label %for.body
    
    for.body:                                         ; preds = %cleanup, %entry.peel.newph
      %retval.0 = phi ptr [ %retval.2.peel, %entry.peel.newph ], [ %retval.2, %cleanup ]
      br i1 false, label %cleanup, label %cleanup.loopexit
    
    cleanup.loopexit:                                 ; preds = %for.body
      br label %cleanup
    
    cleanup:                                          ; preds = %cleanup.loopexit, %for.body
      %retval.2 = phi ptr [ %retval.0, %for.body ], [ @GlobIntONE, %cleanup.loopexit ]
      br i1 false, label %for.body, label %cleanup7.loopexit
    
    cleanup7.loopexit:                                ; preds = %cleanup
      %retval.2.lcssa.ph = phi ptr [ %retval.2, %cleanup ]
      br label %cleanup7
    
    cleanup7:                                         ; preds = %cleanup7.loopexit, %cleanup.peel
      %retval.2.lcssa = phi ptr [ %retval.2.peel, %cleanup.peel ], [ %retval.2.lcssa.ph, %cleanup7.loopexit ]
      ret ptr %retval.2.lcssa
    }
    
    define ptr @tgt() {
    entry:
      br label %for.body.peel.begin
    
    for.body.peel.begin:                              ; preds = %entry
      br label %for.body.peel
    
    for.body.peel:                                    ; preds = %for.body.peel.begin
      br i1 true, label %cleanup.peel, label %cleanup.loopexit.peel
    
    cleanup.loopexit.peel:                            ; preds = %for.body.peel
      br label %cleanup.peel
    
    cleanup.peel:                                     ; preds = %cleanup.loopexit.peel, %for.body.peel
      %retval.2.peel = phi ptr [ undef, %for.body.peel ], [ @GlobIntONE, %cleanup.loopexit.peel ]
      br i1 true, label %for.body.peel.next, label %cleanup7
    
    for.body.peel.next:                               ; preds = %cleanup.peel
      br label %for.body.peel.next1
    
    for.body.peel.next1:                              ; preds = %for.body.peel.next
      br label %entry.peel.newph
    
    entry.peel.newph:                                 ; preds = %for.body.peel.next1
      br label %for.body
    
    for.body:                                         ; preds = %cleanup, %entry.peel.newph
      br i1 false, label %cleanup, label %cleanup.loopexit
    
    cleanup.loopexit:                                 ; preds = %for.body
      br label %cleanup
    
    cleanup:                                          ; preds = %cleanup.loopexit, %for.body
      br i1 false, label %for.body, label %cleanup7.loopexit
    
    cleanup7.loopexit:                                ; preds = %cleanup
      %retval.2.lcssa.ph = phi ptr [ %retval.2.peel, %cleanup ]
      br label %cleanup7
    
    cleanup7:                                         ; preds = %cleanup7.loopexit, %cleanup.peel
      %retval.2.lcssa = phi ptr [ %retval.2.peel, %cleanup.peel ], [ %retval.2.lcssa.ph, %cleanup7.loopexit ]
      ret ptr %retval.2.lcssa
    }
    ```
    1. `simplifyInstruction(%retval.2.peel)` returns `@GlobIntONE`. Thus,
    `ScalarEvolution::createNodeForPHI` returns SCEV expr `@GlobIntONE` for
    `%retval.2.peel`.
    2. `SimplifyIndvar::replaceIVUserWithLoopInvariant` tries to replace the
    use of `%retval.2.peel` in `%retval.2.lcssa.ph` with `@GlobIntONE`.
    3. `simplifyLoopAfterUnroll -> simplifyLoopIVs -> SCEVExpander::expand`
    reuses `%retval.2.peel = phi ptr [ undef, %for.body.peel ], [
    @GlobIntONE, %cleanup.loopexit.peel ]` to generate code for
    `@GlobIntONE`. It is incorrect.
    
    This patch disallows simplifying `phi(undef, X)` to `X` by setting
    `CanUseUndef` to false.
    Closes https://github.com/llvm/llvm-project/issues/114879.

diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 58e23e9556f1..b10811133770 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -6023,7 +6023,11 @@ const SCEV *ScalarEvolution::createNodeForPHI(PHINode *PN) {
   if (const SCEV *S = createAddRecFromPHI(PN))
     return S;
 
-  if (Value *V = simplifyInstruction(PN, {getDataLayout(), &TLI, &DT, &AC}))
+  // We do not allow simplifying phi (undef, X) to X here, to avoid reusing the
+  // phi node for X.
+  if (Value *V = simplifyInstruction(
+          PN, {getDataLayout(), &TLI, &DT, &AC, /*CtxI=*/nullptr,
+               /*UseInstrInfo=*/true, /*CanUseUndef=*/false}))
     return getSCEV(V);
 
   if (const SCEV *S = createNodeFromSelectLikePHI(PN))


Regression tests:
file: llvm/test/Transforms/IndVarSimplify/invalidate-modified-lcssa-phi.ll
commands: opt -passes='loop(indvars,loop-deletion)' -verify-scev -S %s
tests: test_name: test_pr52023
test_body: define i8 @test_pr52023(i1 %c.1, i1 %c.2) {
entry:
  br label %loop.1

loop.1:                                           ; preds = %loop.1.latch, %entry
  %inc79 = phi i8 [ %inc.lcssa, %loop.1.latch ], [ 0, %entry ]
  br label %loop.2

loop.2:                                           ; preds = %loop.2.latch, %loop.1
  %inc6 = phi i8 [ %inc79, %loop.1 ], [ %inc, %loop.2.latch ]
  %inc = add i8 %inc6, 1
  br i1 %c.1, label %loop.2.latch, label %loop.1.latch

loop.2.latch:                                     ; preds = %loop.2
  br i1 false, label %loop.2, label %loop.1.latch

loop.1.latch:                                     ; preds = %loop.2.latch, %loop.2
  %inc.lcssa = phi i8 [ %inc, %loop.2.latch ], [ undef, %loop.2 ]
  br i1 %c.2, label %exit, label %loop.1

exit:                                             ; preds = %loop.1.latch
  %inc.lcssa.lcssa = phi i8 [ %inc.lcssa, %loop.1.latch ]
  ret i8 %inc.lcssa.lcssa
}

file: llvm/test/Transforms/IndVarSimplify/no-iv-rewrite.ll
commands: opt < %s -passes=indvars -S -indvars-predicate-loops=0
tests: test_name: identityphi
test_body: target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"

; Function Attrs: nounwind
define void @identityphi(i32 %limit) #0 {
entry:
  br label %loop

loop:                                             ; preds = %control, %entry
  %iv = phi i32 [ 0, %entry ], [ %iv.next, %control ]
  br i1 undef, label %if.then, label %control

if.then:                                          ; preds = %loop
  br label %control

control:                                          ; preds = %if.then, %loop
  %iv.next = phi i32 [ %iv, %loop ], [ undef, %if.then ]
  %cmp = icmp slt i32 %iv.next, %limit
  br i1 %cmp, label %loop, label %exit

exit:                                             ; preds = %control
  ret void
}

attributes #0 = { nounwind }

file: llvm/test/Transforms/LoopStrengthReduce/X86/2012-01-13-phielim.ll
commands: opt < %s -S -loop-reduce -mtriple=x86_64-- -mcpu=core2
tests: test_name: test3
test_body: ; Function Attrs: nounwind ssp uwtable
define fastcc void @test3(ptr nocapture %u) #0 {
entry:
  br i1 undef, label %meshBB1, label %meshBB5

for.inc8.us.i:                                    ; preds = %for.body3.us.i
  br i1 undef, label %meshBB1, label %meshBB

for.body3.us.i:                                   ; preds = %meshBB, %for.body3.lr.ph.us.i
  %indvars.iv.i.SV.phi = phi i64 [ %indvars.iv.next.i, %meshBB ], [ 0, %for.body3.lr.ph.us.i ]
  %storemerge13.us.i.SV.phi = phi i32 [ 0, %meshBB ], [ 0, %for.body3.lr.ph.us.i ]
  %Opq.sa.calc12 = sub i32 undef, 227
  %0 = add nsw i64 %indvars.iv.i.SV.phi, %indvars.iv8.i.SV.phi26
  %1 = trunc i64 %0 to i32
  %mul.i.us.i = mul nsw i32 0, %1
  %arrayidx5.us.i = getelementptr inbounds double, ptr %u, i64 %indvars.iv.i.SV.phi
  %2 = load double, ptr %arrayidx5.us.i, align 8
  %indvars.iv.next.i = add i64 %indvars.iv.i.SV.phi, 1
  br i1 undef, label %for.inc8.us.i, label %meshBB

for.body3.lr.ph.us.i:                             ; preds = %meshBB1, %meshBB
  %indvars.iv8.i.SV.phi26 = phi i64 [ undef, %meshBB1 ], [ %indvars.iv8.i.SV.phi24, %meshBB ]
  %arrayidx.us.i = getelementptr inbounds double, ptr undef, i64 %indvars.iv8.i.SV.phi26
  %3 = add i64 %indvars.iv8.i.SV.phi26, 1
  br label %for.body3.us.i

for.inc8.us.i2:                                   ; preds = %meshBB5
  unreachable

eval_At_times_u.exit:                             ; preds = %meshBB5
  ret void

meshBB:                                           ; preds = %for.body3.us.i, %for.inc8.us.i
  %indvars.iv8.i.SV.phi24 = phi i64 [ undef, %for.body3.us.i ], [ %3, %for.inc8.us.i ]
  %meshStackVariable.phi = phi i32 [ %Opq.sa.calc12, %for.body3.us.i ], [ undef, %for.inc8.us.i ]
  br i1 undef, label %for.body3.lr.ph.us.i, label %for.body3.us.i

meshBB1:                                          ; preds = %for.inc8.us.i, %entry
  br label %for.body3.lr.ph.us.i

meshBB5:                                          ; preds = %entry
  br i1 undef, label %eval_At_times_u.exit, label %for.inc8.us.i2
}

attributes #0 = { nounwind ssp uwtable }

file: llvm/test/Transforms/LoopUnroll/pr114879.ll
commands: opt -S -passes=loop-unroll-full -unroll-full-max-count=1 %s
tests: test_name: test
test_body: @GlobIntONE = external global i32, align 4

define ptr @test() {
entry:
  br label %for.body

for.body:                                         ; preds = %cleanup, %entry
  %retval.0 = phi ptr [ undef, %entry ], [ %retval.2, %cleanup ]
  %cmp1.not = phi i1 [ true, %entry ], [ false, %cleanup ]
  br i1 %cmp1.not, label %cleanup, label %cleanup.loopexit

cleanup.loopexit:                                 ; preds = %for.body
  br label %cleanup

cleanup:                                          ; preds = %cleanup.loopexit, %for.body
  %retval.2 = phi ptr [ %retval.0, %for.body ], [ @GlobIntONE, %cleanup.loopexit ]
  br i1 %cmp1.not, label %for.body, label %cleanup2

cleanup2:                                         ; preds = %cleanup
  %retval.2.lcssa = phi ptr [ %retval.2, %cleanup ]
  ret ptr %retval.2.lcssa
}

additional_args: --src-unroll=10 -tgt-unroll=10
file: llvm/test/Transforms/LoopVectorize/uniform-blend.ll
commands: opt -passes=loop-vectorize -force-vector-width=4 -S %s
tests: test_name: blend_uniform_iv
test_body: @dst = external global [32 x i16], align 1

define void @blend_uniform_iv(i1 %c) {
entry:
  br label %loop.header

loop.header:                                      ; preds = %loop.latch, %entry
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop.latch ]
  br i1 %c, label %loop.next, label %loop.latch

loop.next:                                        ; preds = %loop.header
  br label %loop.latch

loop.latch:                                       ; preds = %loop.next, %loop.header
  %blend = phi i64 [ poison, %loop.header ], [ %iv, %loop.next ]
  %dst.ptr = getelementptr inbounds [32 x i16], ptr @dst, i16 0, i64 %blend
  store i16 0, ptr %dst.ptr, align 2
  %iv.next = add nuw nsw i64 %iv, 1
  %cmp439 = icmp ult i64 %iv, 31
  br i1 %cmp439, label %loop.header, label %exit

exit:                                             ; preds = %loop.latch
  ret void
}

test_name: blend_uniform_iv_trunc
test_body: @dst = external global [32 x i16], align 1

define void @blend_uniform_iv_trunc(i1 %c) {
entry:
  br label %loop.header

loop.header:                                      ; preds = %loop.latch, %entry
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop.latch ]
  %iv.trunc.2 = trunc i64 %iv to i16
  br i1 %c, label %loop.next, label %loop.latch

loop.next:                                        ; preds = %loop.header
  br label %loop.latch

loop.latch:                                       ; preds = %loop.next, %loop.header
  %blend = phi i16 [ poison, %loop.header ], [ %iv.trunc.2, %loop.next ]
  %dst.ptr = getelementptr inbounds [32 x i16], ptr @dst, i16 0, i16 %blend
  store i16 0, ptr %dst.ptr, align 2
  %iv.next = add nuw nsw i64 %iv, 1
  %cmp439 = icmp ult i64 %iv, 31
  br i1 %cmp439, label %loop.header, label %exit

exit:                                             ; preds = %loop.latch
  ret void
}