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

    <tr>
        <th>Summary</th>
        <td>
            tailcallelim introduces write to readonly byval parameter
        </td>
    </tr>

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

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

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

<pre>
    With the following IR:
```ll
define void @foo(ptr readonly byval(i64) %x) {
start:
  %new_x = alloca i64, align 8
  store i64 0, ptr %new_x, align 8
  call void @foo(ptr %new_x)
  ret void
}
```
`opt -passes="tailcallelim"` results in (https://godbolt.org/z/9arGs5xhx):
```ll
define void @foo(ptr readonly byval(i64) %x) {
  %new_x1 = alloca i64, align 1
  %new_x = alloca i64, align 8
  br label %tailrecurse

tailrecurse: ; preds = %tailrecurse, %start
  store i64 0, ptr %new_x, align 8
  call void @llvm.memcpy.p0.p0.i64(ptr align 1 %new_x1, ptr align 1 %new_x, i64 8, i1 false)
  call void @llvm.memcpy.p0.p0.i64(ptr align 1 %x, ptr align 1 %new_x1, i64 8, i1 false)
  br label %tailrecurse
}

declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg)
```

`%x` is now written to, but it still has its `readonly` attribute.

LangRef seems to [imply](https://llvm.org/docs/LangRef.html#parameter-attributes) that this is illegal:

> The copy is considered to belong to the caller not the callee (for example, readonly functions should not write to byval parameters).

However, Alive2 is fine with writes to `readonly byval` arguments (but does flag identical IR without `byval`): https://alive2.llvm.org/ce/z/T6yxU-

---

This leads to an end-to-end miscompile, where the following recursion (always terminating after one call):
```ll
define void @foo(ptr noalias byval(i64) %x) {
start:
  %new_x = alloca i64, align 8
  %x_val = load i64, ptr %x, align 8
  %is_zero = icmp eq i64 %x_val, 0
  br i1 %is_zero, label %end, label %recurse

recurse:
  store i64 0, ptr %new_x, align 8
  call void @foo(ptr %new_x)
  br label %end

end:
  ret void
}
```
is converted to an infinite loop with `opt -O3` (https://godbolt.org/z/9zbn6hno9):
```ll
define void @foo(ptr noalias nocapture readonly byval(i64) %x) local_unnamed_addr #0 {
  %x_val = load i64, ptr %x, align 8
  %is_zero = icmp eq i64 %x_val, 0
  br i1 %is_zero, label %end, label %recurse

recurse: ; preds = %start, %recurse
  br label %recurse

end: ; preds = %start
  ret void
}
```

If LangRef is right, the bug is in tailcallelim. If Alive2 is right, the bug is in whatever other transforms assume that `readonly` is meaningful on `byval` arguments.

Upstream issue: https://github.com/rust-lang/rust/issues/114312
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJzUV0uP2zYQ_jX0ZWBDpmytdPBhN67bBQoUCBL0uKDEkcSUIlWSWq_z64uhbMveR5o02EMBQxIfM_NxHt_QwnvVGMQNW9-x9XYmhtBat0Gn_pLovwgnlfGz0srD5k8VWggtQm21tntlGrj_yNJblmxZcsuyZPxpPU5IrJVBeLRKAlsltbWM531w4FBIa_QBysOj0IznKlsxXgDj66f4vrkbNfggXDgbANpgcP_wBCzdgtDaVgKi7AcQWjUG8tNOH6xDWoOEVsnqSfiV3ZXQ-hWck0Rx2ukwxI3HI99sn539PLR9gHkvvEfP0i3jPAilyQ5q1THOWZaAQz_o4EEZYDxvQ-g9HZbvGN81VpZWh4V1DeO7r4zvCuF-9eunNsJ5R6dPbl6-6eflj0ekdKBFiZpEyBcOq8F5PB4jPi-n01tg6R30DqWPyp-J8Q80MybIz8dc68du0WFX9YdFn9AvHiI67njiySsn5c8XaJ6s5_FjCbXQBLT4GYtPbxlb_pu1b_v7nLjHpKm0cPhd4IwVWgkPxlaiD4ND2DsVkFLrhPXlllPyHUEfIauuE5TexetFdJ4kN2QJKNK4j-YCGgiW1JRDABXAB6U1tMKDCh5YlpwtZgmIEJwqh4CLS82_C9N8xBo8YuchWGDrO9X1-sDW2xflGD0y1qK0lWd8dxRftKHTjKe9cKLDgG5-tuaprEIrAoRWeYKvtMZG6Kl6x2f6C3xqESrbH2hXZY1XEh1KQlWitqahL2LeyCDk4TANkdijtg7wSXS9jrVxrvZ6MFVQ1njwrR20jKIxYlE5cQGcsRPiKx_9Zvf4iI403mr1iJzwRYLZUy-IekbfTR4_Egz53TVDh4YCwnMKlLToodaiASXRBFUJDfcfoy47BFJykh05Dq6DICKExUUsKjyS46fs8PR5fgl9Pr8afqIQaBQywhUG0Mh5sHM0EjrlK9v1anTdvkWHz9rcWDzKRqIWei8OHgK6ThkRaF3UAR1YM0bkvxH0qWrerymSqgcKOO3UVsjTviNVvkaTjK-Vf_iKzkYpVXU94N-ReU7qSCq5oB21vJCixTMPoZFX41d6wMT_793KLwmSgF2AoOEE4Pt6_li4j-jCWLfCgDK1MlRp2tp-rJjTzeCPlArke7r-19JkrbHFzyXVSyp-K88of_TDYIzoUD4IKcl5afLsfvA_yqOX94ixgsYbxJXodVK8onVMjLc0_tgdMT7vazj1IeXBqaaNwIh9yqGJPcPA5dVxAff1BRe_IbFvRSDiBhtadBCcML62rvMgvB86HNvSszapPHQojDJNPWggrpv4eOLyq_7wuffBoehAeT_gS8ZuVGiHclHZjvGdG3yYa2Ga4zfjuyhG3XS5XKVLPpObVBZpIWa4WWZFkmfrtMhn7aa8WeWrdb5Gnmf5DaYiXWXFcoXZMkmSNFvN1IYnPE3yZJmsVsU6XyxlUq5RropEiBzTmq0S7ITS5-4xi7Y32YrnxSzG3Me_QJyf24Ggxkl39fV25jYkOC-HxtMFSfngJ1VBBY2byzCBMsFZOVTop257XXpT250NTm--4Tiyc3zNe2e_YHXlu3iEfwIAAP__pl8zlw">