<table border="1" cellspacing="0" cellpadding="8">
    <tr>
        <th>Issue</th>
        <td>
            <a href=https://github.com/llvm/llvm-project/issues/130632>130632</a>
        </td>
    </tr>

    <tr>
        <th>Summary</th>
        <td>
            [llvm][opt][regression] `memcpyopt` pass inserts incorrect `memmove` call
        </td>
    </tr>

    <tr>
      <th>Labels</th>
      <td>
      </td>
    </tr>

    <tr>
      <th>Assignees</th>
      <td>
      </td>
    </tr>

    <tr>
      <th>Reporter</th>
      <td>
          HertzDevil
      </td>
    </tr>
</table>

<pre>
    The following LLVM IR, which is already partially optimized from earlier passes, allocates two `{ i64, i32, i64 }`s and swaps their contents using a non-default allocator function: (for less minimal reproductions see https://github.com/crystal-lang/crystal/issues/15543)

```llvm
target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"

define void @main() {
alloca:
  %0 = call ptr @GC_malloc(i64 8)
  %1 = call ptr @GC_malloc(i64 48)
  store ptr %1, ptr %0, align 8
  %2 = getelementptr inbounds nuw i8, ptr %1, i64 24
  call void @llvm.memset.p0.i64(ptr noundef nonnull align 1 dereferenceable(24) %1, i8 18, i64 24, i1 false)
  call void @llvm.memset.p0.i64(ptr noundef nonnull align 1 dereferenceable(24) %2, i8 52, i64 24, i1 false)
  %3 = load ptr, ptr %0, align 8
  %4 = load i64, ptr %3, align 8
  %5 = getelementptr inbounds nuw i8, ptr %3, i64 8
  %6 = load { i32, i64 }, ptr %5, align 8
  %7 = getelementptr inbounds nuw i8, ptr %1, i64 32
  %8 = load { i32, i64 }, ptr %7, align 8
  store i64 3761688987579986996, ptr %1, align 8
  %9 = getelementptr inbounds nuw i8, ptr %1, i64 8
  store { i32, i64 } %8, ptr %9, align 8
  store i64 %4, ptr %2, align 8
  store { i32, i64 } %6, ptr %7, align 8
  ret void
}

declare ptr @GC_malloc(i64)

declare void @llvm.memset.p0.i64(ptr, i8, i64, i1)
```

Running just the `memcpyopt` pass used to produce the following:

```llvm
  %0 = call ptr @GC_malloc(i64 8)
  %1 = call ptr @GC_malloc(i64 48)
  store ptr %1, ptr %0, align 8
  %2 = getelementptr inbounds nuw i8, ptr %1, i64 24
  call void @llvm.memset.p0.i64(ptr noundef nonnull align 1 dereferenceable(24) %1, i8 18, i64 24, i1 false)
  call void @llvm.memset.p0.i64(ptr noundef nonnull align 1 dereferenceable(24) %2, i8 52, i64 24, i1 false)
  %3 = load ptr, ptr %0, align 8
  %4 = load i64, ptr %3, align 8
  %5 = getelementptr inbounds nuw i8, ptr %3, i64 8
  %6 = load { i32, i64 }, ptr %5, align 8
 %7 = getelementptr inbounds nuw i8, ptr %1, i64 32
  store i64 3761688987579986996, ptr %1, align 8
  %8 = getelementptr inbounds nuw i8, ptr %1, i64 8
  call void @llvm.memcpy.p0.p0.i64(ptr align 8 %8, ptr align 8 %7, i64 16, i1 false)
  store i64 %4, ptr %2, align 8
  store { i32, i64 } %6, ptr %7, align 8
  ret void
```

Since LLVM 20 this becomes:

```llvm
  %0 = call ptr @GC_malloc(i64 8)
  %1 = call ptr @GC_malloc(i64 48)
  store ptr %1, ptr %0, align 8
  %2 = getelementptr inbounds nuw i8, ptr %1, i64 24
  call void @llvm.memset.p0.i64(ptr noundef nonnull align 1 dereferenceable(24) %1, i8 18, i64 24, i1 false)
  call void @llvm.memset.p0.i64(ptr noundef nonnull align 1 dereferenceable(24) %2, i8 52, i64 24, i1 false)
  %3 = load ptr, ptr %0, align 8
  %4 = load i64, ptr %3, align 8
  %5 = getelementptr inbounds nuw i8, ptr %3, i64 8
  %6 = getelementptr inbounds nuw i8, ptr %1, i64 32
  store i64 3761688987579986996, ptr %1, align 8
  %7 = getelementptr inbounds nuw i8, ptr %1, i64 8
  call void @llvm.memcpy.p0.p0.i64(ptr align 8 %7, ptr align 8 %6, i64 16, i1 false)
  call void @llvm.memmove.p0.p0.i64(ptr align 8 %6, ptr align 8 %5, i64 16, i1 false)
  store i64 %4, ptr %2, align 8
  ret void
```

The new call to `llvm.memmove.p0.p0.i64` seems to assume that `%5` and `%7` can never alias, when they should be identical here. Adding `noalias` to `GC_malloc` does not appear to help, and I doubt this attribute is necessary.

The first PR that broke this was #108535.

[Compiler Explorer link](https://godbolt.org/z/Gfeja4M6c)
</pre>
<img width="1" height="1" alt="" src="http://email.email.llvm.org/o/eJzsWEuP2zYQ_jX0ZWBDIkVKOvjgjbtpgAQokqLXgpZGNhOKFEhqXefXF6SsfcXZtJs-cljAsEj7m5lvyNHHh_Re7Q3imvArwrcLOYaDdeuf0YXPW7xRerGz7Wn96wGhs1rbozJ7ePv2t3fw5j2hr-B4UM0BlAepHcr2BIN0QUmtT2CHoHr1GVvonO0BpdMKHQzSe_TRVmptGxnQQzhaICIj5RUoUcT_FKPpIQog5ZaIzIM0LfijHDyEAyoHjTUBTfAw-khKgrFm2WInRx1m39ZBN5omKGsI2wChVWcdaPQeemVULzU4HJxtx4Tx4BHhEMLgCdsQek3o9V6Fw7hbNbYn9LpxJx-kXmpp9nddQq-V92PM6jrnvGCE1iTbxI_Ipo_WNz3JNkG6PQZoZZBanuwYgLAtEEpx2RO2OS4HWmaEbRhNX7GbP-zGlijS11LdNnJaEbbJabXsquzcMuknMZuLYvkhwiidqLXYKYNwY1ULpMh6qQyJf9dAyiuSbaYRjMOQbQAI5Vni2kitYQgu2rx-9XufYIRWcaaqKe-Ezr-JLm7hPliHE4zyPM77uZ1NZaL2BqrZMU2O9xhQY48mRKgyOzua1oMZj6Cqex7yuYpokRwkQnPOcVJWPfYew2rIVqn0qmhoojfsYkWZUeszhRxadNihQ9Og3GkktKJFGrE5UAV5dS9ibOXQSe1xzvVfI0DPBDh9mgChnKUh1Fa2cZi-Md7FHfj8bp7B7AKY_63JYTPTW3txFyyJwUMVuLPkF2KXzysMRmcH1V8MXj4OPtVvclaKXFRVXZW8rOtK1LV4HPMR6_p5rO8H_pJsyuaeWf11xnGG7yHpZeTlEOKpQXEYUp1HtSm3s-Y0Ws6v-heKcKuaM-xbr8lU8WdSU62ffcyyO_l7PxoTl4iPow9x8YhrTY99M5zsEIjI0poEo8cWgoVpOcAEvF3zJh28oOgv0vgijT-0NH63Mn6PulXfo24X66EZTrEeHpTEOegD1bv3Wzm7zcWlWf-PtfChNH1QpsFpQ00zCAflYYeN7dG_KM6L4vwIivN_yMYzBev5slFekA3xtGxcDNPbG3wqjrgQh_9T8vR1lYnHd4PHiXNIZ-2vMBZZPAL3PqKk92Mf90EypNN5pCqydA6fumXsNtKAwRtMOUk_XQqgidunE_iDHXULOwTVogmqkRoO6HAFm7aNWzIiMmMnQ5Gdqd1pk8igtejB2AByGFC6CDmgHlLupoU30NpxFybllCE4tRsDgvJgsEHvpTut7sagU84H-OX9lNPO2U84WR6lB0JZnlWc8bMB4VevbD8ojQ5--mPQ1qEDrcwnwreEVo9uCWy7szqsrNsTev2Z0OvXHX6UxTvREFov2jVra1bLBa7zssgFZaJii8M6Z13BcFcwWdCSN5TVvJA8LxiTnGNdLtSaZpRnLM9yQXNKVwWvuahk1YkKGRc5KTLspdKrNKHW7RfpKmKds0wwutByh9rPtztuHVHL3bj3sWiVD_7OLqig0z1QWmf4lvCruD1ODYd7h94rawjfXt4-K-PRhfhsrHPYhDMsFthUJ1ovRqfXT1yvpMjTYzk4-xGb8OBuZcrpZk3_DAAA__84_m5q">