Home > Mobile >  What does bound checking look like in Assembly?
What does bound checking look like in Assembly?

Time:10-11

I am trying to better understand when Rust performs bound checking and how one would know for certain whether the compiler is able to optimize certain parts away.

pub fn get_unchecked_mut(a: i32) -> Vec<i32>{
    let buffers = &mut vec![1, 2, 3];
    let mut i = a as usize;;

    while i < buffers.len() {
        unsafe {
            let buf = buffers.get_unchecked_mut(i);
            *buf  = a;
        };
        i  = 1;
    } 

    return buffers.to_vec();
}

#[inline(never)]
pub fn normal_slice_index(a: i32) -> Vec<i32> {
    let buffers = &mut vec![1, 2, 3];
    let mut i = a as usize;;
    while i < buffers.len() {
        buffers[i]  = a;
        i  = 1;
    }

    return buffers.to_vec();
}

pub fn iter_mut(a: i32) ->Vec<i32> {
    let buffers = &mut vec![1, 2, 3];
    let i = a as usize;

    for buf in buffers[i..].iter_mut() {
        *buf  = a;
    }

    return buffers.to_vec();
}

Which results in the following Assembly:

core::ptr::drop_in_place<alloc::vec::Vec<i32>>:
        mov     rax, qword ptr [rdi   8]
        test    rax, rax
        je      .LBB0_2
        mov     ecx, 4
        mul     rcx
        test    rax, rax
        je      .LBB0_2
        mov     rdi, qword ptr [rdi]
        mov     edx, 4
        mov     rsi, rax
        jmp     qword ptr [rip   __rust_dealloc@GOTPCREL]
.LBB0_2:
        ret

.LCPI1_0:
        .quad   3
        .quad   3
example::get_unchecked_mut:
        push    rbp
        push    r14
        push    rbx
        sub     rsp, 32
        mov     ebp, esi
        mov     r14, rdi
        mov     edi, 12
        mov     esi, 4
        call    qword ptr [rip   __rust_alloc@GOTPCREL]
        test    rax, rax
        je      .LBB1_12
        mov     rbx, rax
        movabs  rax, 8589934593
        mov     qword ptr [rbx], rax
        mov     dword ptr [rbx   8], 3
        mov     qword ptr [rsp   8], rbx
        movaps  xmm0, xmmword ptr [rip   .LCPI1_0]
        movups  xmmword ptr [rsp   16], xmm0
        cmp     ebp, 2
        ja      .LBB1_5
        mov     eax, ebp
        add     dword ptr [rbx   4*rax], ebp
        cmp     ebp, 2
        jae     .LBB1_5
        add     dword ptr [rbx   4*rax   4], ebp
        test    ebp, ebp
        jne     .LBB1_5
        add     dword ptr [rbx   4*rax   8], ebp
.LBB1_5:
        mov     edi, 12
        mov     esi, 4
        call    qword ptr [rip   __rust_alloc@GOTPCREL]
        test    rax, rax
        je      .LBB1_6
        mov     qword ptr [r14], rax
        mov     ecx, dword ptr [rbx   8]
        mov     dword ptr [rax   8], ecx
        mov     rcx, qword ptr [rbx]
        mov     qword ptr [rax], rcx
        movaps  xmm0, xmmword ptr [rip   .LCPI1_0]
        movups  xmmword ptr [r14   8], xmm0
        mov     esi, 12
        mov     edx, 4
        mov     rdi, rbx
        call    qword ptr [rip   __rust_dealloc@GOTPCREL]
        mov     rax, r14
        add     rsp, 32
        pop     rbx
        pop     r14
        pop     rbp
        ret
.LBB1_12:
        mov     edi, 12
        mov     esi, 4
        call    qword ptr [rip   alloc::alloc::handle_alloc_error@GOTPCREL]
        ud2
.LBB1_6:
        mov     edi, 12
        mov     esi, 4
        call    qword ptr [rip   alloc::alloc::handle_alloc_error@GOTPCREL]
        ud2
        mov     rbx, rax
        lea     rdi, [rsp   8]
        call    core::ptr::drop_in_place<alloc::vec::Vec<i32>>
        mov     rdi, rbx
        call    _Unwind_Resume@PLT
        ud2
        call    qword ptr [rip   core::panicking::panic_no_unwind@GOTPCREL]
        ud2

.LCPI2_0:
        .quad   3
        .quad   3
