Surprising struct copy

I was exploring various constructs and looking at the generated code. I was surprised by this

struct Foo {
    x: i32,
    y: i32,
    arr: [i32; 50],
}
#[inline(never)]
fn print(foo: &Foo) {
    println!("x: {}, y: {}", foo.x, foo.y);
}
fn main() {
    let f1 = Foo {
        x: 42,
        y: 24,
        arr: [0; 50],
    };
    // f1.x = 10;
    print(&f1);
    let f2 = f1;
    // f2.x = 20;
    print(&f2);
}

it generates

        push rsi
        .seh_pushreg rsi
        push rdi
        .seh_pushreg rdi
        sub rsp, 456
        .seh_stackalloc 456
        .seh_endprologue
        xorps xmm0, xmm0
        movaps xmmword ptr [rsp + 208], xmm0
        movaps xmmword ptr [rsp + 192], xmm0
        movaps xmmword ptr [rsp + 176], xmm0
        movaps xmmword ptr [rsp + 160], xmm0
        movaps xmmword ptr [rsp + 144], xmm0
        movaps xmmword ptr [rsp + 128], xmm0
        movaps xmmword ptr [rsp + 112], xmm0
        movaps xmmword ptr [rsp + 96], xmm0
        movaps xmmword ptr [rsp + 80], xmm0
        movaps xmmword ptr [rsp + 64], xmm0
        movaps xmmword ptr [rsp + 48], xmm0
        movaps xmmword ptr [rsp + 32], xmm0
        mov qword ptr [rsp + 224], 0
        movabs rax, 103079215146
        mov qword ptr [rsp + 232], rax
        lea rsi, [rsp + 32]
        mov rcx, rsi
        call test1::print
        lea rdi, [rsp + 248]
        mov r8d, 208
        mov rcx, rdi
        mov rdx, rsi
        call memcpy
        mov rcx, rdi
        call test1::print
        nop
        add rsp, 456
        pop rdi
        pop rsi
        ret

It treats
let f2 = f1;

the same way c does, bit blits the struct to a new copy. I expected to see move semantics, in effect making the assignment a no-op and just modifying the compile time symbol table to make f2 refer to the same stack data (given that f1 is no longer usable in any way)

(This is all based on my understanding as a not compiler dev)
This is because currently once the address of a variable has been observed (for example by passing it to a not inlined function) it won't be reused.
There is currently a proposal for a way to fix this: Public view of rust-lang | Zulip team chat

I don't know if that would fix this specific case, but it's goal is to optimize moves more aggressively so it might.

edit: yeah it explicitly makes an example like yours and says that it would not be moved in this case. so if that gets accepted your code will be optimized better

4 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.