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

    <tr>
        <th>Summary</th>
        <td>
            Incorrect codegen for whole program devirtualization
        </td>
    </tr>

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

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

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

<pre>
    (Mostly copied from https://bugs.fuchsia.dev/p/fuchsia/issues/detail?id=124874 which has full context.)

```
// This is /tmp/test.cc
#include <fstream>
#include <iterator>
#include <string>

#undef NDEBUG
#include <assert.h>

class LogStreamBuf : public std::streambuf {
 public:
  LogStreamBuf(){}
};

LogStreamBuf streambuf_;

int main() {
  std::ifstream file;
 file.open("/tmp/test.cc");
  assert(file && file.good() && "Can't open file");

  auto begin = (std::istreambuf_iterator<char>(file));
 auto end = std::istreambuf_iterator<char>();

  std::string contents;
  contents.assign(begin, end);
  assert(!contents.empty() && "empty file");

  printf("%s\n", contents.c_str());
}
```

This compiled with `./bin/clang++ /tmp/test.cc -std=c++17 -fPIC -target x86_64-unknown-linux-gnu -ffunction-sections -fdata-sections -Os -fvisibility=hidden -mllvm -wholeprogramdevirt-branch-funnel-threshold=0 -flto -fwhole-program-vtables -fvisibility-inlines-hidden -fno-exceptions -fno-rtti -g -Wl,--icf=all -fuse-ld=lld -Wl,--gc-sections -D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS`

For llvm revision `fa3cb517e4a569ff075c98bb6d037abf38b88202` results in incorrect codegen such that running the executable results in:

```
a.out: /tmp/test.cc:25: int main(): Assertion `!contents.empty() && "empty file"' failed.
Aborted
```

even though the file `/tmp/test.cc` exists. We hit this assertion in this example because the method `basic_streambuf::underflow` is devirtualized in a place it shouldn't be. The final exe has these bits (dumped from llvm-objdump):

```
; /usr/local/google/home/leonardchan/llvm-monorepo/llvm-build-1-master-fuchsia-toolchain/bin/../include/c++/v1/streambuf:197
; if (__ninp_ == __einp_)
   5affd: 49 8b 44 24 18 movq    0x18(%r12), %rax
   5b002: 49 3b 44 24 20                cmpq 0x20(%r12), %rax
   5b007: 75 15                         jne     0x5b01e <main+0x103>
; /usr/local/google/home/leonardchan/llvm-monorepo/llvm-build-1-master-fuchsia-toolchain/bin/../include/c++/v1/streambuf:198
; return underflow();
   5b009: 4c 89 e7 movq    %r12, %rdi
   5b00c: e8 4f bc fd ff                callq   0x36c60 <_ZNSt3__215basic_streambufIcNS_11char_traitsIcEEE9underflowEv>
```

Here `_ZNSt3__215basic_streambufIcNS_11char_traitsIcEEE9underflowEv` which is `std::__2::basic_streambuf<char, std::__2::char_traits<char>>::underflow()` is the devirtualized call from 

```
; /usr/local/google/home/leonardchan/llvm-monorepo/llvm-build-1-master-fuchsia-toolchain/bin/../include/c++/v1/streambuf:197
; if (__ninp_ == __einp_)
   5affd: 49 8b 44 24 18 movq    0x18(%r12), %rax
   5b002: 49 3b 44 24 20                cmpq 0x20(%r12), %rax
   5b007: 75 17                         jne     0x5b020 <main+0x105>
; /usr/local/google/home/leonardchan/llvm-monorepo/llvm-build-1-master-fuchsia-toolchain/bin/../include/c++/v1/streambuf:198
; return underflow();
   5b009: 49 8b 04 24 movq    (%r12), %rax
   5b00d: 4c 89 e7                      movq    %r12, %rdi
   5b010: ff 50 48                      callq *0x48(%rax)
```

When doing the istreambuf_iterator comparison in the string assign, the first iterator attempts to get a character off the buffer via `basic_streambuf::sgetc` which is defined as:

```
 int_type sgetc() {
        if (__ninp_ == __einp_)
            return underflow();
        return traits_type::to_int_type(*__ninp_);
 }
```

This calls `underflow` which is a virtual function defined in `basic_streambuf`. The issue is that the `basic_streambuf` being used here is actually a `basic_filebuf` from the `std::ifstream` which is a subclass of `basic_streambuf` and overrides `basic_streambuf::underflow` with its own implementation. Because we somehow devirtualize instead to `basic_streambuf::underflow` which just returns EOF, the comparison already assumes the iterator reached EOF and doesn't assign anything to the string.

Still need to dig deeper with why this incorrect devirtualization happens.
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJzsWFtv47rx_zTMy0CGREm2_JAHX-L_P8U2u0DSHrQvBkWNLG4p0ktSidNPX5CUb0l2T4q-HBQVDNuiOPeZH2fErBU7hXhLyiUp1zdscJ02t9_En_Qg0d7Uunm9JbT6s7ZOvgLXe4ENtEb30Dm3tyRfELohdFMPOztpB95ZwSYNPhO62RO6GVcI3QhrB7SEbhp0TEiSb0RD8nVGi2pWwEsneAcds9AOUgLXyuHBTQidk3RN0sX4PU3HT7wNkuGpExaEBUI3rvdCHVo34fy4KReKy6FBIPmqtc4g60l-99FT4dAwp81PHltnhNqdHx63DKrBFh7Wd8u__N9HdMxaNG7SvaHkklkLX_TuMei0HFog-QL2Qy0FB-sa79x8ETWu_dPZMlKOe_zzeH_FhdDK-222JLP1KG-2JvnyUvaV1JOE7ZtdQjnomVCR44X8s3Zi9Ci0QuKJPNxN9B4jKX0XGr80P2-H6CJCK08IhE4JnUYmO62bo_y4TChdMUXozIGXECVfMzyxHZyGGndCAcnXQGh1Vvxs9TnsK96xEP2oiGd5qWZgh6oJzD7N6SPFLqMr1C4mvHL2wiXHpUmsUUKrYAihK6_CT9xHaHaiw37vXt-5Lqz-ymd7I5Rrj3ErLSlXKvxdnVXiW-vMaNoli1PCvSnU8B3qlOt-LyQ28CJcB2SaTjx4eLM2XDK1I3RJ6PJdLUMSPLbm8Xk2g6T9dr-CxDGzQweHarqdFsmg_qH0i0qkUMMh2akBkrYdFHdCq8Ri-LWQtA1z7OL-q197FlbUQgr3SvJ1J5oGFSS9lM89JC-dlrg3emdY3-CzMC6pDVO8S9pBKZSJ6wzaTkuvYwpJK52GpA1kyUiXPDtWS7wWlQglhUKbHAW2Sid44Lg_qqp0YpwTkOwg-U0SukoSwVuSr5mUkLSDxSRIlbI5bdjxC-PW2y_3y9W3b9v1_eNi-eVu-9f7x_vl_Zf7p79tFw8PX58WT_dfHx7fBGujDQTbDXpttfKxalnO6zKbYcHK6bxt01nJ51VdT5s0n7G6zau6qmhKyTQFg3aQzoJQIBTXxiB3wHWDO1RgB96B65gDMyjlC8B1CHhAPgQnXVCfQO7DzGITPTiPmu8QJl_Q0j-4xjC_sgjFMpr0bxfMDFrmE3gSFVjU2jhsfpH2-IwKXKeHXResjAjnJb_ReJoCHoR1dgK_IXTCgfMVw07qChVX8MD6vUSokbPBYuDao-t04_nWzIpQnxGTIsz4I8q0Ur94KcJCTOKBSfFPbDxjBnvJOIJwYDs9yCYCbI0TeApKKyZ9gMIZ7Tq0CLVw_tCtmqHfH5sCnzKJrr_7tejuX53feSj0wRpCN1JzJgnd7LTeeVdvOt37H4laMdPwzkP-JvDvtdIG9_p4Xw9CNkmW9Mw6NMnYcyROa8m7EPoRYSYebMaD2QNOBBNCN88ZoZtLl2Xz2VlH0Xozt1sl1H7rsd_D_3aL_vbUoQBAydrWwzoUc6hqKAqgBWQV9Pr5BwBAesiqkF6lyQL00hX4G3Y4s6jTlI4s8iMLmsKbi_f7H5AeaPoJdjPPblZCVr7lcrq-K4SoYFmnWehZYsks00OW5ue-5Y8csOqso0E3GAXnpL8-hEfHBDAoOFRzwNkpSEd3Rl824orEwwpgBUULNYe2gbZ9Fxom5Y_gy3zKp6n35fbvD48u325pVr4pznv-8LjNMt8rbJ1hwtl7fnd3Nz9pfvd8dv5H4PL_aAKY_GcipunYgPsuepqempPtlsY_7zAltjd0Be_3Xki6aIP85xqIYkwiHHkEu4Yk78YIKf8DkD8KgMw-ByA0fQMg5X81gIRQpcHPZwz5Xac2V-Dz4fUZRMpSz6dtoUyhqD7mExGJ0EV6KI7pww7n0fojXPmtQwWNPnZmH4w4oZNnRthjY4IwzjLHeWU1tjvGOjhRMed8Q2XBafCNOwOPEIw7NKDbNpDUQ9uigWfBftrP2B06foVbDbZCYQPM_rrp8A3h1r3uESKPd6NtvD5bsqfr91PmclvEx6BHNMjp7VGxQLkYRV_x-Mx8xaQMIH7V8Z28xGAEWTiORSfHCfWRt6dpbADD65sI1cyFKH24GWr0KTBYbKDzZ5OXyb1A-QoX4fRN8EgRQH5k-O61whvl7VDHtya6_Yl8phrQz2iMaNB-rh0Ok6jvZfWLAuE76x6VY945E1iOPfYLgtU9dvrl6qACoaxD1vh0_pywYMz3wboxFSzcfd0ci-Wippg0yJpXX01Dj_GIPFWRQcY7bDxpsLjRaGPDHosPmHp1XahefVGak8t0eXRCSlCIQflG7KBB3KOJ_njpXuOwcR7eLuwOzoGO7feo7OSmuc2beT5nN3ibTau0rGbTqrzpbjNe1Bmbc8bybJ5VBZuWfIYFrYoK57TJb8QtTWmeFmmRlmVRlJNpyWjZTBnm8zKdFzkpUuyZkBMP-xNtdjchEW-n2ZzSG8lqlDa8u6T0y9NXP6CV6xtzOx4SO0uKVIaR6kTvhJN4e_9uJG21gTCvwzivv7P3ZjDy9vqt5064bqgnXPfjyXQ8oPZGf0fuLt97BpX_FQAA__81B3lC">