example::normal_slice_index:
        push    rbp
        push    r14
        push    rbx
        sub     rsp, 32
        mov     ebp, esi
        mov     r14, rdi
        mov     edi, 12
        mov     esi, 4
        call    qword ptr [rip   __rust_alloc@GOTPCREL]
        test    rax, rax
        je      .LBB2_12
        mov     rbx, rax
        movabs  rax, 8589934593
        mov     qword ptr [rbx], rax
        mov     dword ptr [rbx   8], 3
        mov     qword ptr [rsp   8], rbx
        movaps  xmm0, xmmword ptr [rip   .LCPI2_0]
        movups  xmmword ptr [rsp   16], xmm0
        cmp     ebp, 2
        ja      .LBB2_5
        mov     eax, ebp
        add     dword ptr [rbx   4*rax], ebp
        cmp     ebp, 2
        jae     .LBB2_5
        add     dword ptr [rbx   4*rax   4], ebp
        test    ebp, ebp
        jne     .LBB2_5
        add     dword ptr [rbx   4*rax   8], ebp
.LBB2_5:
        mov     edi, 12
        mov     esi, 4
        call    qword ptr [rip   __rust_alloc@GOTPCREL]
        test    rax, rax
        je      .LBB2_6
        mov     qword ptr [r14], rax
        mov     ecx, dword ptr [rbx   8]
        mov     dword ptr [rax   8], ecx
        mov     rcx, qword ptr [rbx]
        mov     qword ptr [rax], rcx
        movaps  xmm0, xmmword ptr [rip   .LCPI2_0]
        movups  xmmword ptr [r14   8], xmm0
        mov     esi, 12
        mov     edx, 4
        mov     rdi, rbx
        call    qword ptr [rip   __rust_dealloc@GOTPCREL]
        mov     rax, r14
        add     rsp, 32
        pop     rbx
        pop     r14
        pop     rbp
        ret
.LBB2_12:
        mov     edi, 12
        mov     esi, 4
        call    qword ptr [rip   alloc::alloc::handle_alloc_error@GOTPCREL]
        ud2
.LBB2_6:
        mov     edi, 12
        mov     esi, 4
        call    qword ptr [rip   alloc::alloc::handle_alloc_error@GOTPCREL]
        ud2
        mov     rbx, rax
        lea     rdi, [rsp   8]
        call    core::ptr::drop_in_place<alloc::vec::Vec<i32>>
        mov     rdi, rbx
        call    _Unwind_Resume@PLT
        ud2
        call    qword ptr [rip   core::panicking::panic_no_unwind@GOTPCREL]
        ud2

.LCPI3_0:
        .quad   3
        .quad   3
example::iter_mut:
        push    rbp
        push    r14
        push    rbx
        sub     rsp, 32
        mov     ebp, esi
        mov     r14, rdi
        mov     edi, 12
        mov     esi, 4
        call    qword ptr [rip   __rust_alloc@GOTPCREL]
        test    rax, rax
        je      .LBB3_22
        mov     rbx, rax
        movabs  rax, 8589934593
        mov     qword ptr [rbx], rax
        mov     dword ptr [rbx   8], 3
        mov     qword ptr [rsp   8], rbx
        movdqa  xmm0, xmmword ptr [rip   .LCPI3_0]
        movdqu  xmmword ptr [rsp   16], xmm0
        movsxd  rdi, ebp
        cmp     edi, 4
        jae     .LBB3_2
        cmp     ebp, 3
        je      .LBB3_16
        lea     rax, [rbx   4*rdi]
        lea     rdx, [4*rdi]
        mov     ecx, 8
        sub     rcx, rdx
        cmp     rcx, 28
        jb      .LBB3_14
        shr     rcx, 2
        add     rcx, 1
        mov     r8, rcx
        and     r8, -8
        movd    xmm0, ebp
        pshufd  xmm0, xmm0, 0
        lea     rdx, [r8 - 8]
        mov     r9, rdx
        shr     r9, 3
        add     r9, 1
        test    rdx, rdx
        je      .LBB3_7
        mov     rsi, r9
        and     rsi, -2
        lea     rdx, [rbx   4*rdi]
        add     rdx, 48
        xor     edi, edi
