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.