Skip to content

Commit ee3a7e0

Browse files
committed
Bounds-check with PtrMetadata instead of Len in MIR
1 parent 5e1440a commit ee3a7e0

File tree

90 files changed

+1213
-1461
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

90 files changed

+1213
-1461
lines changed

compiler/rustc_const_eval/src/check_consts/check.rs

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -573,12 +573,27 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
573573
) => {}
574574
Rvalue::ShallowInitBox(_, _) => {}
575575

576-
Rvalue::UnaryOp(_, operand) => {
576+
Rvalue::UnaryOp(op, operand) => {
577577
let ty = operand.ty(self.body, self.tcx);
578-
if is_int_bool_float_or_char(ty) {
579-
// Int, bool, float, and char operations are fine.
580-
} else {
581-
span_bug!(self.span, "non-primitive type in `Rvalue::UnaryOp`: {:?}", ty);
578+
match op {
579+
UnOp::Not | UnOp::Neg => {
580+
if is_int_bool_float_or_char(ty) {
581+
// Int, bool, float, and char operations are fine.
582+
} else {
583+
span_bug!(
584+
self.span,
585+
"non-primitive type in `Rvalue::UnaryOp{op:?}`: {ty:?}",
586+
);
587+
}
588+
}
589+
UnOp::PtrMetadata => {
590+
if !ty.is_ref() && !ty.is_unsafe_ptr() {
591+
span_bug!(
592+
self.span,
593+
"non-pointer type in `Rvalue::UnaryOp({op:?})`: {ty:?}",
594+
);
595+
}
596+
}
582597
}
583598
}
584599

compiler/rustc_mir_build/src/build/expr/as_place.rs

Lines changed: 65 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,63 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
630630
block.and(base_place.index(idx))
631631
}
632632

633+
/// Given a place that's either an array or a slice, returns an operand
634+
/// with the length of the array/slice.
635+
///
636+
/// For arrays it'll be `Operand::Constant` with the actual length;
637+
/// For slices it'll be `Operand::Move` of a local using `PtrMetadata`.
638+
fn len_of_slice_or_array(
639+
&mut self,
640+
block: BasicBlock,
641+
slice: Place<'tcx>,
642+
expr_span: Span,
643+
source_info: SourceInfo,
644+
) -> Operand<'tcx> {
645+
let slice_ty = slice.ty(&self.local_decls, self.tcx).ty;
646+
let usize_ty = self.tcx.types.usize;
647+
if let ty::Array(_elem_ty, len_const) = slice_ty.kind() {
648+
Operand::Constant(Box::new(ConstOperand {
649+
span: expr_span,
650+
user_ty: None,
651+
const_: Const::from_ty_const(len_const.clone(), usize_ty, self.tcx),
652+
}))
653+
} else if slice_ty.is_slice() {
654+
let ptr_or_ref = if let [PlaceElem::Deref] = slice.projection[..]
655+
&& let local_ty = self.local_decls[slice.local].ty
656+
&& local_ty.is_trivially_pure_clone_copy()
657+
{
658+
// It's extremely common that we have something that can be
659+
// directly passed to `PtrMetadata`, so avoid an unnecessary
660+
// temporary and statement in those cases. Note that we can
661+
// only do that for `Copy` types -- not `&mut [_]` -- because
662+
// the MIR we're building here needs to pass NLL later.
663+
Operand::Copy(Place::from(slice.local))
664+
} else {
665+
let ptr_ty = Ty::new_imm_ptr(self.tcx, slice_ty);
666+
let ptr = self.temp(ptr_ty, expr_span);
667+
self.cfg.push_assign(
668+
block,
669+
source_info,
670+
ptr,
671+
Rvalue::RawPtr(Mutability::Not, slice),
672+
);
673+
Operand::Move(ptr)
674+
};
675+
676+
let len = self.temp(usize_ty, expr_span);
677+
self.cfg.push_assign(
678+
block,
679+
source_info,
680+
len,
681+
Rvalue::UnaryOp(UnOp::PtrMetadata, ptr_or_ref),
682+
);
683+
684+
Operand::Move(len)
685+
} else {
686+
span_bug!(expr_span, "len called on something that's neither slice nor array")
687+
}
688+
}
689+
633690
fn bounds_check(
634691
&mut self,
635692
block: BasicBlock,
@@ -638,25 +695,25 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
638695
expr_span: Span,
639696
source_info: SourceInfo,
640697
) -> BasicBlock {
641-
let usize_ty = self.tcx.types.usize;
642-
let bool_ty = self.tcx.types.bool;
643-
// bounds check:
644-
let len = self.temp(usize_ty, expr_span);
645-
let lt = self.temp(bool_ty, expr_span);
698+
let slice = slice.to_place(self);
646699

647700
// len = len(slice)
648-
self.cfg.push_assign(block, source_info, len, Rvalue::Len(slice.to_place(self)));
701+
let len = self.len_of_slice_or_array(block, slice, expr_span, source_info);
702+
649703
// lt = idx < len
704+
let bool_ty = self.tcx.types.bool;
705+
let lt = self.temp(bool_ty, expr_span);
650706
self.cfg.push_assign(
651707
block,
652708
source_info,
653709
lt,
654710
Rvalue::BinaryOp(
655711
BinOp::Lt,
656-
Box::new((Operand::Copy(Place::from(index)), Operand::Copy(len))),
712+
Box::new((Operand::Copy(Place::from(index)), len.to_copy())),
657713
),
658714
);
659-
let msg = BoundsCheck { len: Operand::Move(len), index: Operand::Copy(Place::from(index)) };
715+
let msg = BoundsCheck { len, index: Operand::Copy(Place::from(index)) };
716+
660717
// assert!(lt, "...")
661718
self.assert(block, Operand::Move(lt), true, msg, expr_span)
662719
}

