为什么 Rust 编译器不能优化 Box::d owncast 的 Err 分支?



我有一个Box<dyn Any>,我知道底层类型,所以我想优化Box::downcast()(来源(中的测试。

首先我尝试了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()
}
}

pub unsafe fn downcast() -> Box<i32> {
any().downcast().map_err(|_| std::hint::unreachable_unchecked()).unwrap()
}

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

由于这不是我想要的优化,我尝试了

pub unsafe fn downcast() -> Box<i32> {
let value = any();
std::intrinsics::assume(value.is::<i32>());
value.downcast().unwrap()
}

但这变得更糟(省略了 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

我希望生成这样的代码,这是Box::downcastOk臂:

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)
}

这会导致这种情况(省略行(:

example::downcast:
push    rax
call    any@PLT
pop     rcx
ret

为什么编译器不能以这种方式优化代码?

所有组件由 godbolt 生成。

让我们尝试尽可能手动优化您的代码。如果我们手动内联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()
}
}

我们可以转换它:

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方法调用dyn Any对象上的get_type_id。该方法只能在运行时确定。它可能有副作用。因此,它无法删除。

仅从以下函数中,您就可以看到与示例中相同的冗长汇编代码:

#![feature(get_type_id)]
pub fn nop(any: Box<dyn Any>) {
any.get_type_id();
}

现在你可能会争辩说,Any::get_type_id是由编译器普遍定义的,不能被覆盖,但编译器不够聪明,无法意识到这一点。

相关内容

  • 没有找到相关文章

最新更新