Issue 141038
Summary Cross language lto fails in presence of C++ destructor
Labels new issue
Assignees
Reporter HKalbasi
    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");
}
```
_______________________________________________
llvm-bugs mailing list
llvm-bugs@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs

Reply via email to