Bug type: hang
Component: MemCpyOptimizer
Bug location: llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp: MemCpyOptPass::processMemCpyMemCpyDependence
Issue title: [MemcpyOpt] Infinite loops when forwarding memcpy on globals
Labels: llvm:hang
llvm:transforms
Reported by: dtcxzyw

Reproducer: https://godbolt.org/z/GKzGEKE18
```
; bin/opt -passes=memcpyopt reduced.ll -S
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"
target triple = "x86_64-unknown-linux-gnu"

%struct.S1 = type { i64, i16, i32, %struct.S0, i8, i32, i8, i32, i8 }
%struct.S0 = type { i32, i32, i32, i32, i16 }

@g = external global i8
@g_265 = external global %struct.S1

define void @func() {
entry:
  call void @llvm.memcpy.p0.p0.i64(ptr getelementptr inbounds (i8, ptr @g_265, i64 16), ptr getelementptr inbounds nuw (i8, ptr @g_265, i64 16), i64 20, i1 false)
  call void @llvm.memcpy.p0.p0.i64(ptr @g, ptr getelementptr inbounds (i8, ptr @g_265, i64 24), i64 1, i1 false)
  ret void
}
```
```
Found roots: %entry 
Clobber starting at access 1 = MemoryDef(liveOnEntry)
  for instruction   call void @llvm.memcpy.p0.p0.i64(ptr getelementptr inbounds (i8, ptr @g_265, i64 16), ptr getelementptr inbounds nuw (i8, ptr @g_265, i64 16), i64 20, i1 false)
  is 0 = MemoryDef(liveOnEntry)
Clobber starting at access 1 = MemoryDef(liveOnEntry)
  for instruction   call void @llvm.memcpy.p0.p0.i64(ptr getelementptr inbounds (i8, ptr @g_265, i64 16), ptr getelementptr inbounds nuw (i8, ptr @g_265, i64 16), i64 20, i1 false)
  is 1 = MemoryDef(liveOnEntry)
Clobber starting at access 1 = MemoryDef(liveOnEntry)
  for instruction   call void @llvm.memcpy.p0.p0.i64(ptr getelementptr inbounds (i8, ptr @g_265, i64 16), ptr getelementptr inbounds nuw (i8, ptr @g_265, i64 16), i64 20, i1 false)
  is 1 = MemoryDef(liveOnEntry)
MemCpyOptPass: Forwarding memcpy->memcpy src:
  call void @llvm.memcpy.p0.p0.i64(ptr getelementptr inbounds (i8, ptr @g_265, i64 16), ptr getelementptr inbounds nuw (i8, ptr @g_265, i64 16), i64 20, i1 false)
  call void @llvm.memcpy.p0.p0.i64(ptr @g, ptr getelementptr inbounds (i8, ptr @g_265, i64 24), i64 1, i1 false)
Clobber starting at access 1 = MemoryDef(liveOnEntry)
  for instruction   call void @llvm.memcpy.p0.p0.i64(ptr getelementptr inbounds (i8, ptr @g_265, i64 16), ptr getelementptr inbounds nuw (i8, ptr @g_265, i64 16), i64 20, i1 false)
  is 0 = MemoryDef(liveOnEntry)
Clobber starting at access 1 = MemoryDef(liveOnEntry)
  for instruction   call void @llvm.memcpy.p0.p0.i64(ptr getelementptr inbounds (i8, ptr @g_265, i64 16), ptr getelementptr inbounds nuw (i8, ptr @g_265, i64 16), i64 20, i1 false)
  is 1 = MemoryDef(liveOnEntry)
Clobber starting at access 1 = MemoryDef(liveOnEntry)
  for instruction   call void @llvm.memcpy.p0.p0.i64(ptr getelementptr inbounds (i8, ptr @g_265, i64 16), ptr getelementptr inbounds nuw (i8, ptr @g_265, i64 16), i64 20, i1 false)
  is 1 = MemoryDef(liveOnEntry)
MemCpyOptPass: Forwarding memcpy->memcpy src:
  call void @llvm.memcpy.p0.p0.i64(ptr getelementptr inbounds (i8, ptr @g_265, i64 16), ptr getelementptr inbounds nuw (i8, ptr @g_265, i64 16), i64 20, i1 false)
  call void @llvm.memcpy.p0.p0.i64(ptr @g, ptr getelementptr inbounds (i8, ptr getelementptr inbounds nuw (i8, ptr @g_265, i64 16), i64 8), i64 1, i1 false)
...
```


Fix patch:
commit 378daa6c6fd10d3704be449f2fe9c55df522a6e9
Author: Yingwei Zheng <dtcxzyw2333@gmail.com>
Date:   Thu Aug 22 17:20:47 2024 +0800

    [MemCpyOpt] Avoid infinite loops in `MemCpyOptPass::processMemCpyMemCpyDependence` (#103218)
    
    Closes https://github.com/llvm/llvm-project/issues/102994.

diff --git a/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp b/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp
index 1d779128e454..3f15fa2163d2 100644
--- a/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp
+++ b/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp
@@ -1193,6 +1193,10 @@ bool MemCpyOptPass::processMemCpyMemCpyDependence(MemCpyInst *M,
       CopySourceAlign = commonAlignment(*CopySourceAlign, MForwardOffset);
   }
 
+  // Avoid infinite loops
+  if (BAA.isMustAlias(M->getSource(), CopySource))
+    return false;
+
   // Verify that the copied-from memory doesn't change in between the two
   // transfers.  For example, in:
   //    memcpy(a <- b)


Regression tests:
file: llvm/test/Transforms/MemCpyOpt/pr102994.ll
commands: opt -S -passes=memcpyopt < %s
tests: test_name: func2
test_body: @g3 = external global i8, align 1

define void @func2(ptr %p) {
entry:
  %gep1 = getelementptr i8, ptr %p, i64 32
  %gep2 = getelementptr i8, ptr %p, i64 34
  %gep3 = getelementptr i8, ptr %p, i64 32
  call void @llvm.memcpy.p0.p0.i64(ptr %gep1, ptr %gep3, i64 32, i1 false)
  call void @llvm.memcpy.p0.p0.i64(ptr @g3, ptr %gep2, i64 1, i1 false)
  ret void
}

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0

attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }

test_name: func
test_body: @g1 = external global i8
@g2 = external global [64 x i8]

define void @func() {
entry:
  call void @llvm.memcpy.p0.p0.i64(ptr getelementptr inbounds (i8, ptr @g2, i64 16), ptr getelementptr inbounds nuw (i8, ptr @g2, i64 16), i64 20, i1 false)
  call void @llvm.memcpy.p0.p0.i64(ptr @g1, ptr getelementptr inbounds (i8, ptr @g2, i64 24), i64 1, i1 false)
  ret void
}

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0

attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }