Assume slice len is bounded by allocation size
Uses assume to check the length against a constant upper bound. The inlined result then informs the optimizer of the sound value range. This was tried with unreachable_unchecked before which introduces a branch. This has the advantage of not being executed in sound code but complicates basic blocks. It resulted in ~2% increased compile time in some worst cases. Add a codegen test for the assumption, testing the issue from #67186
This commit is contained in:
parent
d92d28e523
commit
e44784b875
|
@ -80,8 +80,9 @@
|
||||||
#![feature(constctlz)]
|
#![feature(constctlz)]
|
||||||
#![feature(const_panic)]
|
#![feature(const_panic)]
|
||||||
#![feature(const_pin)]
|
#![feature(const_pin)]
|
||||||
#![feature(const_fn_union)]
|
|
||||||
#![feature(const_fn)]
|
#![feature(const_fn)]
|
||||||
|
#![feature(const_fn_union)]
|
||||||
|
#![feature(const_assume)]
|
||||||
#![cfg_attr(not(bootstrap), feature(const_fn_floating_point_arithmetic))]
|
#![cfg_attr(not(bootstrap), feature(const_fn_floating_point_arithmetic))]
|
||||||
#![cfg_attr(not(bootstrap), feature(const_fn_fn_ptr_basics))]
|
#![cfg_attr(not(bootstrap), feature(const_fn_fn_ptr_basics))]
|
||||||
#![feature(const_generics)]
|
#![feature(const_generics)]
|
||||||
|
|
|
@ -78,6 +78,17 @@ pub use index::check_range;
|
||||||
#[lang = "slice"]
|
#[lang = "slice"]
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
impl<T> [T] {
|
impl<T> [T] {
|
||||||
|
#[cfg(not(bootstrap))] // Unused in bootstrap
|
||||||
|
/// The maximum, inclusive, length such that the slice is no larger than `isize::MAX` bytes.
|
||||||
|
/// This constant is used in `len` below.
|
||||||
|
const MAX_LEN_BOUND: usize = {
|
||||||
|
if mem::size_of::<T>() == 0 {
|
||||||
|
usize::MAX
|
||||||
|
} else {
|
||||||
|
isize::MAX as usize / mem::size_of::<T>()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/// Returns the number of elements in the slice.
|
/// Returns the number of elements in the slice.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
|
@ -90,11 +101,23 @@ impl<T> [T] {
|
||||||
#[rustc_const_stable(feature = "const_slice_len", since = "1.32.0")]
|
#[rustc_const_stable(feature = "const_slice_len", since = "1.32.0")]
|
||||||
#[inline]
|
#[inline]
|
||||||
// SAFETY: const sound because we transmute out the length field as a usize (which it must be)
|
// SAFETY: const sound because we transmute out the length field as a usize (which it must be)
|
||||||
#[allow_internal_unstable(const_fn_union)]
|
#[allow_internal_unstable(const_fn_union, const_assume)]
|
||||||
pub const fn len(&self) -> usize {
|
pub const fn len(&self) -> usize {
|
||||||
// SAFETY: this is safe because `&[T]` and `FatPtr<T>` have the same layout.
|
// SAFETY: this is safe because `&[T]` and `FatPtr<T>` have the same layout.
|
||||||
// Only `std` can make this guarantee.
|
// Only `std` can make this guarantee.
|
||||||
unsafe { crate::ptr::Repr { rust: self }.raw.len }
|
let raw_len = unsafe { crate::ptr::Repr { rust: self }.raw.len };
|
||||||
|
|
||||||
|
#[cfg(not(bootstrap))] // FIXME: executing assume in const eval not supported in bootstrap
|
||||||
|
// SAFETY: this assume asserts that `raw_len * size_of::<T>() <= isize::MAX`. All
|
||||||
|
// references must point to one allocation with size at most isize::MAX. Note that we the
|
||||||
|
// multiplication could appear to overflow until we have assumed the bound. This overflow
|
||||||
|
// would make additional values appear 'valid' and then `n > 1` the range of permissible
|
||||||
|
// length would no longer be the full or even a single range.
|
||||||
|
unsafe {
|
||||||
|
crate::intrinsics::assume(raw_len <= Self::MAX_LEN_BOUND)
|
||||||
|
};
|
||||||
|
|
||||||
|
raw_len
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if the slice has a length of 0.
|
/// Returns `true` if the slice has a length of 0.
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
// min-llvm-version: 11.0
|
||||||
|
// compile-flags: -O -C panic=abort
|
||||||
|
#![crate_type = "lib"]
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn len_range(a: &[u8], b: &[u8]) -> usize {
|
||||||
|
// CHECK-NOT: panic
|
||||||
|
a.len().checked_add(b.len()).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn len_range_on_non_byte(a: &[u16], b: &[u16]) -> usize {
|
||||||
|
// CHECK-NOT: panic
|
||||||
|
a.len().checked_add(b.len()).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Zst;
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn zst_range(a: &[Zst], b: &[Zst]) -> usize {
|
||||||
|
// Zsts may be arbitrarily large.
|
||||||
|
// CHECK: panic
|
||||||
|
a.len().checked_add(b.len()).unwrap()
|
||||||
|
}
|
Loading…
Reference in New Issue