Почему компилятор Rust не может оптимизировать руку Err Box :: downcast? - PullRequest
0 голосов
/ 14 сентября 2018

У меня есть 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

Почему компилятор не может оптимизировать код таким способом?

Вся сборка , сгенерированная Godbolt .

1 Ответ

0 голосов
/ 18 января 2019

Давайте попробуем оптимизировать ваш код настолько хорошо, насколько это возможно, вручную. Если мы вручную включим 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 универсально определяется компилятором и не может быть переопределен, но компилятор не достаточно умен, чтобы понять это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...