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

    <tr>
        <th>Summary</th>
        <td>
            [clang] [x86-64] lrint()/lrintf() using instruction writing to 32-bit register if assigned to 32-bit int even though long is 64-bit
        </td>
    </tr>

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

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

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

<pre>
    This concerns a difference in the new optimizer behavior of clang 19, compared to clang 18 and older, and gcc as well. When building with `-O -ffast-math` or above, clang 19 makes programs behave quite differently compared to clang 18, and I think it's a bug though I'm not sure whether clang and llvm devs will agree.

According to the C standard, functions like `lrint()` and `llrint()` are to round their argument to the nearest integer, but the implementation can do whatever it likes with the return value if the argument is too large to fit into a `long` or `long long` respectively. On 32-bit systems where `long` is 32-bit, this means that lrint() and llrint() behave differently, and that's fine. But when `long` is 64-bit, `lrint()` is supposed to accommodate values large enough to fit into a `long`. This no longer happens with the clang 19 optimization, depending on how `lrint()` is used.

The crucial thing is the way that the instructions used behave differently depending on the size of int register written to. If writing to a 32-bit register like `eax`, then the float-to-int conversion _saturates_ the value at the 32-bit signed int range. If writing to a 64-bit int register, then larger float values can be converted, and this is necessary for example if you want to convert a value between the signed and unsigned 32-bit int max from float to int. And if you expect a larger value to wrap around in converting to a 64-bit int, such as a float between 32-bit and 64-bit int max, then suddenly this no longer happens when assigning the result to a 32-bit int, even an unsigned one, despite using a 64-bit C API function.

