Description
The internals of llvm do not permit creation of slices spanning more
than isize::MAX
bytes, as a limit resulting from pointer offsetting
inbounds. It must be both possible to represent arbitrary pointer
differences of the same allocation within an isize
and there must be
no wrapping in the ptr::offset
and array indexing. This has influenced
the surface documentation for slice::from_raw_parts
:
The total size of the slice must be no larger than isize::MAX bytes in
memory. See the safety documentation of pointer::offset.
However, the implementation in the core
library can not leverage this
property for optimization. To illustrate this, the following code should never
require an overflow check and never panic but the compiler is unable to
remove the panic path.
pub fn len_add(a: &[u8], b: &[u8]) -> usize {
a.len().checked_add(b.len()).unwrap()
}
The core
library could add an optimization hint in its implementation
of slice::len
asserting that the value range is in fact at most as large as
the maximum possible values of isize
. This information should then be
propagated automatically at each call site in optimizer passes.
// This is libcore, referring to the stabilized `unreachable_unchecked`.
use crate::intrinsics::unreachable;
// Alternative of `[T]::len` in libcore
pub const fn len(&self) -> usize {
let raw_len = unsafe {
crate::ptr::Repr { rust: self }.raw.len
};
if mem::size_of::<T>() > 0 {
if raw_len > isize::max_value() as usize / mem::size_of::<T>() {
// SAFETY: allocations can not be larger than `isize::MAX`
// bytes. Since slices refer to a single allocation, this extends
// to slice lengths of sized types. At this point we would have
// however:
//
// > `len() * mem::size_of::<T>() > isize::MAX`.
//
// Since this path can be deduced as unreachable due to the
// below hint, the optimizer can restrict the value range of
// return values for the purpose of optimization at each
// call site.
unsafe { unreachable() }
}
}
raw_len
}
This currently allows the optimizer to remove the bounds check, as can be seen in this implementation: https://play.rust-lang.org/?version=nightly&mode=release&edition=2018&gist=6e46ce72b9affea080a33eee72b53267
I don't see a way to enable this on current nightly as slice::len
is a const fn
and several parts of the above implementation will consequently not yet work.