Skip to content

Commit 4cf8aa1

Browse files
committed
slice.get(i) should use a slice projection in MIR, like slice[i] does
1 parent ae8ab87 commit 4cf8aa1

14 files changed

+359
-77
lines changed

compiler/rustc_hir_analysis/src/check/intrinsic.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ pub fn check_intrinsic_type(
277277
vec![Ty::new_imm_ptr(tcx, param(0)), tcx.types.isize],
278278
Ty::new_imm_ptr(tcx, param(0)),
279279
),
280+
sym::slice_get_unchecked => (2, 0, vec![param(0), tcx.types.usize], param(1)),
280281
sym::ptr_mask => (
281282
1,
282283
0,

compiler/rustc_mir_transform/src/lower_intrinsics.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,52 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics {
262262
});
263263
terminator.kind = TerminatorKind::Goto { target };
264264
}
265+
sym::slice_get_unchecked => {
266+
let target = target.unwrap();
267+
let Ok([ptrish, index]) = take_array(args) else {
268+
span_bug!(
269+
terminator.source_info.span,
270+
"Wrong number of arguments for {intrinsic:?}",
271+
);
272+
};
273+
274+
let place = ptrish.node.place().unwrap();
275+
assert!(!place.is_indirect());
276+
let updated_place = place.project_deeper(
277+
&[
278+
ProjectionElem::Deref,
279+
ProjectionElem::Index(
280+
index.node.place().unwrap().as_local().unwrap(),
281+
),
282+
],
283+
tcx,
284+
);
285+
286+
let ret_ty = generic_args.type_at(0);
287+
let rvalue = match *ret_ty.kind() {
288+
ty::RawPtr(_, Mutability::Not) => {
289+
Rvalue::RawPtr(RawPtrKind::Const, updated_place)
290+
}
291+
ty::RawPtr(_, Mutability::Mut) => {
292+
Rvalue::RawPtr(RawPtrKind::Mut, updated_place)
293+
}
294+
ty::Ref(region, _, Mutability::Not) => {
295+
Rvalue::Ref(region, BorrowKind::Shared, updated_place)
296+
}
297+
ty::Ref(region, _, Mutability::Mut) => Rvalue::Ref(
298+
region,
299+
BorrowKind::Mut { kind: MutBorrowKind::Default },
300+
updated_place,
301+
),
302+
_ => bug!("Unknown return type {ret_ty:?}"),
303+
};
304+
305+
block.statements.push(Statement {
306+
source_info: terminator.source_info,
307+
kind: StatementKind::Assign(Box::new((*destination, rvalue))),
308+
});
309+
terminator.kind = TerminatorKind::Goto { target };
310+
}
265311
sym::transmute | sym::transmute_unchecked => {
266312
let dst_ty = destination.ty(local_decls, tcx).ty;
267313
let Ok([arg]) = take_array(args) else {

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1953,6 +1953,7 @@ symbols! {
19531953
slice,
19541954
slice_from_raw_parts,
19551955
slice_from_raw_parts_mut,
1956+
slice_get_unchecked,
19561957
slice_into_vec,
19571958
slice_iter,
19581959
slice_len_fn,

library/core/src/intrinsics/mod.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1758,6 +1758,31 @@ pub const unsafe fn offset<Ptr, Delta>(dst: Ptr, offset: Delta) -> Ptr;
17581758
#[rustc_intrinsic]
17591759
pub const unsafe fn arith_offset<T>(dst: *const T, offset: isize) -> *const T;
17601760

1761+
/// Indexes into a slice without performing bounds checks.
1762+
///
1763+
/// Equivalent to `{&, &mut, &raw const, &raw mut} (*slice_ptr)[index]`,
1764+
/// depending on the types involved.
1765+
///
1766+
/// This is exposed via `SliceIndex::get_unchecked(_mut)`,
1767+
/// and isn't intended to be used elsewhere.
1768+
///
1769+
/// # Safety
1770+
///
1771+
/// `index < PtrMetadata(slice_ptr)`, and the resulting offsetting is in-bounds
1772+
#[cfg(not(bootstrap))]
1773+
#[rustc_nounwind]
1774+
#[rustc_intrinsic]
1775+
pub const unsafe fn slice_get_unchecked<SlicePtr: SliceGetUnchecked<ItemPtr>, ItemPtr>(
1776+
slice_ptr: SlicePtr,
1777+
index: usize,
1778+
) -> ItemPtr;
1779+
1780+
pub trait SliceGetUnchecked<T> {}
1781+
impl<T> SliceGetUnchecked<*const T> for *const [T] {}
1782+
impl<T> SliceGetUnchecked<*mut T> for *mut [T] {}
1783+
impl<'a, T> SliceGetUnchecked<&'a T> for &'a [T] {}
1784+
impl<'a, T> SliceGetUnchecked<&'a mut T> for &'a mut [T] {}
1785+
17611786
/// Masks out bits of the pointer according to a mask.
17621787
///
17631788
/// Note that, unlike most intrinsics, this is safe to call;

library/core/src/slice/index.rs

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
//! Indexing implementations for `[T]`.
22
3+
#[cfg(not(bootstrap))]
4+
use crate::intrinsics::slice_get_unchecked;
35
use crate::panic::const_panic;
46
use crate::ub_checks::assert_unsafe_precondition;
57
use crate::{ops, range};
@@ -83,13 +85,15 @@ const fn slice_end_index_overflow_fail() -> ! {
8385
// Both the safe and unsafe public methods share these helpers,
8486
// which use intrinsics directly to get *no* extra checks.
8587

88+
#[cfg(bootstrap)]
8689
#[inline(always)]
8790
const unsafe fn get_noubcheck<T>(ptr: *const [T], index: usize) -> *const T {
8891
let ptr = ptr as *const T;
8992
// SAFETY: The caller already checked these preconditions
9093
unsafe { crate::intrinsics::offset(ptr, index) }
9194
}
9295

96+
#[cfg(bootstrap)]
9397
#[inline(always)]
9498
const unsafe fn get_mut_noubcheck<T>(ptr: *mut [T], index: usize) -> *mut T {
9599
let ptr = ptr as *mut T;
@@ -103,8 +107,9 @@ const unsafe fn get_offset_len_noubcheck<T>(
103107
offset: usize,
104108
len: usize,
105109
) -> *const [T] {
110+
let ptr = ptr as *const T;
106111
// SAFETY: The caller already checked these preconditions
107-
let ptr = unsafe { get_noubcheck(ptr, offset) };
112+
let ptr = unsafe { crate::intrinsics::offset(ptr, offset) };
108113
crate::intrinsics::aggregate_raw_ptr(ptr, len)
109114
}
110115

@@ -114,8 +119,9 @@ const unsafe fn get_offset_len_mut_noubcheck<T>(
114119
offset: usize,
115120
len: usize,
116121
) -> *mut [T] {
122+
let ptr = ptr as *mut T;
117123
// SAFETY: The caller already checked these preconditions
118-
let ptr = unsafe { get_mut_noubcheck(ptr, offset) };
124+
let ptr = unsafe { crate::intrinsics::offset(ptr, offset) };
119125
crate::intrinsics::aggregate_raw_ptr(ptr, len)
120126
}
121127

@@ -224,15 +230,35 @@ unsafe impl<T> SliceIndex<[T]> for usize {
224230

225231
#[inline]
226232
fn get(self, slice: &[T]) -> Option<&T> {
227-
// SAFETY: `self` is checked to be in bounds.
228-
if self < slice.len() { unsafe { Some(&*get_noubcheck(slice, self)) } } else { None }
233+
if self < slice.len() {
234+
#[cfg(bootstrap)]
235+
// SAFETY: `self` is checked to be in bounds.
236+
unsafe {
237+
Some(&*get_noubcheck(slice, self))
238+
}
239+
#[cfg(not(bootstrap))]
240+
// SAFETY: `self` is checked to be in bounds.
241+
unsafe {
242+
Some(slice_get_unchecked(slice, self))
243+
}
244+
} else {
245+
None
246+
}
229247
}
230248

231249
#[inline]
232250
fn get_mut(self, slice: &mut [T]) -> Option<&mut T> {
233251
if self < slice.len() {
252+
#[cfg(bootstrap)]
253+
// SAFETY: `self` is checked to be in bounds.
254+
unsafe {
255+
Some(&mut *get_mut_noubcheck(slice, self))
256+
}
257+
#[cfg(not(bootstrap))]
234258
// SAFETY: `self` is checked to be in bounds.
235-
unsafe { Some(&mut *get_mut_noubcheck(slice, self)) }
259+
unsafe {
260+
Some(slice_get_unchecked(slice, self))
261+
}
236262
} else {
237263
None
238264
}
@@ -253,7 +279,14 @@ unsafe impl<T> SliceIndex<[T]> for usize {
253279
// Use intrinsics::assume instead of hint::assert_unchecked so that we don't check the
254280
// precondition of this function twice.
255281
crate::intrinsics::assume(self < slice.len());
256-
get_noubcheck(slice, self)
282+
#[cfg(bootstrap)]
283+
{
284+
get_noubcheck(slice, self)
285+
}
286+
#[cfg(not(bootstrap))]
287+
{
288+
slice_get_unchecked(slice, self)
289+
}
257290
}
258291
}
259292

@@ -265,7 +298,16 @@ unsafe impl<T> SliceIndex<[T]> for usize {
265298
(this: usize = self, len: usize = slice.len()) => this < len
266299
);
267300
// SAFETY: see comments for `get_unchecked` above.
268-
unsafe { get_mut_noubcheck(slice, self) }
301+
unsafe {
302+
#[cfg(bootstrap)]
303+
{
304+
get_mut_noubcheck(slice, self)
305+
}
306+
#[cfg(not(bootstrap))]
307+
{
308+
slice_get_unchecked(slice, self)
309+
}
310+
}
269311
}
270312

271313
#[inline]

tests/mir-opt/lower_intrinsics.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,3 +267,24 @@ pub fn get_metadata(a: *const i32, b: *const [u8], c: *const dyn std::fmt::Debug
267267
let _usize = ptr_metadata(b);
268268
let _vtable = ptr_metadata(c);
269269
}
270+
271+
// EMIT_MIR lower_intrinsics.slice_get.LowerIntrinsics.diff
272+
pub unsafe fn slice_get<'a, 'b>(
273+
r: &'a [i8],
274+
rm: &'b mut [i16],
275+
p: *const [i32],
276+
pm: *mut [i64],
277+
i: usize,
278+
) -> (&'a i8, &'b mut i16, *const i32, *mut i64) {
279+
use std::intrinsics::slice_get_unchecked;
280+
// CHECK: = &(*_{{[0-9]+}})[_{{[0-9]+}}]
281+
// CHECK: = &mut (*_{{[0-9]+}})[_{{[0-9]+}}]
282+
// CHECK: = &raw const (*_{{[0-9]+}})[_{{[0-9]+}}]
283+
// CHECK: = &raw mut (*_{{[0-9]+}})[_{{[0-9]+}}]
284+
(
285+
slice_get_unchecked(r, i),
286+
slice_get_unchecked(rm, i),
287+
slice_get_unchecked(p, i),
288+
slice_get_unchecked(pm, i),
289+
)
290+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
- // MIR for `slice_get` before LowerIntrinsics
2+
+ // MIR for `slice_get` after LowerIntrinsics
3+
4+
fn slice_get(_1: &[i8], _2: &mut [i16], _3: *const [i32], _4: *mut [i64], _5: usize) -> (&i8, &mut i16, *const i32, *mut i64) {
5+
debug r => _1;
6+
debug rm => _2;
7+
debug p => _3;
8+
debug pm => _4;
9+
debug i => _5;
10+
let mut _0: (&i8, &mut i16, *const i32, *mut i64);
11+
let mut _6: &i8;
12+
let _7: &i8;
13+
let mut _8: &[i8];
14+
let mut _9: usize;
15+
let mut _10: &mut i16;
16+
let mut _11: &mut i16;
17+
let mut _12: &mut [i16];
18+
let mut _13: usize;
19+
let mut _14: *const i32;
20+
let mut _15: *const [i32];
21+
let mut _16: usize;
22+
let mut _17: *mut i64;
23+
let mut _18: *mut [i64];
24+
let mut _19: usize;
25+
26+
bb0: {
27+
StorageLive(_6);
28+
StorageLive(_7);
29+
StorageLive(_8);
30+
_8 = copy _1;
31+
StorageLive(_9);
32+
_9 = copy _5;
33+
- _7 = slice_get_unchecked::<&[i8], &i8>(move _8, move _9) -> [return: bb1, unwind unreachable];
34+
+ _7 = &(*_8)[_9];
35+
+ goto -> bb1;
36+
}
37+
38+
bb1: {
39+
_6 = &(*_7);
40+
StorageDead(_9);
41+
StorageDead(_8);
42+
StorageLive(_10);
43+
StorageLive(_11);
44+
StorageLive(_12);
45+
_12 = move _2;
46+
StorageLive(_13);
47+
_13 = copy _5;
48+
- _11 = slice_get_unchecked::<&mut [i16], &mut i16>(move _12, move _13) -> [return: bb2, unwind unreachable];
49+
+ _11 = &mut (*_12)[_13];
50+
+ goto -> bb2;
51+
}
52+
53+
bb2: {
54+
_10 = &mut (*_11);
55+
StorageDead(_13);
56+
StorageDead(_12);
57+
StorageLive(_14);
58+
StorageLive(_15);
59+
_15 = copy _3;
60+
StorageLive(_16);
61+
_16 = copy _5;
62+
- _14 = slice_get_unchecked::<*const [i32], *const i32>(move _15, move _16) -> [return: bb3, unwind unreachable];
63+
+ _14 = &raw const (*_15)[_16];
64+
+ goto -> bb3;
65+
}
66+
67+
bb3: {
68+
StorageDead(_16);
69+
StorageDead(_15);
70+
StorageLive(_17);
71+
StorageLive(_18);
72+
_18 = copy _4;
73+
StorageLive(_19);
74+
_19 = copy _5;
75+
- _17 = slice_get_unchecked::<*mut [i64], *mut i64>(move _18, move _19) -> [return: bb4, unwind unreachable];
76+
+ _17 = &raw mut (*_18)[_19];
77+
+ goto -> bb4;
78+
}
79+
80+
bb4: {
81+
StorageDead(_19);
82+
StorageDead(_18);
83+
_0 = (move _6, move _10, move _14, move _17);
84+
StorageDead(_17);
85+
StorageDead(_14);
86+
StorageDead(_10);
87+
StorageDead(_6);
88+
StorageDead(_11);
89+
StorageDead(_7);
90+
return;
91+
}
92+
}
93+

0 commit comments

Comments
 (0)