Skip to content

Commit 8ed3a80

Browse files
authored
Rollup merge of #103287 - saethlin:faster-len-check, r=thomcc
Use a faster allocation size check in slice::from_raw_parts I've been perusing through the codegen changes that result from turning on the standard library debug assertions. The previous check in here uses saturating arithmetic, which in my experience sometimes makes LLVM just fail to optimize things around the saturating operation. Here is a demo of the codegen difference: https://godbolt.org/z/WMEqrjajW Before: ```asm example::len_check_old: mov rax, rdi mov ecx, 3 mul rcx setno cl test rax, rax setns al and al, cl ret example::len_check_old: mov rax, rdi mov ecx, 8 mul rcx setno cl test rax, rax setns al and al, cl ret ``` After: ```asm example::len_check_new: movabs rax, 3074457345618258603 cmp rdi, rax setb al ret example::len_check_new: shr rdi, 60 sete al ret ``` Running rustc-perf locally, this looks like up to a 4.5% improvement when `debug-assertions-std = true`. Thanks ```@LegionMammal978``` (I think that's you?) for turning my idea into a much cleaner implementation. r? ```@thomcc```
2 parents f2c2e58 + cfcb0a2 commit 8ed3a80

File tree

3 files changed

+16
-5
lines changed

3 files changed

+16
-5
lines changed

library/core/src/intrinsics.rs

+10
Original file line numberDiff line numberDiff line change
@@ -2229,6 +2229,16 @@ pub(crate) fn is_aligned_and_not_null<T>(ptr: *const T) -> bool {
22292229
!ptr.is_null() && ptr.is_aligned()
22302230
}
22312231

2232+
/// Checks whether an allocation of `len` instances of `T` exceeds
2233+
/// the maximum allowed allocation size.
2234+
pub(crate) fn is_valid_allocation_size<T>(len: usize) -> bool {
2235+
let max_len = const {
2236+
let size = crate::mem::size_of::<T>();
2237+
if size == 0 { usize::MAX } else { isize::MAX as usize / size }
2238+
};
2239+
len <= max_len
2240+
}
2241+
22322242
/// Checks whether the regions of memory starting at `src` and `dst` of size
22332243
/// `count * size_of::<T>()` do *not* overlap.
22342244
pub(crate) fn is_nonoverlapping<T>(src: *const T, dst: *const T, count: usize) -> bool {

library/core/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@
192192
#![feature(extern_types)]
193193
#![feature(fundamental)]
194194
#![feature(if_let_guard)]
195+
#![feature(inline_const)]
195196
#![feature(intra_doc_pointers)]
196197
#![feature(intrinsics)]
197198
#![feature(lang_items)]

library/core/src/slice/raw.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
//! Free functions to create `&[T]` and `&mut [T]`.
22
33
use crate::array;
4-
use crate::intrinsics::{assert_unsafe_precondition, is_aligned_and_not_null};
4+
use crate::intrinsics::{
5+
assert_unsafe_precondition, is_aligned_and_not_null, is_valid_allocation_size,
6+
};
57
use crate::ops::Range;
68
use crate::ptr;
79

@@ -91,8 +93,7 @@ pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T]
9193
// SAFETY: the caller must uphold the safety contract for `from_raw_parts`.
9294
unsafe {
9395
assert_unsafe_precondition!([T](data: *const T, len: usize) =>
94-
is_aligned_and_not_null(data)
95-
&& crate::mem::size_of::<T>().saturating_mul(len) <= isize::MAX as usize
96+
is_aligned_and_not_null(data) && is_valid_allocation_size::<T>(len)
9697
);
9798
&*ptr::slice_from_raw_parts(data, len)
9899
}
@@ -135,8 +136,7 @@ pub const unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a m
135136
// SAFETY: the caller must uphold the safety contract for `from_raw_parts_mut`.
136137
unsafe {
137138
assert_unsafe_precondition!([T](data: *mut T, len: usize) =>
138-
is_aligned_and_not_null(data)
139-
&& crate::mem::size_of::<T>().saturating_mul(len) <= isize::MAX as usize
139+
is_aligned_and_not_null(data) && is_valid_allocation_size::<T>(len)
140140
);
141141
&mut *ptr::slice_from_raw_parts_mut(data, len)
142142
}

0 commit comments

Comments
 (0)