Option::unwrap, panic!, print!, maybe others induce string relocations
Consider the following code:
pub fn foo(a: Option<usize>) -> usize {
a.unwrap()
}
This generates the following:
example::foo:
cmp qword ptr [rdi], 1
jne .LBB0_1
mov rax, qword ptr [rdi + 8]
ret
.LBB0_1:
push rbp
mov rbp, rsp
lea rdi, [rip + .Lref.2]
call core::panicking::panic@PLT
ud2
str.0:
.ascii "called `Option::unwrap()` on a `None` value"
str.1:
.ascii "/checkout/src/libcore/option.rs"
.Lref.2:
.quad str.0
.quad 43
.quad str.1
.quad 31
.long 335
.long 21
The quads under .Lref.2 correspond to (pointer, length) pairs for both strings emitted by unwrap(). The way they are stored looks related to the call convention for core::panicking::panic, which looks like it just wants a pointer to a buffer containing 2 &str and 2 integers. The problem with this is that each of those pointers need relocations in the final position independent binary. And in ELF, those are large: each relocation is 2 (on 32-bits) or 3 (on 64-bits) words, so 24 bytes on 64-bits. Per string.
The same kind of thing happens with panic!:
pub fn foo() {
panic!("foo")
}
I'll skip the assembly for the code itself, but the data looks like:
str.1:
.ascii "/tmp/compiler-explorer-compiler118116-63-xzcrje.hzu9d/example.rs"
.Lref.2:
.quad str.1
.quad 64
.long 2
.long 5
str.4:
.ascii "foo"
Do note that there is no reference to str.4, the corresponding (pointer, length) is actually created from code in that case:
lea rcx, [rip + str.4]
mov qword ptr [rax], rcx
mov qword ptr [rax + 8], 3
And the same again for print!:
pub fn foo() {
print!("foo")
}
generating this data:
str.0:
.ascii "foo"
.Lref.1:
.quad str.0
.quad 3
A counter example is Option::expect:
pub fn foo(a: Option<usize>) -> usize {
a.expect("bar")
}
which generates:
example::foo:
cmp qword ptr [rdi], 1
jne .LBB0_1
mov rax, qword ptr [rdi + 8]
ret
.LBB0_1:
push rbp
mov rbp, rsp
lea rdi, [rip + str.0]
mov esi, 3
call core::option::expect_failed@PLT
ud2
str.0:
.ascii "bar"
I have no clue how this can be improved, but surely one of our enlightened people have.
Cc @rust-lang/wg-codegen
Anthony Ramine at 2018-04-02 14:23:58
I am not sure what the proposed improvement is here, either. On recent versions, everything seems much different, see this Godbolt:
.L__unnamed_6: .L__unnamed_1: .ascii "called `Option::unwrap()` on a `None` value" .L__unnamed_7: .ascii "/app/example.rs" .L__unnamed_2: .quad .L__unnamed_7 .asciz "\017\000\000\000\000\000\000\000\002\000\000\000\005\000\000" .L__unnamed_3: .ascii "an expectation" .L__unnamed_4: .quad .L__unnamed_7 .asciz "\017\000\000\000\000\000\000\000\006\000\000\000\005\000\000" .L__unnamed_8: .ascii "a print" .L__unnamed_5: .quad .L__unnamed_8 .asciz "\007\000\000\000\000\000\000"Nonzero number of quads, but less. Everything in the actual code seems to be using
rip-relative addressing withlea, and it loads both things at a time before proceeding.However, the dizzying array of stuff with the full-blown
panic!sections remains quite significant in this one, but that may be because of trying to minimize the impact of thepanic!call itself:example::panics: push rax call std::panicking::begin_panic ud2 .L__unnamed_3: .quad core::ptr::drop_in_place<&str> .asciz "\020\000\000\000\000\000\000\000\b\000\000\000\000\000\000" .quad <std::panicking::begin_panic::PanicPayload<A> as core::panic::BoxMeUp>::take_box .quad <std::panicking::begin_panic::PanicPayload<A> as core::panic::BoxMeUp>::get .L__unnamed_4: .quad core::ptr::drop_in_place<&str> .asciz "\020\000\000\000\000\000\000\000\b\000\000\000\000\000\000" .quad <T as core::any::Any>::type_id .L__unnamed_1: .ascii "a panic" .L__unnamed_5: .ascii "/app/example.rs" .L__unnamed_2: .quad .L__unnamed_5 .asciz "\017\000\000\000\000\000\000\000\002\000\000\000\005\000\000"I hesitate to pronounce things as better or worse now, or to assert the string relocations are necessarily bad or good.
Jubilee at 2022-07-11 03:14:44