Why one more assembler instruction for default string

Viewed 95

I found interesting behavior.

A call to default on a String has one more instruction than a call to to_string on an empty &str. Why?

pub fn string_default() -> String {
    Default::default()
}

pub fn string_to_string() -> String {
    "".to_string()
}
example::string_default:
        mov     rax, rdi
        mov     rcx, qword ptr [rip + .L__unnamed_1]
        xorps   xmm0, xmm0
        movups  xmmword ptr [rdi + 8], xmm0
        mov     qword ptr [rdi], rcx
        ret

example::string_to_string:
        mov     rax, rdi
        xorps   xmm0, xmm0
        movups  xmmword ptr [rdi + 8], xmm0
        mov     qword ptr [rdi], 1
        ret

.L__unnamed_1:
        .asciz  "\001\000\000\000\000\000\000\000\000\000\000\000\000\000\000"

UPD: build profile 'release'

UPD (2): before rust 1.52.0 the assembler code is the same for both functions (4 instructions). In 1.51.0 and earlier mov qword comes before xorps.

1 Answers

&str.to_string() builds the Strings backing Vec through a call on &[u8]::to_owned() which in turn calls &[u8]::to_vec() and ends up calling RawVec::with_capacity_in.

String::new() and String::default() create the Vec through Vec::new() which internally wraps RawVec::SELF, a const value.

You can reduce the example to

fn slice_to_vec() -> Vec<u8> {
    let b: &[u8] = &[];
    let v = b.to_vec();
}
// example::slice_to_vec:
//        mov     rax, rdi
//        mov     qword ptr [rdi], 1
//        xorps   xmm0, xmm0
//        movups  xmmword ptr [rdi + 8], xmm0
//        ret

vs

pub fn vec() -> Vec<u8> {
    Vec::new()
}
// example::vec:
//        mov     rax, rdi
//        mov     rcx, qword ptr [rip + .L__unnamed_1]
//        mov     qword ptr [rdi], rcx
//        xorps   xmm0, xmm0
//        movups  xmmword ptr [rdi + 8], xmm0
//        ret
//
// .L__unnamed_1:
//         .asciz  "\001\000\000\000\000\000\000\000\000\000\000\000\000\000\000"

I can't explain why this happens, but 1.52 happened to upgrade the LLVM version used by Rust, so perhaps that changed the generated assembly.

Related