简体   繁体   English

为什么Rust编译器不能优化Box :: downcast的Err arm?

[英]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::downcastOk 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM