Bug type: miscompilation
Component: IR
ValueTracking
Bug location: llvm/lib/Analysis/ValueTracking.cpp: llvm::getUnderlyingObject
llvm/lib/IR/Value.cpp: stripPointerCastsAndOffsets
Issue title: [LICM] Miscompilation caused by wrong AA result
Labels: miscompilation
TBAA
llvm:ir
llvm:analysis
Reported by: dtcxzyw

Reproducer: https://godbolt.org/z/Y1vvbndf8
```
@c = dso_local global i32 0, align 4
@d = dso_local local_unnamed_addr global ptr @c, align 8

define i32 @main() {
entry:
  br label %for.cond

for.cond:                                         ; preds = %f.exit.split, %entry
  %b.0 = phi i32 [ 0, %entry ], [ %inc6, %f.exit.split ]
  %cmp = icmp ult i32 %b.0, 2
  br i1 %cmp, label %for.cond1.preheader, label %for.cond.cleanup

for.cond1.preheader:                              ; preds = %for.cond
  br label %for.cond1

for.cond.cleanup:                                 ; preds = %for.cond
  %0 = load i32, ptr @c, align 4, !tbaa !13
  ret i32 %0

for.cond1:                                        ; preds = %for.cond1, %for.cond1.preheader
  %i.0 = phi i32 [ %inc, %for.cond1 ], [ 0, %for.cond1.preheader ]
  %cmp2 = icmp ult i32 %i.0, 2
  %inc = add nuw nsw i32 %i.0, 1
  br i1 %cmp2, label %for.cond1, label %for.body.i.preheader, !llvm.loop !17

for.body.i.preheader:                             ; preds = %for.cond1
  %cmp2.lcssa = phi i1 [ %cmp2, %for.cond1 ]
  %1 = xor i1 %cmp2.lcssa, true
  %2 = bitcast i1 %1 to <1 x i1>
  %3 = call <1 x ptr> @llvm.masked.load.v1p0.p0(ptr @d, i32 8, <1 x i1> %2, <1 x ptr> poison), !tbaa !9
  %4 = bitcast <1 x ptr> %3 to ptr
  store i32 0, ptr @c, align 4, !tbaa !13
  %5 = load i32, ptr %4, align 4, !tbaa !13
  %tobool1.not.i = icmp ne i32 %5, 0
  %tobool1.not.i.fr = freeze i1 %tobool1.not.i
  br i1 %tobool1.not.i.fr, label %f.exit.split, label %for.body.i.preheader.split

for.body.i.preheader.split:                       ; preds = %for.body.i.preheader
  br label %for.body.i

for.body.i:                                       ; preds = %for.body.i.preheader.split, %for.body.i
  %n.04.i = phi i8 [ %add.i, %for.body.i ], [ -66, %for.body.i.preheader.split ]
  %add.i = add nsw i8 %n.04.i, 1
  %tobool.not.i = icmp eq i8 %add.i, 0
  br i1 %tobool.not.i, label %f.exit, label %for.body.i, !llvm.loop !15

f.exit:                                           ; preds = %for.body.i
  br label %f.exit.split

f.exit.split:                                     ; preds = %for.body.i.preheader, %f.exit
  store i32 7, ptr %4, align 4, !tbaa !13
  %inc6 = add nuw nsw i32 %b.0, 1
  br label %for.cond, !llvm.loop !18
}

!9 = !{!10, !10, i64 0}
!10 = !{!"any pointer", !11, i64 0}
!11 = !{!"omnipotent char", !12, i64 0}
!12 = !{!"Simple C/C++ TBAA"}
!13 = !{!14, !14, i64 0}
!14 = !{!"int", !11, i64 0}
!15 = distinct !{!15, !16}
!16 = !{!"llvm.loop.mustprogress"}
!17 = distinct !{!17, !16}
!18 = distinct !{!18, !16}
```
After LICM:
```
source_filename = "/app/example.ll"

define i32 @main() {
entry:
  br label %for.cond

for.cond:                                         ; preds = %f.exit.split, %entry
  %0 = phi i32 [ poison, %entry ], [ 0, %f.exit.split ]
  %b.0 = phi i32 [ 0, %entry ], [ %inc6, %f.exit.split ]
  %cmp = icmp ult i32 %b.0, 2
  br i1 %cmp, label %for.cond1.preheader, label %for.cond.cleanup

for.cond1.preheader:                              ; preds = %for.cond
  br label %for.cond1

for.cond.cleanup:                                 ; preds = %for.cond
  %.lcssa = phi i32 [ %0, %for.cond ]
  store i32 %.lcssa, ptr @c, align 4, !tbaa !0
  %1 = load i32, ptr @c, align 4, !tbaa !0
  ret i32 %1

for.cond1:                                        ; preds = %for.cond1, %for.cond1.preheader
  %i.0 = phi i32 [ %inc, %for.cond1 ], [ 0, %for.cond1.preheader ]
  %cmp2 = icmp ult i32 %i.0, 2
  %inc = add nuw nsw i32 %i.0, 1
  br i1 %cmp2, label %for.cond1, label %for.body.i.preheader, !llvm.loop !4

for.body.i.preheader:                             ; preds = %for.cond1
  %cmp2.lcssa = phi i1 [ %cmp2, %for.cond1 ]
  %2 = xor i1 %cmp2.lcssa, true
  %3 = bitcast i1 %2 to <1 x i1>
  %4 = call <1 x ptr> @llvm.masked.load.v1p0.p0(ptr @d, i32 8, <1 x i1> %3, <1 x ptr> poison), !tbaa !6
  %5 = bitcast <1 x ptr> %4 to ptr
  %6 = load i32, ptr %5, align 4, !tbaa !0
  %tobool1.not.i = icmp ne i32 %6, 0
  %tobool1.not.i.fr = freeze i1 %tobool1.not.i
  br i1 %tobool1.not.i.fr, label %f.exit.split, label %for.body.i.preheader.split

for.body.i.preheader.split:                       ; preds = %for.body.i.preheader
  br label %for.body.i

for.body.i:                                       ; preds = %for.body.i, %for.body.i.preheader.split
  %n.04.i = phi i8 [ %add.i, %for.body.i ], [ -66, %for.body.i.preheader.split ]
  %add.i = add nsw i8 %n.04.i, 1
  %tobool.not.i = icmp eq i8 %add.i, 0
  br i1 %tobool.not.i, label %f.exit, label %for.body.i, !llvm.loop !8

f.exit:                                           ; preds = %for.body.i
  br label %f.exit.split

f.exit.split:                                     ; preds = %f.exit, %for.body.i.preheader
  store i32 7, ptr %5, align 4, !tbaa !0
  %inc6 = add nuw nsw i32 %b.0, 1
  br label %for.cond, !llvm.loop !9
}

declare <1 x ptr> @llvm.masked.load.v1p0.p0(ptr nocapture, i32 immarg, <1 x i1>, <1 x ptr>) #0

attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: read) }

!0 = !{!1, !1, i64 0}
!1 = !{!"int", !2, i64 0}
!2 = !{!"omnipotent char", !3, i64 0}
!3 = !{!"Simple C/C++ TBAA"}
!4 = distinct !{!4, !5}
!5 = !{!"llvm.loop.mustprogress"}
!6 = !{!7, !7, i64 0}
!7 = !{!"any pointer", !2, i64 0}
!8 = distinct !{!8, !5}
!9 = distinct !{!9, !5}
```
```
> lli test.ll
> echo $?
7
> bin/opt -passes=licm test.ll -S -o out.ll
> lli out.ll
> echo $?
0
```
`store i32 7, ptr %5, align 4, !tbaa !0` may write something to `@c`. So this transform is incorrect.

