Why can the Rust compiler not optimize away the Err arm of Box::downcast?
我有一个
首先,我尝试使用
1 2 3 4 5 6 7 8 | pub unsafe fn downcast() -> Box<i32> { let value = any(); if let Ok(value) = value.downcast() { value } else { std::hint::unreachable_unchecked() } } |
和
1 2 3 | pub unsafe fn downcast() -> Box<i32> { any().downcast().map_err(|_| std::hint::unreachable_unchecked()).unwrap() } |
与
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | example::downcast: push rbx sub rsp, 16 call any@PLT mov rbx, rax mov qword ptr [rsp], rax mov qword ptr [rsp + 8], rdx mov rdi, rax call qword ptr [rdx + 24] mov rax, rbx add rsp, 16 pop rbx ret mov rbx, rax mov rdi, rsp call core::ptr::drop_in_place mov rdi, rbx call _Unwind_Resume@PLT ud2 |
由于这不是我想要的优化,所以我尝试了
1 2 3 4 5 | pub unsafe fn downcast() -> Box<i32> { let value = any(); std::intrinsics::assume(value.is::<i32>()); value.downcast().unwrap() } |
但这变得更糟(省略了118行):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | example::downcast: push r15 push r14 push rbx sub rsp, 32 call any@PLT mov rbx, rax mov r14, rdx mov qword ptr [rsp], rax mov qword ptr [rsp + 8], rdx mov r15, qword ptr [rdx + 24] mov rdi, rax call r15 mov qword ptr [rsp + 16], rbx mov qword ptr [rsp + 24], r14 mov rdi, rbx call r15 movabs rcx, -5015437470765251660 ;TypeId::of::<i32>() cmp rax, rcx jne .LBB5_7 mov rax, rbx add rsp, 32 pop rbx pop r14 pop r15 ret .LBB5_7: mov rdi, rbx mov rsi, r14 call core::result::unwrap_failed ud2 mov rbx, rax lea rdi, [rsp + 16] call core::ptr::drop_in_place mov rdi, rbx call _Unwind_Resume@PLT ud2 mov rbx, rax mov rdi, rsp call core::ptr::drop_in_place mov rdi, rbx call _Unwind_Resume@PLT ud2 |
我期望生成这样的代码,这是
1 2 3 4 5 | pub unsafe fn downcast() -> Box<i32> { let value = any(); let raw: *mut dyn Any = Box::into_raw(value); Box::from_raw(raw as *mut i32) } |
结果(省略零行):
1 2 3 4 5 | example::downcast: push rax call any@PLT pop rcx ret |
为什么编译器不能以这种方式优化代码?
所有组件均由Godbolt生成。
让我们尝试尽可能地手动优化您的代码。 如果手动内联
1 2 3 4 5 6 7 8 9 | pub unsafe fn downcast() -> Box<i32> { let value = any(); if value.is::<i32>() { let raw: *mut Any = Box::into_raw(value); Box::from_raw(raw as *mut i32) } else { std::hint::unreachable_unchecked() } } |
我们可以将其转换为:
1 2 3 4 5 6 | pub unsafe fn downcast() -> Box<i32> { let value = any(); value.is::<i32>(); let raw: *mut Any = Box::into_raw(value); Box::from_raw(raw as *mut i32) } |
您可以从以下函数中看到与示例相同的冗长的汇编代码:
1 2 3 4 | #![feature(get_type_id)] pub fn nop(any: Box<dyn Any>) { any.get_type_id(); } |
现在您可能会争辩说