Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit ae7f43e

Browse files
committedJun 16, 2024
Auto merge of rust-lang#126299 - scottmcm:tune-sliceindex-ubchecks, r=saethlin
Remove superfluous UbChecks from `SliceIndex` methods The current implementation calls the unsafe ones from the safe ones, but that means they end up emitting UbChecks that are impossible to hit, since we just checked those things. This PR adds some new module-local helpers for the code shared between them, so the safe methods can be small enough to inline by avoiding those extra checks, while the unsafe methods still help catch length mistakes. r? `@saethlin`
2 parents 4cc1c37 + 339f266 commit ae7f43e

File tree

1 file changed

+86
-32
lines changed

1 file changed

+86
-32
lines changed
 

‎core/src/slice/index.rs

+86-32
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
33
use crate::intrinsics::const_eval_select;
44
use crate::ops;
5-
use crate::ptr;
65
use crate::ub_checks::assert_unsafe_precondition;
76

87
#[stable(feature = "rust1", since = "1.0.0")]
@@ -106,6 +105,47 @@ const fn slice_end_index_overflow_fail() -> ! {
106105
panic!("attempted to index slice up to maximum usize");
107106
}
108107

108+
// The UbChecks are great for catching bugs in the unsafe methods, but including
109+
// them in safe indexing is unnecessary and hurts inlining and debug runtime perf.
110+
// Both the safe and unsafe public methods share these helpers,
111+
// which use intrinsics directly to get *no* extra checks.
112+
113+
#[inline(always)]
114+
const unsafe fn get_noubcheck<T>(ptr: *const [T], index: usize) -> *const T {
115+
let ptr = ptr as *const T;
116+
// SAFETY: The caller already checked these preconditions
117+
unsafe { crate::intrinsics::offset(ptr, index) }
118+
}
119+
120+
#[inline(always)]
121+
const unsafe fn get_mut_noubcheck<T>(ptr: *mut [T], index: usize) -> *mut T {
122+
let ptr = ptr as *mut T;
123+
// SAFETY: The caller already checked these preconditions
124+
unsafe { crate::intrinsics::offset(ptr, index) }
125+
}
126+
127+
#[inline(always)]
128+
const unsafe fn get_offset_len_noubcheck<T>(
129+
ptr: *const [T],
130+
offset: usize,
131+
len: usize,
132+
) -> *const [T] {
133+
// SAFETY: The caller already checked these preconditions
134+
let ptr = unsafe { get_noubcheck(ptr, offset) };
135+
crate::intrinsics::aggregate_raw_ptr(ptr, len)
136+
}
137+
138+
#[inline(always)]
139+
const unsafe fn get_offset_len_mut_noubcheck<T>(
140+
ptr: *mut [T],
141+
offset: usize,
142+
len: usize,
143+
) -> *mut [T] {
144+
// SAFETY: The caller already checked these preconditions
145+
let ptr = unsafe { get_mut_noubcheck(ptr, offset) };
146+
crate::intrinsics::aggregate_raw_ptr(ptr, len)
147+
}
148+
109149
mod private_slice_index {
110150
use super::ops;
111151
#[stable(feature = "slice_get_slice", since = "1.28.0")]
@@ -203,13 +243,17 @@ unsafe impl<T> SliceIndex<[T]> for usize {
203243
#[inline]
204244
fn get(self, slice: &[T]) -> Option<&T> {
205245
// SAFETY: `self` is checked to be in bounds.
206-
if self < slice.len() { unsafe { Some(&*self.get_unchecked(slice)) } } else { None }
246+
if self < slice.len() { unsafe { Some(&*get_noubcheck(slice, self)) } } else { None }
207247
}
208248

209249
#[inline]
210250
fn get_mut(self, slice: &mut [T]) -> Option<&mut T> {
211-
// SAFETY: `self` is checked to be in bounds.
212-
if self < slice.len() { unsafe { Some(&mut *self.get_unchecked_mut(slice)) } } else { None }
251+
if self < slice.len() {
252+
// SAFETY: `self` is checked to be in bounds.
253+
unsafe { Some(&mut *get_mut_noubcheck(slice, self)) }
254+
} else {
255+
None
256+
}
213257
}
214258

215259
#[inline]
@@ -227,7 +271,7 @@ unsafe impl<T> SliceIndex<[T]> for usize {
227271
// Use intrinsics::assume instead of hint::assert_unchecked so that we don't check the
228272
// precondition of this function twice.
229273
crate::intrinsics::assume(self < slice.len());
230-
slice.as_ptr().add(self)
274+
get_noubcheck(slice, self)
231275
}
232276
}
233277

@@ -239,7 +283,7 @@ unsafe impl<T> SliceIndex<[T]> for usize {
239283
(this: usize = self, len: usize = slice.len()) => this < len
240284
);
241285
// SAFETY: see comments for `get_unchecked` above.
242-
unsafe { slice.as_mut_ptr().add(self) }
286+
unsafe { get_mut_noubcheck(slice, self) }
243287
}
244288

