Auto merge of #126299 - scottmcm:tune-sliceindex-ubchecks, r=saethlin · model-checking/verify-rust-std@ae7f43e (original) (raw)
`@@ -2,7 +2,6 @@
`
2
2
``
3
3
`use crate::intrinsics::const_eval_select;
`
4
4
`use crate::ops;
`
5
``
`-
use crate::ptr;
`
6
5
`use crate::ub_checks::assert_unsafe_precondition;
`
7
6
``
8
7
`#[stable(feature = "rust1", since = "1.0.0")]
`
`@@ -106,6 +105,47 @@ const fn slice_end_index_overflow_fail() -> ! {
`
106
105
`panic!("attempted to index slice up to maximum usize");
`
107
106
`}
`
108
107
``
``
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(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(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(
`
``
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(
`
``
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
+
109
149
`mod private_slice_index {
`
110
150
`use super::ops;
`
111
151
`#[stable(feature = "slice_get_slice", since = "1.28.0")]
`
`@@ -203,13 +243,17 @@ unsafe impl SliceIndex<[T]> for usize {
`
203
243
`#[inline]
`
204
244
`fn get(self, slice: &[T]) -> Option<&T> {
`
205
245
`` // 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 }
`
207
247
`}
`
208
248
``
209
249
`#[inline]
`
210
250
`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
`+
}
`
213
257
`}
`
214
258
``
215
259
`#[inline]
`
`@@ -227,7 +271,7 @@ unsafe impl SliceIndex<[T]> for usize {
`
227
271
`// Use intrinsics::assume instead of hint::assert_unchecked so that we don't check the
`
228
272
`// precondition of this function twice.
`
229
273
`crate::intrinsics::assume(self < slice.len());
`
230
``
`-
slice.as_ptr().add(self)
`
``
274
`+
get_noubcheck(slice, self)
`
231
275
`}
`
232
276
`}
`
233
277
``
`@@ -239,7 +283,7 @@ unsafe impl SliceIndex<[T]> for usize {
`
239
283
`(this: usize = self, len: usize = slice.len()) => this < len
`
240
284
`);
`
241
285
`` // SAFETY: see comments for get_unchecked
above.
``
242
``
`-
unsafe { slice.as_mut_ptr().add(self) }
`
``
286
`+
unsafe { get_mut_noubcheck(slice, self) }
`
243
287
`}
`
244
288
``
245
289
`#[inline]
`
`@@ -265,7 +309,7 @@ unsafe impl SliceIndex<[T]> for ops::IndexRange {
`
265
309
`fn get(self, slice: &[T]) -> Option<&[T]> {
`
266
310
`if self.end() <= slice.len() {
`
267
311
`` // 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())) }
`
269
313
`} else {
`
270
314
`None
`
271
315
`}
`
`@@ -275,7 +319,7 @@ unsafe impl SliceIndex<[T]> for ops::IndexRange {
`
275
319
`fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> {
`
276
320
`if self.end() <= slice.len() {
`
277
321
`` // 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())) }
`
279
323
`} else {
`
280
324
`None
`
281
325
`}
`
`@@ -292,7 +336,7 @@ unsafe impl SliceIndex<[T]> for ops::IndexRange {
`
292
336
`` // cannot be longer than isize::MAX
. They also guarantee that
``
293
337
`` // self
is in bounds of slice
so self
cannot overflow an isize
,
``
294
338
`` // 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()) }
`
296
340
`}
`
297
341
``
298
342
`#[inline]
`
`@@ -304,14 +348,14 @@ unsafe impl SliceIndex<[T]> for ops::IndexRange {
`
304
348
`);
`
305
349
``
306
350
`` // 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()) }
`
308
352
`}
`
309
353
``
310
354
`#[inline]
`
311
355
`fn index(self, slice: &[T]) -> &[T] {
`
312
356
`if self.end() <= slice.len() {
`
313
357
`` // 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()) }
`
315
359
`} else {
`
316
360
`slice_end_index_len_fail(self.end(), slice.len())
`
317
361
`}
`
`@@ -321,7 +365,7 @@ unsafe impl SliceIndex<[T]> for ops::IndexRange {
`
321
365
`fn index_mut(self, slice: &mut [T]) -> &mut [T] {
`
322
366
`if self.end() <= slice.len() {
`
323
367
`` // 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()) }
`
325
369
`} else {
`
326
370
`slice_end_index_len_fail(self.end(), slice.len())
`
327
371
`}
`
`@@ -338,21 +382,26 @@ unsafe impl SliceIndex<[T]> for ops::Range {
`
338
382
``
339
383
`#[inline]
`
340
384
`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
`+
{
`
344
389
`` // 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
`
346
393
`}
`
347
394
`}
`
348
395
``
349
396
`#[inline]
`
350
397
`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
`+
{
`
354
401
`` // 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
`
356
405
`}
`
357
406
`}
`
358
407
``
`@@ -373,8 +422,10 @@ unsafe impl SliceIndex<[T]> for ops::Range {
`
373
422
`` // self
is in bounds of slice
so self
cannot overflow an isize
,
``
374
423
`` // so the call to add
is safe and the length calculation cannot overflow.
``
375
424
`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)
`
378
429
`}
`
379
430
`}
`
380
431
``
`@@ -391,31 +442,34 @@ unsafe impl SliceIndex<[T]> for ops::Range {
`
391
442
`);
`
392
443
`` // SAFETY: see comments for get_unchecked
above.
``
393
444
`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)
`
396
447
`}
`
397
448
`}
`
398
449
``
399
450
`#[inline(always)]
`
400
451
`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() {
`
404
457
`slice_end_index_len_fail(self.end, slice.len());
`
405
458
`}
`
406
459
`` // 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) }
`
408
461
`}
`
409
462
``
410
463
`#[inline]
`
411
464
`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() {
`
415
469
`slice_end_index_len_fail(self.end, slice.len());
`
416
470
`}
`
417
471
`` // 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) }
`
419
473
`}
`
420
474
`}
`
421
475
``