[Here's](https://godbolt.org/z/EzYT45fYr) a test function `use_rounding()` showing the difference in what clang 18 vs. clang 19 generates. (Modern gcc also behaves like clang 18.)
```
#include <stdint.h>
#include <math.h>

uint32_t use_rounding(float x) {
    return lrintf(x);
}
```

clang 18 output, `-O -ffast-math`:
```
use_rounding:
        cvtss2si        rax, xmm0
 ret
```

clang 19 output, `-O -ffast-math`:
```
use_rounding:
 cvtss2si        eax, xmm0
        ret
```

If this wrapper around `lrintf()` had returned `uint64_t`, then the outputs would have been the same. Likewise, if `llrintf()` instead of `lrintf()` is used, the output is also the same.

This works the same for `lrint()`/`llrint()` as for `lrintf()`/`llrintf()`, it's a slightly different SSE instruction but with the same choice of register size. It results in the same problem too.


As I mentioned at the start, I think this is a bug. I also had to change my software to fix its behavior with clang 19 (so that my audio code makes the right noises again, not wrong ones). But at least there is a change possible, using the ll-versions rather than l-versions of these functions. But depending on whether or not clang and llvm devs consider this a bug, that may or may not be possible in later versions.

After all, if you consider the new optimization to actually be valid, then there does not exist any reason for it to not be done for the ll-versions of these functions as well, other than it probably having been overlooked. If you do change those in the same way as well, then software like mine, and any other software depending on llrint functions handling larger values gracefully, will break, and the only recourse will be to disable optimizations when building with clang.
</pre>
<img width="1" height="1" alt="" src="http://email.email.llvm.org/o/eJysV11v6zgO_TXuC5HAdZImfchD2jvFFtjFLDAXWMxTQdu0raksZUU5H_fXLyjJidNm3rYoEFhfPCIPySNkVq0h2marl2z14wEH31m3_cuS_tw_lLY-b392iqGypiJnGBBq1TTkyFQEyoDvCAwdwe696tUvclBShwdlHdgGKo2mhcfnrHiFyvZ7dFSDt-P4BtDUYHVNTlbIR1tVgAxH0noO_-nIQDkoXSvTwlH5DrKnfPY7zJoG2c969F32lIN1gKU9UDCTTEKPn8Swd7Z12HOERfDfQXm63MHr811cI5p38J0yn6B8Vqzl8uXQgu_s0HbwnhXrHoz1wIMjOHbkO3LpCNms9aGHmg4MR6U1YOuI5lm-y_LdrqqsC5fyNrjwFdijqdHVYroZTOWVNQxafZLcWTtlfFZssuJZLizHy-jXYUdyoLODqeVY5QBdO_Rk_GjIEDpiD8p4aqPby8GHKdXvNclaFNtQoYHawrFDTwdyoHxAwzEOssGRH5yBA-qBQDVh7GJPMXhrQaNrA6hGBaMWMCC3pk2RS18wDjniPVVeHUif5_C7gUUxK5UHPrOnnsXRjqZnKE5L5DJe2NoTGgbfoYeJh1JQJgOJExM2jIGXvSHkjTI0h5fBi13zxezTcjT7PUSKgYf93nJkFlaV7Xtbo6foME6uIRPY9HcemkPIP2ODf8hBh_s9mUkULoRPKRiiJ5hq2pMJJLMGOnu8D3JgqhMrf8ppbqgU6sD7NgSxIzjiOXoz0MSwd0Pip-y-48Zb27KL1S-SiqCMB0etYk8Ojk55Twa8ncN7Ez5TTuAY9cvaMRMIT9lTHkNN8exGW_Qzb2dyeGXNgRwLgz8Y_eDQE3-EdZGo6Rojq6T81REWmpa-A4lBvgF-sR5C6CKAMaySNyUlHJ7qK6UUi0MNVcSM7gyNdUAnlLST9DnbAY4YUzXtBkygS_JHotGVAbKcOZj0kW4jIHs8QeNsn1B5K6Nz2Jl6NEInSTDAEX404S0cHe4BY_lQZgRxxxNyJx6qTko1JkMjxARF4E1c1-Pp4jUe6pqMPkeX3KG2LIqdKdgOpYYH7W-YkWDQQRabqyusoch-3kupH1jOuIB_hd2_3y8FNhE_W738gxxJwmerH1mx6bzfc7bYZcVbVry1ti6t9nPr2qx4-5UVb7_9-vPnctX86UJZAS8VdTxTSDowfQQ3KtNes407exwvdNtFpchem-KB59esbslQ4PAcsmLzL1uTM7FLarYp9VKfGA-Yi718J2kS__NdViyUqfRQE2SLV_a1cKLLFr99m5OeepnJd4MyflF8ePhypRjzk9w_W79k-Q4AxoYQikyTFRuZzhYym61_fEWU7y4XtoPfD2Md_dbeJQ63e2-whFlIf9XBMxesxm8XaXfqe9knAP8WxvP_A8ZX-3Rrf0R1D8Z7E_NBknBPbszDsWg3Vx51WCdXU5iXGD0tP_zXuhjvw3C0g64h1OjyUkSwpzn8U33SUXFIGNVcJcXEmJR7wlpq9x0oqYEks8mijAZ2XgyN_UWuZ90nX2ZCDfzWlyTt7qgbvlnd3F0-HX296jbWqu1CYxqbFPzxx2_TXhZk0KWpBmxVZ1UVutalC0kbm8O7TwWJRwkc1u-dLTX1onvGwpLvdgzvIIpI2VC1Y_dhjy4wbVSYY3cIEnMO79GBEmnpBp20JujPwLbxxyTzGnUC5fkquAP6C5uzYhNCgF424lAraSs1JWEcqqo4BYxVTAzYogq6QSTt0dnQu4mz4jnqHxFThBzwO4pYE7C9ZValDjSK9VZO13qWOjGDwyCOfYcGJsM2iEamq-KNtm7kw6isrQvQ7gnsyhpWdTAw-jBSUi6PZ9kqP7K9vMKV4GmUsI6ARnneyCBqnfJCmubExM2LJ4rloPD8gFqfxcIBtaqnuegIakscENBJsfTHMzhCtiawWoXmlgDW1sTM-OrG7_4aH0tizF59rHxgI5b6LIkvngypbw_ktLWfVAehIxerL_zynWW6YbQIv4mB2L1HCoae06vYbiUecqWI4bLmJpAxQyfYOzS1lsmpEGFoHVbUDDrK8fB4Kh3h51VJEVjRD44qOzimtCZkRa0YJbbT8CRJcfuSDDyaP9TbRf28eMYH2j6uF5vFerl5Wjx022VJiIumaIrlekVrWm8en3NcNWXzmOdUrR_UtsiLVV7kj_njalks5vlqRauqytf5pmiWa8yWOfWo9Fx4KuLhQTEPtH0sVoti-aCxJM3h4V0UAUxWFPIGd1vZMCuHlrNlrhV7vh7hldfhtR53rH5Atno5bZ5mT0v5mJbM4m1aEVNiTiveROZ-VduqSQIsvl4m8jLorfQEDu-2yzvoYXB6-0U5Kd8N5byyvaDRh_Fntnf2L6p8VrwFn3BWvCW3HLbF_wIAAP__7ISTzA">