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

    <tr>
        <th>Summary</th>
        <td>
            Some loop idioms miss a const-propagation optimisation hinted by __builtin_unreachable().
        </td>
    </tr>

    <tr>
      <th>Labels</th>
      <td>
            new issue
      </td>
    </tr>

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

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

<pre>
    Given a trivial function `f(void*, size_t)` then calling it with `uint64_t x; f(&x, sizeof(x));` I'm hoping to see constant propagation yield a much simpler implementation of `f()`.  The caveat is that the constant length argument goes through an opaque function, and I'm trying to use `__builtin_unreachable()` to inform the compiler when it can deduce the result and continue with constant propagation:
```C++
inline __attribute__((const)) size_t secret(size_t i, size_t limit) {
    size_t r;
    asm("nop # min %0, %1, %2" : "=r"(r) : "r"(i), "r"(limit));
    return r;
}

inline __attribute__((const)) size_t test_bu(size_t i) {
    constexpr size_t limit = 12;
#if 1
    size_t r = secret(i, limit);
    if (i <= limit && r != i) __builtin_unreachable();
#elif 1
    size_t r = secret(i, limit);
    size_t rr = std::min(i, limit);
    if (rr != r) __builtin_unreachable();
#else
    // this isn't actually in my application; it just shows what happens with more transparency
    size_t r = std::min(i, limit);
#endif
    return r;
}
```

Depending on how `test_bu` is called I get varying degrees of success:

These work as expected:
```C++
bool test_ge(uint64_t& x) { return test_bu(sizeof(x)) >= sizeof(x); }    // compiles to constant
bool test_eq(uint64_t& x) { return test_bu(sizeof(x)) == sizeof(x); }    // compiles to constant
bool test_ge(unsigned __int128& x) { return test_bu(sizeof(x)) >= sizeof(x); }    // correct, subject to hint version, above
bool test_eq(unsigned __int128& x) { return test_bu(sizeof(x)) == sizeof(x); }    // correct, subject to hint version, above
```

The specialisation here works as expected, the generic implementation is probably OK:
```C++
void method_two(void* data, size_t length) {
    uint8_t* ptr = reinterpret_cast<uint8_t*>(data);
 size_t vl = test_bu(length);
    while (length > 0) {
 __builtin_memset(ptr, 0, vl);
        ptr += vl;
        length -= vl;
        vl = test_bu(length);
    }
}

void special_two(uint64_t& x) { method_two(&x, sizeof(x)); }    // compiles to one wide store operation
```

But this version misses the optimisation entirely, despite being almost the same:
```C++
void method_one(void* data, size_t length) {
    uint8_t* ptr = reinterpret_cast<uint8_t*>(data);
    size_t vl;
    for ( ; length > 0; length -= vl) {
        vl = test_bu(length);
 __builtin_memset(ptr, 0, vl);
        ptr += vl;
    }
}

void special_one(uint64_t& x) { method_one(&x, sizeof(x)); }    // compiles to a lot of code with branches and stuff
```

https://godbolt.org/z/vqb3bK8Pd

</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJzEV1tv27gS_jX0y6CBRPn64Ic4Pjko-nAOsH0XKGkssUuRKjmy4_76xVDyLU3TZNvFBkZk8zL8ZubjNyMVgq4t4lrMNmK2naieGufXoUkL52hSuOq4_q_eowUF5PVeKwO73paknQUxT3ZCLvdOV0LeC_kAQX_DnIRciXkC1KCFUhmjbQ2a4KCp4T29tjSf5gRPItsAWxBy_nTa7njgiU3Ilcg2bOijkIsWGtexIXIQEKF0NpCyBJ13napVBHTUaCpQ0PZlA0G3nUEP8dGipWGN251wDzDvAD43CKXaoyLQAahRxNgvRxi0NTWgfN2zHagd8jLv-roBZcF16muP57iwJ8pWI2zyxxF2H5CPzvOi14a0zXvrUZWNKgye4fBCbXfOtyOGttPsxYGDqQlKZaHCqi8xznsMvaF4XOksadvjEOeX4iOye5FsRXIv5snweRByw584qq3RFiHPFZHXRU-Y5xHXMhobUjKmGAKWHknI5fhbX9IPRreal4NYjKYB4DTpOannQRXaeIS0rgMhM2i1BSFnCZsTcpaOTymkBJHdg5BSZFvPD7n08ZBhdBzSEebD1cgJzcin89Eeqff2Co9YbMcv7w8HYaC86G_i8cz_uAufOn8TJhDZFlJ5ASEzvYP0-7DFheeox3CfPbt2S--A50FkD7xjPETOhZyzEZnyaET3GhGv4KD5NUCn9eMGqpiG2X2r7Rv88GfI_l2QA14sCfko5CNQowPoYIVcEKiSemXMEbSF9giq64wux0uy4Xv2pQ8EoXGHAAcWhEZ1HdowXK7WeQTyyoZOebTl8UfReYuzjNdWevcmZp4u7jVRt9ixAVuDs9C4A4vMiY_zhCWNRRgr-Ag1EuzVoEgV1h4xsCCGviwxhIs-xP-fGwwIB-f_BBUAnzosCaufiEjhnBluQ82JOWk9s-9pvBIn_27vzLXug8j-E-N3M55tQCy2VxkdxTGwZp707jkK_PorKLa_CcUQCxtrbQV5ri2lsez9E0HxHkuKctwXX7AkBtZoS7BHH071qXB7fDlWv4zybUF7J8oXmc-FO3RYamV0GMp7g37gbLghrXyI5bJGi16Xz3sCHbhMFqowR_jfp58wnNsdaJEaV-V0cJcGCCpF6roMxrbhuzrAZFwyF--ho0EnPGpL6DuPlJcqkMgeLqs47XI52L6SyPGQvYkWLgk5n3qjpodGG4TzNHMJkltoF21tsQ1R1Dvy7E8sxnvz3Cb_RQ_khiHszXez42EffjD9RuwX9bst0DERY_rHTLx40W9y9Uqr-cq9dpbbqgohEEu_69APxeLH3Nz0NJSckc_Q6hBi38j7SbcnxqIl7dEcGVWFodOEUCArtDKtC0MvGlSL7-Cls_hv8fJSA59lfOeYKkvgSN-w8PL7RJTn0N5Klt9M4TcRbwj1a8QbVvw94ikwjrhEl64aO_vCK1s2GGLTH6jf7V5hYUPUxboeTdeuKpyhO-drIR-_Cfm4_1pkxafl_6th-aRaZ9UqW6kJrtNFMpfT6WKVTpp1itNFVWRSKiXLaZmsqnlaZlky3a3S5WqZTfRaJnKapHKeTGdZmt7N5yop57sZrtJ0tkikmCbYKm3ujNm3jGCiQ-hxvVjNZunEqAJNiG-hUlo8QJzkBn62nfg17_lQ9HUQ08ToQOFihTQZXP_hWgTjXAe60q4N8baBGgryh-t3xJu7xwUHKyiOr3WWd5Pem_WzSGpq-uKudK2Qj4xlfPBRX2Jhe4weBCEfo4d_BQAA__9BJ53f">