Related patch: https://github.com/llvm/llvm-project/pull/96878
Original C code (needs x86 +cf or riscv +zicldst):
```
#include "csmith.h"
uint32_t c = 0;
int32_t *d = &c;
void f(int32_t *o) {
  uint8_t n;
  for (n = 190; n; n += 1) {
    *o = 0;
    if (*d)
      break;
  }
}
int main() {
  for (int b = 0; b <= 1; b++) {
    uint64_t j[2];
    int i;
    for (i = 0; i < 2; i++)
      j[i] = 1;
    f(&c);
    safe_add_func_int32_t_s_s(0, j[1]);
    *d = 7;
  }
  printf("%d\n", c);
  return 0;
}
```

cc @nikic @fhahn @KanRobert 


Comments: author: KanRobert
body: Which patch supports `zicldst` for riscv? @dtcxzyw 
author: nikic
body: Hm, we probably have a lot of places that don't handle things like `bitcast <1 x ptr> %3 to ptr` correctly, and assume that bitcast of pointer is always a pointer to pointer cast.
author: dtcxzyw
body: > Which patch supports `zicldst` for riscv? @dtcxzyw

We have a prototype which is still working in progress:
+ llvm support: https://github.com/llvm/llvm-project/compare/main...dtcxzyw:llvm-project:zicldst-support
+ qemu support: https://github.com/qemu/qemu/compare/master...cyyself:qemu:exp_rv_zicldst


