[英]Why can the Rust compiler not optimize away the Err arm of Box::downcast?
I have a Box<dyn Any>
and I know the underlying type so I want to optimize away the test in Box::downcast()
( source ). 我有一个
Box<dyn Any>
,我知道底层类型,所以我想在Box::downcast()
( 源代码 )中优化测试。
First I tried with std::hint::unreachable_unchecked()
: 首先我尝试使用
std::hint::unreachable_unchecked()
:
pub unsafe fn downcast() -> Box<i32> {
let value = any();
if let Ok(value) = value.downcast() {
value
} else {
std::hint::unreachable_unchecked()
}
}
and 和
pub unsafe fn downcast() -> Box<i32> {
any().downcast().map_err(|_| std::hint::unreachable_unchecked()).unwrap()
}
with rustc -C opt-level=3
both result in this (40 lines omitted): 使用
rustc -C opt-level=3
都会导致这种情况(省略40行):
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
Since this is not the optimization I was looking for, I tried 由于这不是我想要的优化,我试过了
pub unsafe fn downcast() -> Box<i32> {
let value = any();
std::intrinsics::assume(value.is::<i32>());
value.downcast().unwrap()
}
but this got even worse (118 lines omitted): 但这变得更糟(省略了118行):
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
I expected to generate code like this, which is the Ok
arm from Box::downcast
: 我希望生成这样的代码,这是来自
Box::downcast
的Ok
arm:
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)
}
which results in this ( zero lines omitted): 这导致了这一点(省略了零行):
example::downcast:
push rax
call any@PLT
pop rcx
ret
Why can the compiler not optimize the code in such a way? 为什么编译器不能以这种方式优化代码?
All assembly generated by godbolt . 由godbolt生成的所有组件。
Let's try to optimize your code as good as we can manually. 让我们尽可能地手动优化您的代码。 If we manually inline
downcast()
we get the following: 如果我们手动内联
downcast()
我们得到以下内容:
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()
}
}
We can transform this: 我们可以改变这个:
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)
}
value.is::<i32>()
is unused! value.is::<i32>()
未使用! Can we remove it? 我们可以删除吗? Here's lies the issue.
这就是问题所在。
The is
method calls get_type_id
on a dyn Any
object. 的
is
方法调用get_type_id
上的dyn Any
对象。 That method can only be determined at runtime. 该方法只能在运行时确定。 And it may have side effects .
它可能有副作用 。 Thus it can not be removed.
因此无法删除。
You can see the same lengthy assembly code as in your examples just from the following function: 您可以通过以下函数看到与示例中相同的冗长汇编代码:
#![feature(get_type_id)]
pub fn nop(any: Box<dyn Any>) {
any.get_type_id();
}
Now you may argue that Any::get_type_id
is universally defined by the compiler and can not be overridden, but the compiler isn't smart enough to realize that. 现在你可能会争辩说
Any::get_type_id
是由编译器普遍定义的,不能被覆盖,但编译器不够聪明,无法实现。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.