compiler/rustc_mir_transform/src/instsimplify.rs

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ impl<'tcx> crate::MirPass<'tcx> for InstSimplify {
4747
}
4848
ctx.simplify_bool_cmp(rvalue);
4949
ctx.simplify_ref_deref(rvalue);
50-
ctx.simplify_len(rvalue);
5150
ctx.simplify_ptr_aggregate(rvalue);
5251
ctx.simplify_cast(rvalue);
5352
}
@@ -132,18 +131,6 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> {
132131
}
133132
}
134133

135-
/// Transform `Len([_; N])` ==> `N`.
136-
fn simplify_len(&self, rvalue: &mut Rvalue<'tcx>) {
137-
if let Rvalue::Len(ref place) = *rvalue {
138-
let place_ty = place.ty(self.local_decls, self.tcx).ty;
139-
if let ty::Array(_, len) = *place_ty.kind() {
140-
let const_ = Const::from_ty_const(len, self.tcx.types.usize, self.tcx);
141-
let constant = ConstOperand { span: DUMMY_SP, const_, user_ty: None };
142-
*rvalue = Rvalue::Use(Operand::Constant(Box::new(constant)));
143-
}
144-
}
145-
}
146-
147134
/// Transform `Aggregate(RawPtr, [p, ()])` ==> `Cast(PtrToPtr, p)`.
148135
fn simplify_ptr_aggregate(&self, rvalue: &mut Rvalue<'tcx>) {
149136
if let Rvalue::Aggregate(box AggregateKind::RawPtr(pointee_ty, mutability), fields) = rvalue

compiler/rustc_mir_transform/src/validate.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1130,14 +1130,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
11301130
);
11311131
}
11321132
UnOp::PtrMetadata => {
1133-
if !matches!(self.mir_phase, MirPhase::Runtime(_)) {
1134-
// It would probably be fine to support this in earlier phases, but at
1135-
// the time of writing it's only ever introduced from intrinsic
1136-
// lowering or other runtime-phase optimization passes, so earlier
1137-
// things can just `bug!` on it.
1138-
self.fail(location, "PtrMetadata should be in runtime MIR only");
1139-
}
1140-
11411133
check_kinds!(
11421134
a,
11431135
"Cannot PtrMetadata non-pointer non-reference type {:?}",

tests/mir-opt/array_index_is_temporary.main.SimplifyCfg-pre-optimizations.after.panic-abort.mir

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ fn main() -> () {
77
let mut _5: u32;
88
let mut _6: *mut usize;
99
let _7: usize;
10-
let mut _8: usize;
11-
let mut _9: bool;
10+
let mut _8: bool;
1211
scope 1 {
1312
debug x => _1;
1413
let mut _2: usize;
@@ -41,9 +40,8 @@ fn main() -> () {
4140
StorageDead(_6);
4241
StorageLive(_7);
4342
_7 = copy _2;
44-
_8 = Len(_1);
45-
_9 = Lt(copy _7, copy _8);
46-
assert(move _9, "index out of bounds: the length is {} but the index is {}", move _8, copy _7) -> [success: bb2, unwind unreachable];
43+
_8 = Lt(copy _7, const 3_usize);
44+
assert(move _8, "index out of bounds: the length is {} but the index is {}", const 3_usize, copy _7) -> [success: bb2, unwind unreachable];
4745
}
4846

4947
bb2: {

tests/mir-opt/array_index_is_temporary.main.SimplifyCfg-pre-optimizations.after.panic-unwind.mir

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ fn main() -> () {
77
let mut _5: u32;
88
let mut _6: *mut usize;
99
let _7: usize;
10-
let mut _8: usize;
11-
let mut _9: bool;
10+
let mut _8: bool;
1211
scope 1 {
1312
debug x => _1;
1413
let mut _2: usize;
@@ -41,9 +40,8 @@ fn main() -> () {
4140
StorageDead(_6);
4241
StorageLive(_7);
4342
_7 = copy _2;
44-
_8 = Len(_1);
45-
_9 = Lt(copy _7, copy _8);
46-
assert(move _9, "index out of bounds: the length is {} but the index is {}", move _8, copy _7) -> [success: bb2, unwind continue];
43+
_8 = Lt(copy _7, const 3_usize);
44+
assert(move _8, "index out of bounds: the length is {} but the index is {}", const 3_usize, copy _7) -> [success: bb2, unwind continue];
4745
}
4846

4947
bb2: {
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// MIR for `index_array` after built
2+
3+
fn index_array(_1: &[i32; 7], _2: usize) -> &i32 {
4+
debug array => _1;
5+
debug index => _2;
6+
let mut _0: &i32;
7+
let _3: &i32;
8+
let _4: usize;
9+
let mut _5: bool;
10+
11+
bb0: {
12+
StorageLive(_3);
13+
StorageLive(_4);
14+
_4 = copy _2;
15+
_5 = Lt(copy _4, const 7_usize);
16+
assert(move _5, "index out of bounds: the length is {} but the index is {}", const 7_usize, copy _4) -> [success: bb1, unwind: bb2];
17+
}
18+
19+
bb1: {
20+
_3 = &(*_1)[_4];
21+
_0 = &(*_3);
22+
StorageDead(_4);
23+
StorageDead(_3);
24+
return;
25+
}
26+
27+
bb2 (cleanup): {
28+
resume;
29+
}
30+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// MIR for `index_custom` after built
2+
3+
fn index_custom(_1: &WithSliceTail, _2: usize) -> &i32 {
4+
debug custom => _1;
5+
debug index => _2;
6+
let mut _0: &i32;
7+
let _3: &i32;
8+
let _4: usize;
9+
let mut _5: *const [i32];
10+
let mut _6: usize;
11+
let mut _7: bool;
12+
13+
bb0: {
14+
StorageLive(_3);
15+
StorageLive(_4);
16+
_4 = copy _2;
17+
_5 = &raw const ((*_1).1: [i32]);
18+
_6 = PtrMetadata(move _5);
19+
_7 = Lt(copy _4, copy _6);
20+
assert(move _7, "index out of bounds: the length is {} but the index is {}", move _6, copy _4) -> [success: bb1, unwind: bb2];
21+
}
22+
23+
bb1: {
24+
_3 = &((*_1).1: [i32])[_4];
25+
_0 = &(*_3);
26+
StorageDead(_4);
27+
StorageDead(_3);
28+
return;
29+
}
30+
31+
bb2 (cleanup): {
32+
resume;
33+
}
34+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// MIR for `index_mut_slice` after built
2+
3+
fn index_mut_slice(_1: &mut [i32], _2: usize) -> &i32 {
4+
debug slice => _1;
5+
debug index => _2;
6+
let mut _0: &i32;
7+
let _3: &i32;
8+
let _4: usize;
9+
let mut _5: *const [i32];
10+
let mut _6: usize;
11+
let mut _7: bool;
12+
13+
bb0: {
14+
StorageLive(_3);
15+
StorageLive(_4);
16+
_4 = copy _2;
17+
_5 = &raw const (*_1);
18+
_6 = PtrMetadata(move _5);
19+
_7 = Lt(copy _4, copy _6);
20+
assert(move _7, "index out of bounds: the length is {} but the index is {}", move _6, copy _4) -> [success: bb1, unwind: bb2];
21+
}
22+
23+
bb1: {
24+
_3 = &(*_1)[_4];
25+
_0 = &(*_3);
26+
StorageDead(_4);
27+
StorageDead(_3);
28+
return;
29+
}
30+
31+
bb2 (cleanup): {
32+
resume;
33+
}
34+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// MIR for `index_slice` after built
2+
3+
fn index_slice(_1: &[i32], _2: usize) -> &i32 {
4+
debug slice => _1;
5+
debug index => _2;
6+
let mut _0: &i32;
7+
let _3: &i32;
8+
let _4: usize;
9+
let mut _5: usize;
10+
let mut _6: bool;
11+
12+
bb0: {
13+
StorageLive(_3);
14+
StorageLive(_4);
15+
_4 = copy _2;
16+
_5 = PtrMetadata(copy _1);
17+
_6 = Lt(copy _4, copy _5);
18+
assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, copy _4) -> [success: bb1, unwind: bb2];
19+
}
20+
21+
bb1: {
22+
_3 = &(*_1)[_4];
23+
_0 = &(*_3);
24+
StorageDead(_4);
25+
StorageDead(_3);
26+
return;
27+
}
28+
29+
bb2 (cleanup): {
30+
resume;
31+
}
32+
}

0 commit comments

Comments
 (0)