Skip to content

Commit 2d383ad

Browse files
committed
Add slice_index intrinsic
1 parent 1c05d50 commit 2d383ad

File tree

7 files changed

+293
-1
lines changed

7 files changed

+293
-1
lines changed

compiler/rustc_hir_analysis/src/check/intrinsic.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
216216

217217
sym::type_name => (1, Vec::new(), Ty::new_static_str(tcx)),
218218
sym::type_id => (1, Vec::new(), tcx.types.u128),
219+
sym::slice_index => (2, vec![param(0), tcx.types.usize], param(1)),
219220
sym::offset => (2, vec![param(0), param(1)], param(0)),
220221
sym::arith_offset => (
221222
1,

compiler/rustc_mir_transform/src/lower_intrinsics.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,43 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
234234
terminator.kind = TerminatorKind::Goto { target };
235235
}
236236
}
237+
sym::slice_index => {
238+
let target = target.unwrap();
239+
240+
let elem =
241+
if let Some(slice_ptr) = args[0].place()
242+
&& let Some(index_place) = args[1].place()
243+
&& let Some(index) = index_place.as_local()
244+
{
245+
let slice = tcx.mk_place_deref(slice_ptr);
246+
tcx.mk_place_index(slice, index)
247+
} else {
248+
span_bug!(
249+
terminator.source_info.span,
250+
"Invalid call to slice_index"
251+
);
252+
};
253+
254+
let elem_ptr =
255+
match generic_args.type_at(1).kind() {
256+
ty::Ref(region, _, Mutability::Mut) =>
257+
Rvalue::Ref(*region, BorrowKind::Mut { kind: MutBorrowKind::Default }, elem),
258+
ty::Ref(region, _, Mutability::Not) =>
259+
Rvalue::Ref(*region, BorrowKind::Shared, elem),
260+
ty::RawPtr(ty_and_mut) =>
261+
Rvalue::AddressOf(ty_and_mut.mutbl, elem),
262+
x => bug!("Invalid return type for slice_index: {x:?}"),
263+
};
264+
265+
block.statements.push(Statement {
266+
source_info: terminator.source_info,
267+
kind: StatementKind::Assign(Box::new((
268+
*destination,
269+
elem_ptr,
270+
))),
271+
});
272+
terminator.kind = TerminatorKind::Goto { target };
273+
}
237274
sym::offset => {
238275
let target = target.unwrap();
239276
let Ok([ptr, delta]) = <[_; 2]>::try_from(std::mem::take(args)) else {

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1528,6 +1528,7 @@ symbols! {
15281528
slice,
15291529
slice_from_raw_parts,
15301530
slice_from_raw_parts_mut,
1531+
slice_index,
15311532
slice_len_fn,
15321533
slice_patterns,
15331534
slicing_syntax,

library/core/src/intrinsics.rs

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
7474
unsafe { crate::ptr::drop_in_place(to_drop) }
7575
}
7676

77+
#[allow(private_bounds)]
7778
extern "rust-intrinsic" {
7879
// N.B., these intrinsics take raw pointers because they mutate aliased
7980
// memory, which is not valid for either `&` or `&mut`.
@@ -1410,6 +1411,39 @@ extern "rust-intrinsic" {
14101411
#[rustc_nounwind]
14111412
pub fn needs_drop<T: ?Sized>() -> bool;
14121413

1414+
/// Indexes into a slice using `ProjectionElem::Index`
1415+
///
1416+
/// Depending on the return type, this lowers to one of the following in MIR:
1417+
/// - `&raw const (*slice)[index]`, for `*const _`
1418+
/// - `&raw mut (*slice)[index]`, for `*mut _`
1419+
/// - `&(*slice)[index]`, for `&_`
1420+
/// - `&mut (*slice)[index]`, for `&mut _`
1421+
///
1422+
/// This is intended for use in implementing `[T]::get_unchecked_mut`,
1423+
/// `[T]::get`, and friends. It should not be used elsewhere; ICEs from
1424+
/// misuse in callers of intrinsics are *not* considered compiler bugs.
1425+
///
1426+
/// While semantically unnecessary, as it could be done via `offset`, this
1427+
/// reduces and simplifies the MIR produced for common operations.
1428+
///
1429+
/// # Safety
1430+
///
1431+
/// Requires `index < slice.len()` and that `slice` points to a valid slice.
1432+
///
1433+
/// If the return type is a reference (mutable or shared), then you must
1434+
/// ensure that its lifetime is no longer than the input's.
1435+
#[cfg(not(bootstrap))]
1436+
#[must_use = "returns a new pointer rather than modifying its argument"]
1437+
#[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
1438+
#[rustc_nounwind]
1439+
pub fn slice_index<
1440+
Slice: PointerOrReference<To = [Item::To]>,
1441+
Item: PointerOrReference<To: Sized>,
1442+
>(
1443+
slice: Slice,
1444+
index: usize,
1445+
) -> Item;
1446+
14131447
/// Calculates the offset from a pointer.
14141448
///
14151449
/// This is implemented as an intrinsic to avoid converting to and from an
@@ -1430,7 +1464,7 @@ extern "rust-intrinsic" {
14301464
#[must_use = "returns a new pointer rather than modifying its argument"]
14311465
#[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
14321466
#[rustc_nounwind]
1433-
pub fn offset<Ptr, Delta>(dst: Ptr, offset: Delta) -> Ptr;
1467+
pub fn offset<Ptr: PointerOrReference<To: Sized>, Delta>(dst: Ptr, offset: Delta) -> Ptr;
14341468

14351469
/// Calculates the offset from a pointer, potentially wrapping.
14361470
///
@@ -2855,3 +2889,20 @@ pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize) {
28552889
write_bytes(dst, val, count)
28562890
}
28572891
}
2892+
2893+
/// No semantic meaning; just to help reduce mistakes with generic intrinsics
2894+
trait PointerOrReference {
2895+
type To: ?Sized;
2896+
}
2897+
impl<T: ?Sized> PointerOrReference for &T {
2898+
type To = T;
2899+
}
2900+
impl<T: ?Sized> PointerOrReference for &mut T {
2901+
type To = T;
2902+
}
2903+
impl<T: ?Sized> PointerOrReference for *const T {
2904+
type To = T;
2905+
}
2906+
impl<T: ?Sized> PointerOrReference for *mut T {
2907+
type To = T;
2908+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
- // MIR for `index_project` before LowerIntrinsics
2+
+ // MIR for `index_project` after LowerIntrinsics
3+
4+
fn index_project(_1: &mut [i32], _2: &[i32], _3: *mut [i32], _4: *const [i32], _5: usize) -> () {
5+
debug a => _1;
6+
debug b => _2;
7+
debug c => _3;
8+
debug d => _4;
9+
debug i => _5;
10+
let mut _0: ();
11+
let _6: &mut i32;
12+
let mut _7: &mut [i32];
13+
let mut _8: usize;
14+
let mut _10: &[i32];
15+
let mut _11: usize;
16+
let mut _13: *mut [i32];
17+
let mut _14: usize;
18+
let mut _16: *const [i32];
19+
let mut _17: usize;
20+
scope 1 {
21+
debug _a => _6;
22+
let _9: &i32;
23+
scope 2 {
24+
debug _b => _9;
25+
let _12: *mut i32;
26+
scope 3 {
27+
debug _c => _12;
28+
let _15: *const i32;
29+
scope 4 {
30+
debug _d => _15;
31+
}
32+
}
33+
}
34+
}
35+
36+
bb0: {
37+
StorageLive(_6);
38+
StorageLive(_7);
39+
_7 = move _1;
40+
StorageLive(_8);
41+
_8 = _5;
42+
- _6 = slice_index::<&mut [i32], &mut i32>(move _7, move _8) -> [return: bb1, unwind unreachable];
43+
+ _6 = &mut (*_7)[_8];
44+
+ goto -> bb1;
45+
}
46+
47+
bb1: {
48+
StorageDead(_8);
49+
StorageDead(_7);
50+
StorageLive(_9);
51+
StorageLive(_10);
52+
_10 = _2;
53+
StorageLive(_11);
54+
_11 = _5;
55+
- _9 = slice_index::<&[i32], &i32>(move _10, move _11) -> [return: bb2, unwind unreachable];
56+
+ _9 = &(*_10)[_11];
57+
+ goto -> bb2;
58+
}
59+
60+
bb2: {
61+
StorageDead(_11);
62+
StorageDead(_10);
63+
StorageLive(_12);
64+
StorageLive(_13);
65+
_13 = _3;
66+
StorageLive(_14);
67+
_14 = _5;
68+
- _12 = slice_index::<*mut [i32], *mut i32>(move _13, move _14) -> [return: bb3, unwind unreachable];
69+
+ _12 = &raw mut (*_13)[_14];
70+
+ goto -> bb3;
71+
}
72+
73+
bb3: {
74+
StorageDead(_14);
75+
StorageDead(_13);
76+
StorageLive(_15);
77+
StorageLive(_16);
78+
_16 = _4;
79+
StorageLive(_17);
80+
_17 = _5;
81+
- _15 = slice_index::<*const [i32], *const i32>(move _16, move _17) -> [return: bb4, unwind unreachable];
82+
+ _15 = &raw const (*_16)[_17];
83+
+ goto -> bb4;
84+
}
85+
86+
bb4: {
87+
StorageDead(_17);
88+
StorageDead(_16);
89+
_0 = const ();
90+
StorageDead(_15);
91+
StorageDead(_12);
92+
StorageDead(_9);
93+
StorageDead(_6);
94+
return;
95+
}
96+
}
97+
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
- // MIR for `index_project` before LowerIntrinsics
2+
+ // MIR for `index_project` after LowerIntrinsics
3+
4+
fn index_project(_1: &mut [i32], _2: &[i32], _3: *mut [i32], _4: *const [i32], _5: usize) -> () {
5+
debug a => _1;
6+
debug b => _2;
7+
debug c => _3;
8+
debug d => _4;
9+
debug i => _5;
10+
let mut _0: ();
11+
let _6: &mut i32;
12+
let mut _7: &mut [i32];
13+
let mut _8: usize;
14+
let mut _10: &[i32];
15+
let mut _11: usize;
16+
let mut _13: *mut [i32];
17+
let mut _14: usize;
18+
let mut _16: *const [i32];
19+
let mut _17: usize;
20+
scope 1 {
21+
debug _a => _6;
22+
let _9: &i32;
23+
scope 2 {
24+
debug _b => _9;
25+
let _12: *mut i32;
26+
scope 3 {
27+
debug _c => _12;
28+
let _15: *const i32;
29+
scope 4 {
30+
debug _d => _15;
31+
}
32+
}
33+
}
34+
}
35+
36+
bb0: {
37+
StorageLive(_6);
38+
StorageLive(_7);
39+
_7 = move _1;
40+
StorageLive(_8);
41+
_8 = _5;
42+
- _6 = slice_index::<&mut [i32], &mut i32>(move _7, move _8) -> [return: bb1, unwind unreachable];
43+
+ _6 = &mut (*_7)[_8];
44+
+ goto -> bb1;
45+
}
46+
47+
bb1: {
48+
StorageDead(_8);
49+
StorageDead(_7);
50+
StorageLive(_9);
51+
StorageLive(_10);
52+
_10 = _2;
53+
StorageLive(_11);
54+
_11 = _5;
55+
- _9 = slice_index::<&[i32], &i32>(move _10, move _11) -> [return: bb2, unwind unreachable];
56+
+ _9 = &(*_10)[_11];
57+
+ goto -> bb2;
58+
}
59+
60+
bb2: {
61+
StorageDead(_11);
62+
StorageDead(_10);
63+
StorageLive(_12);
64+
StorageLive(_13);
65+
_13 = _3;
66+
StorageLive(_14);
67+
_14 = _5;
68+
- _12 = slice_index::<*mut [i32], *mut i32>(move _13, move _14) -> [return: bb3, unwind unreachable];
69+
+ _12 = &raw mut (*_13)[_14];
70+
+ goto -> bb3;
71+
}
72+
73+
bb3: {
74+
StorageDead(_14);
75+
StorageDead(_13);
76+
StorageLive(_15);
77+
StorageLive(_16);
78+
_16 = _4;
79+
StorageLive(_17);
80+
_17 = _5;
81+
- _15 = slice_index::<*const [i32], *const i32>(move _16, move _17) -> [return: bb4, unwind unreachable];
82+
+ _15 = &raw const (*_16)[_17];
83+
+ goto -> bb4;
84+
}
85+
86+
bb4: {
87+
StorageDead(_17);
88+
StorageDead(_16);
89+
_0 = const ();
90+
StorageDead(_15);
91+
StorageDead(_12);
92+
StorageDead(_9);
93+
StorageDead(_6);
94+
return;
95+
}
96+
}
97+

tests/mir-opt/lower_intrinsics.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,3 +241,11 @@ pub unsafe fn ptr_offset(p: *const i32, d: isize) -> *const i32 {
241241

242242
core::intrinsics::offset(p, d)
243243
}
244+
245+
// EMIT_MIR lower_intrinsics.index_project.LowerIntrinsics.diff
246+
pub unsafe fn index_project(a: &mut [i32], b: &[i32], c: *mut [i32], d: *const [i32], i: usize) {
247+
let _a: &mut i32 = core::intrinsics::slice_index(a, i);
248+
let _b: &i32 = core::intrinsics::slice_index(b, i);
249+
let _c: *mut i32 = core::intrinsics::slice_index(c, i);
250+
let _d: *const i32 = core::intrinsics::slice_index(d, i);
251+
}

0 commit comments

Comments
 (0)