代码之家  ›  专栏  ›  技术社区  ›  Tim Diekmann suresh madaparthi

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

  •  10
  • Tim Diekmann suresh madaparthi  · 技术社区  · 6 年前

    我有一个 Box<dyn Any> 我知道底层的类型,所以我想在 Box::downcast() ( source ).

    首先我试着 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
    

    我希望生成这样的代码,这是 Ok 手臂从 Box::downcast

    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
    

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

    所有组件 generated by godbolt

    1 回复  |  直到 5 年前
        1
  •  2
  •   orlp    6 年前

    让我们尽量手动优化您的代码。如果我们手动内联 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 方法调用 get_type_id dyn Any 对象。该方法只能在运行时确定。以及 可能有副作用 . 因此它可以

    您可以通过以下函数看到与示例中相同的冗长汇编代码:

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

    Any::get_type_id 是由编译器普遍定义的,不能被重写,但是编译器没有足够的智能来实现这一点。