245289
#[inline]
@@ -265,7 +309,7 @@ unsafe impl<T> SliceIndex<[T]> for ops::IndexRange {
265309
fn get(self, slice: &[T]) -> Option<&[T]> {
266310
if self.end() <= slice.len() {
267311
// SAFETY: `self` is checked to be valid and in bounds above.
268-
unsafe { Some(&*self.get_unchecked(slice)) }
312+
unsafe { Some(&*get_offset_len_noubcheck(slice, self.start(), self.len())) }
269313
} else {
270314
None
271315
}
@@ -275,7 +319,7 @@ unsafe impl<T> SliceIndex<[T]> for ops::IndexRange {
275319
fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> {
276320
if self.end() <= slice.len() {
277321
// SAFETY: `self` is checked to be valid and in bounds above.
278-
unsafe { Some(&mut *self.get_unchecked_mut(slice)) }
322+
unsafe { Some(&mut *get_offset_len_mut_noubcheck(slice, self.start(), self.len())) }
279323
} else {
280324
None
281325
}
@@ -292,7 +336,7 @@ unsafe impl<T> SliceIndex<[T]> for ops::IndexRange {
292336
// cannot be longer than `isize::MAX`. They also guarantee that
293337
// `self` is in bounds of `slice` so `self` cannot overflow an `isize`,
294338
// so the call to `add` is safe.
295-
unsafe { ptr::slice_from_raw_parts(slice.as_ptr().add(self.start()), self.len()) }
339+
unsafe { get_offset_len_noubcheck(slice, self.start(), self.len()) }
296340
}
297341

298342
#[inline]
@@ -304,14 +348,14 @@ unsafe impl<T> SliceIndex<[T]> for ops::IndexRange {
304348
);
305349

306350
// SAFETY: see comments for `get_unchecked` above.
307-
unsafe { ptr::slice_from_raw_parts_mut(slice.as_mut_ptr().add(self.start()), self.len()) }
351+
unsafe { get_offset_len_mut_noubcheck(slice, self.start(), self.len()) }
308352
}
309353

310354
#[inline]
311355
fn index(self, slice: &[T]) -> &[T] {
312356
if self.end() <= slice.len() {
313357
// SAFETY: `self` is checked to be valid and in bounds above.
314-
unsafe { &*self.get_unchecked(slice) }
358+
unsafe { &*get_offset_len_noubcheck(slice, self.start(), self.len()) }
315359
} else {
316360
slice_end_index_len_fail(self.end(), slice.len())
317361
}
@@ -321,7 +365,7 @@ unsafe impl<T> SliceIndex<[T]> for ops::IndexRange {
321365
fn index_mut(self, slice: &mut [T]) -> &mut [T] {
322366
if self.end() <= slice.len() {
323367
// SAFETY: `self` is checked to be valid and in bounds above.
324-
unsafe { &mut *self.get_unchecked_mut(slice) }
368+
unsafe { &mut *get_offset_len_mut_noubcheck(slice, self.start(), self.len()) }
325369
} else {
326370
slice_end_index_len_fail(self.end(), slice.len())
327371
}
@@ -338,21 +382,26 @@ unsafe impl<T> SliceIndex<[T]> for ops::Range<usize> {
338382

339383
#[inline]
340384
fn get(self, slice: &[T]) -> Option<&[T]> {
341-
if self.start > self.end || self.end > slice.len() {
342-
None
343-
} else {
385+
// Using checked_sub is a safe way to get `SubUnchecked` in MIR
386+
if let Some(new_len) = usize::checked_sub(self.end, self.start)
387+
&& self.end <= slice.len()
388+
{
344389
// SAFETY: `self` is checked to be valid and in bounds above.
345-
unsafe { Some(&*self.get_unchecked(slice)) }
390+
unsafe { Some(&*get_offset_len_noubcheck(slice, self.start, new_len)) }
391+
} else {
392+
None
346393
}
347394
}
348395

349396
#[inline]
350397
fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> {
351-
if self.start > self.end || self.end > slice.len() {
352-
None
353-
} else {
398+
if let Some(new_len) = usize::checked_sub(self.end, self.start)
399+
&& self.end <= slice.len()
400+
{
354401
// SAFETY: `self` is checked to be valid and in bounds above.
355-
unsafe { Some(&mut *self.get_unchecked_mut(slice)) }
402+
unsafe { Some(&mut *get_offset_len_mut_noubcheck(slice, self.start, new_len)) }
403+
} else {
404+
None
356405
}
357406
}
358407

@@ -373,8 +422,10 @@ unsafe impl<T> SliceIndex<[T]> for ops::Range<usize> {
373422
// `self` is in bounds of `slice` so `self` cannot overflow an `isize`,
374423
// so the call to `add` is safe and the length calculation cannot overflow.
375424
unsafe {
376-
let new_len = self.end.unchecked_sub(self.start);
377-
ptr::slice_from_raw_parts(slice.as_ptr().add(self.start), new_len)
425+
// Using the intrinsic avoids a superfluous UB check,
426+
// since the one on this method already checked `end >= start`.
427+
let new_len = crate::intrinsics::unchecked_sub(self.end, self.start);
428+
get_offset_len_noubcheck(slice, self.start, new_len)
378429
}
379430
}
380431

@@ -391,31 +442,34 @@ unsafe impl<T> SliceIndex<[T]> for ops::Range<usize> {
391442
);
392443
// SAFETY: see comments for `get_unchecked` above.
393444
unsafe {
394-
let new_len = self.end.unchecked_sub(self.start);
395-
ptr::slice_from_raw_parts_mut(slice.as_mut_ptr().add(self.start), new_len)
445+
let new_len = crate::intrinsics::unchecked_sub(self.end, self.start);
446+
get_offset_len_mut_noubcheck(slice, self.start, new_len)
396447
}
397448
}
398449

399450
#[inline(always)]
400451
fn index(self, slice: &[T]) -> &[T] {
401-
if self.start > self.end {
402-
slice_index_order_fail(self.start, self.end);
403-
} else if self.end > slice.len() {
452+
// Using checked_sub is a safe way to get `SubUnchecked` in MIR
453+
let Some(new_len) = usize::checked_sub(self.end, self.start) else {
454+
slice_index_order_fail(self.start, self.end)
455+
};
456+
if self.end > slice.len() {
404457
slice_end_index_len_fail(self.end, slice.len());
405458
}
406459
// SAFETY: `self` is checked to be valid and in bounds above.
407-
unsafe { &*self.get_unchecked(slice) }
460+
unsafe { &*get_offset_len_noubcheck(slice, self.start, new_len) }
408461
}
409462

410463
#[inline]
411464
fn index_mut(self, slice: &mut [T]) -> &mut [T] {
412-
if self.start > self.end {
413-
slice_index_order_fail(self.start, self.end);
414-
} else if self.end > slice.len() {
465+
let Some(new_len) = usize::checked_sub(self.end, self.start) else {
466+
slice_index_order_fail(self.start, self.end)
467+
};
468+
if self.end > slice.len() {
415469
slice_end_index_len_fail(self.end, slice.len());
416470
}
417471
// SAFETY: `self` is checked to be valid and in bounds above.
418-
unsafe { &mut *self.get_unchecked_mut(slice) }
472+
unsafe { &mut *get_offset_len_mut_noubcheck(slice, self.start, new_len) }
419473
}
420474
}
421475

0 commit comments

Comments
 (0)
Failed to load comments.