Fix patch:
commit ebab105670a409e426ddcb0278578711a622b1b2
Author: Nikita Popov <npopov@redhat.com>
Date:   Thu Jul 4 09:43:16 2024 +0200

    [IR] Don't strip through pointer to vector of pointer bitcasts
    
    When using stripPointerCasts() and getUnderlyingObject(), don't
    strip through a bitcast from ptr to <1 x ptr>, which is not a
    no-op pointer cast. Calling code is generally not prepared to
    handle that situation, resulting in incorrect alias analysis
    results for example.
    
    Fixes https://github.com/llvm/llvm-project/issues/97600.

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 5476dc5d8518..258576f0cdff 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -6403,9 +6403,10 @@ const Value *llvm::getUnderlyingObject(const Value *V, unsigned MaxLookup) {
       V = GEP->getPointerOperand();
     } else if (Operator::getOpcode(V) == Instruction::BitCast ||
                Operator::getOpcode(V) == Instruction::AddrSpaceCast) {
-      V = cast<Operator>(V)->getOperand(0);
-      if (!V->getType()->isPointerTy())
+      Value *NewV = cast<Operator>(V)->getOperand(0);
+      if (!NewV->getType()->isPointerTy())
         return V;
+      V = NewV;
     } else if (auto *GA = dyn_cast<GlobalAlias>(V)) {
       if (GA->isInterposable())
         return V;
diff --git a/llvm/lib/IR/Value.cpp b/llvm/lib/IR/Value.cpp
index 8522747ccf12..b2ee75811fbb 100644
--- a/llvm/lib/IR/Value.cpp
+++ b/llvm/lib/IR/Value.cpp
@@ -652,9 +652,10 @@ static const Value *stripPointerCastsAndOffsets(
       }
       V = GEP->getPointerOperand();
     } else if (Operator::getOpcode(V) == Instruction::BitCast) {
-      V = cast<Operator>(V)->getOperand(0);
-      if (!V->getType()->isPointerTy())
+      Value *NewV = cast<Operator>(V)->getOperand(0);
+      if (!NewV->getType()->isPointerTy())
         return V;
+      V = NewV;
     } else if (StripKind != PSK_ZeroIndicesSameRepresentation &&
                Operator::getOpcode(V) == Instruction::AddrSpaceCast) {
       // TODO: If we know an address space cast will not change the


Regression tests:
file: llvm/test/Analysis/BasicAA/ptr-vector.ll
commands: opt -S -passes=licm < %s 2>&1
tests: test_name: test
test_body: @c = dso_local global i32 0, align 4
@d = dso_local local_unnamed_addr global ptr @c, align 8

define i32 @main() {
entry:
  br label %for.cond

for.cond:                                         ; preds = %f.exit.split, %entry
  %b.0 = phi i32 [ 0, %entry ], [ %inc6, %f.exit.split ]
  %cmp = icmp ult i32 %b.0, 2
  br i1 %cmp, label %for.cond1.preheader, label %for.cond.cleanup

for.cond1.preheader:                              ; preds = %for.cond
  br label %for.cond1

for.cond.cleanup:                                 ; preds = %for.cond
  %0 = load i32, ptr @c, align 4, !tbaa !13
  %retval = xor i32 %0, 7
  ret i32 %retval

for.cond1:                                        ; preds = %for.cond1, %for.cond1.preheader
  %i.0 = phi i32 [ %inc, %for.cond1 ], [ 0, %for.cond1.preheader ]
  %cmp2 = icmp ult i32 %i.0, 2
  %inc = add nuw nsw i32 %i.0, 1
  br i1 %cmp2, label %for.cond1, label %for.body.i.preheader, !llvm.loop !17

for.body.i.preheader:                             ; preds = %for.cond1
  %cmp2.lcssa = phi i1 [ %cmp2, %for.cond1 ]
  %1 = xor i1 %cmp2.lcssa, true
  %2 = bitcast i1 %1 to <1 x i1>
  %3 = call <1 x ptr> @llvm.masked.load.v1p0.p0(ptr @d, i32 8, <1 x i1> %2, <1 x ptr> poison), !tbaa !9
  %4 = bitcast <1 x ptr> %3 to ptr
  store i32 0, ptr @c, align 4, !tbaa !13
  %5 = load i32, ptr %4, align 4, !tbaa !13
  %tobool1.not.i = icmp ne i32 %5, 0
  %tobool1.not.i.fr = freeze i1 %tobool1.not.i
  br i1 %tobool1.not.i.fr, label %f.exit.split, label %for.body.i.preheader.split

for.body.i.preheader.split:                       ; preds = %for.body.i.preheader
  br label %for.body.i

for.body.i:                                       ; preds = %for.body.i.preheader.split, %for.body.i
  %n.04.i = phi i8 [ %add.i, %for.body.i ], [ -66, %for.body.i.preheader.split ]
  %add.i = add nsw i8 %n.04.i, 1
  %tobool.not.i = icmp eq i8 %add.i, 0
  br i1 %tobool.not.i, label %f.exit, label %for.body.i, !llvm.loop !15

f.exit:                                           ; preds = %for.body.i
  br label %f.exit.split

f.exit.split:                                     ; preds = %for.body.i.preheader, %f.exit
  store i32 7, ptr %4, align 4, !tbaa !13
  %inc6 = add nuw nsw i32 %b.0, 1
  br label %for.cond, !llvm.loop !18
}

!9 = !{!10, !10, i64 0}
!10 = !{!"any pointer", !11, i64 0}
!11 = !{!"omnipotent char", !12, i64 0}
!12 = !{!"Simple C/C++ TBAA"}
!13 = !{!14, !14, i64 0}
!14 = !{!"int", !11, i64 0}
!15 = distinct !{!15, !16}
!16 = !{!"llvm.loop.mustprogress"}
!17 = distinct !{!17, !16}
!18 = distinct !{!18, !16}