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

    <tr>
        <th>Summary</th>
        <td>
            Cross language lto fails in presence of C++ destructor
        </td>
    </tr>

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

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

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

<pre>
    I'm working on a C++/Rust interop tool called [Zngur](https://github.com/HKalbasi/zngur), and when benchmarking it, I noticed this problem. Here is a reduced version without any Zngur related code.

This C++ code:

```C++
#include <cstdint>
#include <array>

extern "C" {
    void push_to_vec(void *v, uint64_t i);
 void new_vec_in_stack(void *v);
    void free_vec_in_stack(void *v);
}

struct MyVec {
    alignas(8) std::array<uint8_t, 24> data;

 MyVec() {
 new_vec_in_stack(reinterpret_cast<void*>(data.begin()));
    }

    // ~MyVec() {
    // free_vec_in_stack(reinterpret_cast<void*>(data.begin()));
    // }
};

void build_vec(int n)
{
    MyVec v;
    void* vec = reinterpret_cast<void*>(v.data.begin());

    for (int i = 0; i < n; i++)
 {
        push_to_vec(vec, i);
    }

 free_vec_in_stack(vec);
}


extern "C" {
    void do_the_job()
 {
        for (int i = 0; i < 100000; i++)
        {
 build_vec(10000);
        }
    }
}
```

Becomes significantly (2x) slower if I use the destructor (commented out) instead of manually calling `free_vec_in_stack` at the end of `build_vec` function. Even when I add an empty destructor, it will become 2x slower. Marking the destructor as `inline` doesn't help.

Here is the Rust driver code:
```Rust
use std::ffi::c_void;
use std::time::Instant;

#[unsafe(no_mangle)]
pub extern "C" fn new_vec_in_stack(v: *mut c_void) {
    unsafe {
 std::ptr::write(v as *mut Vec<u64>, vec![]);
 }
}

#[unsafe(no_mangle)]
pub extern "C" fn free_vec_in_stack(v: *mut c_void) {
    unsafe {
        _ = std::ptr::read(v as *mut Vec<u64>);
 }
}

#[unsafe(no_mangle)]
pub extern "C" fn push_to_vec(v: *mut c_void, i: u64) {
    let v = unsafe { &mut *(v as *mut Vec<u64>) };
 v.push(i);
}

unsafe extern "C" {
    fn do_the_job();
}

fn build_vec(n: u64) -> Vec<u64> {
    let mut r = vec![];
    for i in 0..n {
        r.push(i);
    }
    r
}

fn main() {
    let start = Instant::now();
    for _ in 0..100_000 {
 std::hint::black_box(build_vec(10000));
    }
    println!("Pure rust = {:?}", start.elapsed());

    let start = Instant::now();
    unsafe {
 do_the_job();
    }
    println!("Cross language = {:?}", start.elapsed());
}
```