.LBB3_9:
        movdqu  xmm1, xmmword ptr [rdx   4*rdi - 48]
        movdqu  xmm2, xmmword ptr [rdx   4*rdi - 32]
        movdqu  xmm3, xmmword ptr [rdx   4*rdi - 16]
        movdqu  xmm4, xmmword ptr [rdx   4*rdi]
        paddd   xmm1, xmm0
        paddd   xmm2, xmm0
        movdqu  xmmword ptr [rdx   4*rdi - 48], xmm1
        movdqu  xmmword ptr [rdx   4*rdi - 32], xmm2
        paddd   xmm3, xmm0
        paddd   xmm4, xmm0
        movdqu  xmmword ptr [rdx   4*rdi - 16], xmm3
        movdqu  xmmword ptr [rdx   4*rdi], xmm4
        add     rdi, 16
        add     rsi, -2
        jne     .LBB3_9
        test    r9b, 1
        je      .LBB3_12
.LBB3_11:
        movdqu  xmm1, xmmword ptr [rax   4*rdi]
        movdqu  xmm2, xmmword ptr [rax   4*rdi   16]
        paddd   xmm1, xmm0
        paddd   xmm2, xmm0
        movdqu  xmmword ptr [rax   4*rdi], xmm1
        movdqu  xmmword ptr [rax   4*rdi   16], xmm2
.LBB3_12:
        cmp     rcx, r8
        je      .LBB3_16
        lea     rax, [rax   4*r8]
.LBB3_14:
        mov     rcx, rbx
        add     rcx, 12
.LBB3_15:
        add     dword ptr [rax], ebp
        add     rax, 4
        cmp     rax, rcx
        jne     .LBB3_15
.LBB3_16:
        mov     edi, 12
        mov     esi, 4
        call    qword ptr [rip   __rust_alloc@GOTPCREL]
        test    rax, rax
        je      .LBB3_17
        mov     qword ptr [r14], rax
        mov     ecx, dword ptr [rbx   8]
        mov     dword ptr [rax   8], ecx
        mov     rcx, qword ptr [rbx]
        mov     qword ptr [rax], rcx
        movaps  xmm0, xmmword ptr [rip   .LCPI3_0]
        movups  xmmword ptr [r14   8], xmm0
        mov     esi, 12
        mov     edx, 4
        mov     rdi, rbx
        call    qword ptr [rip   __rust_dealloc@GOTPCREL]
        mov     rax, r14
        add     rsp, 32
        pop     rbx
        pop     r14
        pop     rbp
        ret
.LBB3_7:
        xor     edi, edi
        test    r9b, 1
        jne     .LBB3_11
        jmp     .LBB3_12
.LBB3_22:
        mov     edi, 12
        mov     esi, 4
        call    qword ptr [rip   alloc::alloc::handle_alloc_error@GOTPCREL]
        ud2
.LBB3_2:
        lea     rdx, [rip   .L__unnamed_1]
        mov     esi, 3
        call    qword ptr [rip   core::slice::index::slice_start_index_len_fail@GOTPCREL]
        jmp     .LBB3_3
.LBB3_17:
        mov     edi, 12
        mov     esi, 4
        call    qword ptr [rip   alloc::alloc::handle_alloc_error@GOTPCREL]
.LBB3_3:
        ud2
        mov     rbx, rax
        lea     rdi, [rsp   8]
        call    core::ptr::drop_in_place<alloc::vec::Vec<i32>>
        mov     rdi, rbx
        call    _Unwind_Resume@PLT
        ud2
        call    qword ptr [rip   core::panicking::panic_no_unwind@GOTPCREL]
        ud2

.L__unnamed_2:
        .ascii  "/app/example.rs"

.L__unnamed_1:
        .quad   .L__unnamed_2
        .asciz  "\017\000\000\000\000\000\000\000!\000\000\000\020\000\000"

DW.ref.rust_eh_personality:
        .quad   rust_eh_personality

For these 3 scenarios, when does Rust perform bound checking and what does this look like in Assembly?

The get_unchecked_mut() is guaranteed to not do any bounds checking, but does it occur for the latter two? I would presume it does, but I can not find it anywhere in the ASM.

CodePudding user response:

In normal_slice_index the compiler simply unrolled the loop and did the elements separately. The bounds checking is on lines 112-113:

    cmp     ebp, 2
    ja      .LBB2_5

Since you explicitly have while i < buffers.len() there will be no error.

In iter_mut the bounds checking is on lines 187-188:

    cmp     edi, 4
    jae     .LBB3_2

At .LBB3_3 we have:

    mov     esi, 3
    call    qword ptr [rip   core::slice::index::slice_start_index_len_fail@GOTPCREL]

Which will raise the error.

Note that since you have the vec visible to the compiler, it knows the length is 3 and that got inlined. Code might look different if you pass in the vector as an argument.

  • Related