rust-lang/rust-memory-model

optimizing around unchecked-get

Opened this issue · 2 comments

The unchecked-get use case consists of safe code that contains one or two small unsafe calls which check application-level constraints -- for example, eliding a bounds check, or asserting that a string is utf-8:

fn foo() {
    let vec: Vec<i32> = vec![...];
    ...
    unsafe { vec.get_unchecked(i) }
    ...
}

Ideally, code like this would be optimized just as well as the original safe code (and of course would not have a bounds check). However, the introduction of an unsafe block and a call to an unsafe function can be a barrier to optimization, such as in the Tootsie Pop model (#21).

The traditional example is:

fn example_optimizable(b: &mut Context, bigs: &[Big]) {
    let index: usize = calculate_index(b);
    let tmp = unsafe { <[_]>::unchecked_get(bigs, index) };
    b.big = tmp;
}

Which is α-equivalent to

fn get_pointer_to_big(b: &Context) -> usize {
    &b.big as *const _ as usize
}

unsafe fn mutate_through_pointer(bigs: &[Big], index: usize) -> Big {
    Big {
        x: 0,
        y: (index as *const Big).x,
        ...
    }
}

fn example_nonoptimizable(b: &mut Context, bigs: &[Big]) {
    let index: usize = get_pointer_to_big(b);
    let tmp = unsafe { mutate_through_pointer(bigs, index) };
    b.big = tmp;
}

Disregarding panics, we want to do copy elimination in the first case, but not the second, but that is not really possible.

This is a problem in Tootsie Pop style models, but not in most others (such as Stacked Borrows) where unsafe blocks do not have any operational effect.