Here is the result of the with destructor version:
```
Pure rust = 1.57105235s
Cross language = 3.138335498s
```
Here is the result of the without destructor version:
```
Pure rust = 1.633161618s
Cross language = 1.655836619s
```
And this one is the result of without destructor version, but when xlto is disabled:
```
Pure rust = 1.608407431s
Cross language = 3.019778757s
```
I enable xlto using this command:
```
cargo clean && CXX=clang++ RUSTFLAGS="-Clinker-plugin-lto -Clinker=clang -Clink-arg=-fuse-ld=lld" cargo run -r
```
And here is my `build.rs` file:
```Rust
fn main() {
    cc::Build::new()
        .cpp(true)
 .file("job.cpp")
        .flag("-flto=thin")
 .compile("libjob.a");
}
```
</pre>
<img width="1" height="1" alt="" src="http://email.email.llvm.org/o/eJysWF2P6jgS_TXmpUSU2BDoBx5ouOy0dkdazcyuRvuCnKQCvtfYyHagex_2t6_KSWg-0n137k6EukPsqjqnXHXsIL1XO4O4YNNnNl2PZBP21i1--qvUhfRqVNjqbfHC-OwAZ-u-KbMDa0DCivHn-Nn80vgAygR09gjBWg2l1BorYNPnf5ld49h0zfh8H8LRM7FkfMP4ZqfCvimS0h4Y3_TBGN_8OxrwJ8ZXIE0F5z0aKNCU-4Nso6tAYy9gbFAlVhD2ysPR2ULjIYGf0CEoDxIcVg2Nn9B5ZQ2cVdjbJoA0bxBhgUMtA1ZQ2goTli5ZuvyNnHXU4nMCHEdYnrafnni6ZFwoU-qmQmBiVfpQKROY-PIwJJ2Tb91AusTXgM4A43zFOAc2I18AACerKjg2fr8NdnvCkvF5fMT48kScG2VCPtkGUJQgEc3iBINnmr9VZuuDLL_dGvZT-wi1Q_z-dDZbt3B9cE0Z4Oe3f2J5BVZqtTPSMz6fM_4EPlSUKrHsuK4I7Hwb14pPmPgClQyyc50uW3eMR9vO6QALh7Gujg7DtpQ-MLEipIwvKZt8Tj6TAnfKtK66z4XvhUT8EgsP_jMQ-n10KDn_N4rWdQdmtr5kIWa9aJSuuuVWJoAhW5rXm7eZP92uIuNLONGCiDV8D98pGUJ4WQoAqK2DLryKLlMmnuPtCky87budoF1lja67iqW_q-sSvVmHweIjk_uy-26jVHYb9rj9aouO0wOwz1hlKV2P1Lqrc3S9NtHihtU7syuO7d9eLFoiz1jaA3ogpVW1KqUJ-o2g8dfYO9qe0YGq4QUajxD2CBW2fddSKO3hgIa0yjaBTJTxAWUFtoaDNI3U-i2qLgkky9PHLOcpyBA9o4lmLE_f2eUp1I0pg7ImgS8nNK3uvoCsKpAG8HAMb1eY4goHOCutoYjkgL92PBL4uVPqOx7SU1BltDJIESuL3jA-C7BHfewEuNdvso0bS-XUCd21Fve5pWGWLiljF_Wpa9XelNvYAnGtbmYEdcD27sX4IEmwu0ZgXLDpc2O8rJHxubHbgzQ7jbTmU1rVY1PAXU3WZlB8mViSmh6aAB2QG61pY_QPLtCOwbU3Z6cCQTjFnLV-SLPEqsknsadXEIsyazft96q8KcIfpjTYpH-IU3dtY9s9EnQoq0_5_bl87hRqgAkJllgChb9hpTHAKZJ45weM52RL-vopB7hoPZwSwkBi9Kh0necP1a42j1p366E2N0plrqiMaeu9xnVHjmC7SPCmoC4aRxqqQBlIk8Tcra57JHUrie4O5EH2G9AdCh-kCxHFpSmpTIw9X_Pt8Ww7PFmabtM0feijveodFFqW37aFfWV8Pqjlw7iPTpmgDaWDwvO_Nw7BkRoRRApH3jdkwDkVT8SfoJZHj9XwFvvHaN520_Dyf4555az3oKXZNXKHPwR8aCu71meHvtGBNhP6Rufra7nvzt13os3S5W02s2Q6y9IpF1PP0uUAapFkYi7EdPI093eePgdDh_0fwZMLkeVZns0_AJQl-XQ6F3mePd0DWprufcSaAWCfgOIrKJrQ7rqvOlgyrpSXhcbqf0CczifpbCKyD1OYZk-z2Xw2nd0jfgE0FKWN2vh231Ye6MghzUDwUrqdhVKjJLHKGc9h9fvvTKxLitm9Ov3yj19_2_xt-ZdfmaBSG6-0Mt_QjY-62Skzplj9s96yezCWbsfEelw3Hse6YmKtdUWC2MZ1jYGxG0j7vquFw9vlaJM4H082Sn9wdPhIksqybcxn8tL1KJ7fj5ndlZTHI-Pz4BrsBpIYK_bfV1u04_zOqNZy104Z1zpYJtZhTxj6efROfLy40aogT7IbH2rMUbUQ1ZN4kiNcZLPJ7CnLcsFH-4Wgm6d8Vk24TLHETCCWdYU4zYtaTnCkFjzl03TKeZalqeAJn-T5pJ5J5Fk6yzJkkxQPUulE69MhsW43Ut43uMgmWSrmIy0L1D7-csC5wTPEUYI6XY_cgozGRbPzbJJq5YN_dxNU0Li4K1Wqiloq7Unfjw49mhKpcfoX8vfGGTVOLz75RYECdf_GR2e_YhkY30R4nvFNh_-04P8NAAD__6WA5EA">