Skip to content

Commit 41701b3

Browse files
committed
Auto merge of rust-lang#134297 - scottmcm:drop-arrays-by-unsizing, r=<try>
Stop emitting drop loops for every array length We can just unsize the array to a slice and drop that instead, reusing the same slice loop for every array length. As part of this (and how I originally noticed this), use `PtrMetadata` instead of `Len` for the slice length, for another step closer to being able to remove `Rvalue::Len`. Demonstration that yes, every slice length gets its own loop today: <https://rust.godbolt.org/z/5EsPjPWv4>
2 parents 4a204be + f6cd47a commit 41701b3

4 files changed

+90
-63
lines changed

compiler/rustc_mir_dataflow/src/elaborate_drops.rs

+62-62
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use rustc_index::Idx;
66
use rustc_middle::mir::patch::MirPatch;
77
use rustc_middle::mir::*;
88
use rustc_middle::span_bug;
9+
use rustc_middle::ty::adjustment::PointerCoercion;
910
use rustc_middle::ty::util::IntTypeExt;
1011
use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt};
1112
use rustc_span::DUMMY_SP;
@@ -738,70 +739,52 @@ where
738739
loop_block
739740
}
740741

741-
fn open_drop_for_array(&mut self, ety: Ty<'tcx>, opt_size: Option<u64>) -> BasicBlock {
742-
debug!("open_drop_for_array({:?}, {:?})", ety, opt_size);
742+
fn open_drop_for_array(
743+
&mut self,
744+
place_ty: Ty<'tcx>,
745+
element_ty: Ty<'tcx>,
746+
opt_size: Option<u64>,
747+
) -> BasicBlock {
748+
debug!("open_drop_for_array({:?}, {:?})", element_ty, opt_size);
743749
let tcx = self.tcx();
744750

745-
if let Some(size) = opt_size {
746-
enum ProjectionKind<Path> {
747-
Drop(std::ops::Range<u64>),
748-
Keep(u64, Path),
749-
}
750-
// Previously, we'd make a projection for every element in the array and create a drop
751-
// ladder if any `array_subpath` was `Some`, i.e. moving out with an array pattern.
752-
// This caused huge memory usage when generating the drops for large arrays, so we instead
753-
// record the *subslices* which are dropped and the *indexes* which are kept
754-
let mut drop_ranges = vec![];
755-
let mut dropping = true;
756-
let mut start = 0;
757-
for i in 0..size {
758-
let path = self.elaborator.array_subpath(self.path, i, size);
759-
if dropping && path.is_some() {
760-
drop_ranges.push(ProjectionKind::Drop(start..i));
761-
dropping = false;
762-
} else if !dropping && path.is_none() {
763-
dropping = true;
764-
start = i;
765-
}
766-
if let Some(path) = path {
767-
drop_ranges.push(ProjectionKind::Keep(i, path));
768-
}
769-
}
770-
if !drop_ranges.is_empty() {
771-
if dropping {
772-
drop_ranges.push(ProjectionKind::Drop(start..size));
773-
}
774-
let fields = drop_ranges
775-
.iter()
776-
.rev()
777-
.map(|p| {
778-
let (project, path) = match p {
779-
ProjectionKind::Drop(r) => (
780-
ProjectionElem::Subslice {
781-
from: r.start,
782-
to: r.end,
783-
from_end: false,
784-
},
785-
None,
786-
),
787-
&ProjectionKind::Keep(offset, path) => (
788-
ProjectionElem::ConstantIndex {
789-
offset,
790-
min_length: size,
791-
from_end: false,
792-
},
793-
Some(path),
794-
),
795-
};
796-
(tcx.mk_place_elem(self.place, project), path)
797-
})
798-
.collect::<Vec<_>>();
799-
let (succ, unwind) = self.drop_ladder_bottom();
800-
return self.drop_ladder(fields, succ, unwind).0;
801-
}
751+
if let Some(0) = opt_size {
752+
span_bug!(self.source_info.span, "Opened drop for zero-length array of {element_ty:?}")
802753
}
803754

804-
self.drop_loop_pair(ety)
755+
let array_ptr_ty = Ty::new_mut_ptr(tcx, place_ty);
756+
let array_ptr = self.new_temp(array_ptr_ty);
757+
let slice_ty = Ty::new_slice(tcx, element_ty);
758+
let slice_ptr_ty = Ty::new_mut_ptr(tcx, slice_ty);
759+
let slice_ptr = self.new_temp(slice_ptr_ty);
760+
761+
let unsize_and_drop_block = BasicBlockData {
762+
statements: vec![
763+
self.assign(Place::from(array_ptr), Rvalue::RawPtr(Mutability::Mut, self.place)),
764+
self.assign(
765+
Place::from(slice_ptr),
766+
Rvalue::Cast(
767+
CastKind::PointerCoercion(
768+
PointerCoercion::Unsize,
769+
CoercionSource::Implicit,
770+
),
771+
Operand::Move(Place::from(array_ptr)),
772+
slice_ptr_ty,
773+
),
774+
),
775+
],
776+
is_cleanup: self.unwind.is_cleanup(),
777+
terminator: Some(Terminator {
778+
source_info: self.source_info,
779+
kind: TerminatorKind::Drop {
780+
place: Place::from(slice_ptr).project_deeper(&[PlaceElem::Deref], tcx),
781+
target: self.succ,
782+
unwind: self.unwind.into_action(),
783+
replace: false,
784+
},
785+
}),
786+
};
787+
self.elaborator.patch().new_block(unsize_and_drop_block)
805788
}
806789

807790
/// Creates a pair of drop-loops of `place`, which drops its contents, even
@@ -817,10 +800,27 @@ where
817800

818801
let loop_block = self.drop_loop(self.succ, cur, len, ety, unwind);
819802

803+
let place_ty = self.place_ty(self.place);
804+
assert!(place_ty.is_slice(), "Expected slice, got {place_ty:?}");
805+
806+
let [PlaceElem::Deref] = self.place.projection.as_slice() else {
807+
span_bug!(
808+
self.source_info.span,
809+
"Expected place for slice drop shim to be *_n, but it's {:?}",
810+
self.place,
811+
);
812+
};
813+
820814
let zero = self.constant_usize(0);
821815
let block = BasicBlockData {
822816
statements: vec![
823-
self.assign(len.into(), Rvalue::Len(self.place)),
817+
self.assign(
818+
len.into(),
819+
Rvalue::UnaryOp(
820+
UnOp::PtrMetadata,
821+
Operand::Copy(Place::from(self.place.local)),
822+
),
823+
),
824824
self.assign(cur.into(), Rvalue::Use(zero)),
825825
],
826826
is_cleanup: unwind.is_cleanup(),
@@ -863,7 +863,7 @@ where
863863
ty::Dynamic(..) => self.complete_drop(self.succ, self.unwind),
864864
ty::Array(ety, size) => {
865865
let size = size.try_to_target_usize(self.tcx());
866-
self.open_drop_for_array(*ety, size)
866+
self.open_drop_for_array(ty, *ety, size)
867867
}
868868
ty::Slice(ety) => self.drop_loop_pair(*ety),
869869

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// MIR for `std::ptr::drop_in_place` before AddMovesForPackedDrops
2+
3+
fn std::ptr::drop_in_place(_1: *mut [String; 42]) -> () {
4+
let mut _0: ();
5+
let mut _2: *mut [std::string::String; 42];
6+
let mut _3: *mut [std::string::String];
7+
8+
bb0: {
9+
goto -> bb3;
10+
}
11+
12+
bb1: {
13+
return;
14+
}
15+
16+
bb2 (cleanup): {
17+
resume;
18+
}
19+
20+
bb3: {
21+
_2 = &raw mut (*_1);
22+
_3 = move _2 as *mut [std::string::String] (PointerCoercion(Unsize, Implicit));
23+
drop((*_3)) -> [return: bb1, unwind: bb2];
24+
}
25+
}

tests/mir-opt/slice_drop_shim.core.ptr-drop_in_place.[String].AddMovesForPackedDrops.before.mir

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ fn std::ptr::drop_in_place(_1: *mut [String]) -> () {
4444
}
4545

4646
bb7: {
47-
_2 = Len((*_1));
47+
_2 = PtrMetadata(copy _1);
4848
_3 = const 0_usize;
4949
goto -> bb6;
5050
}

tests/mir-opt/slice_drop_shim.rs

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
// if we use -Clink-dead-code.
66

77
// EMIT_MIR core.ptr-drop_in_place.[String].AddMovesForPackedDrops.before.mir
8+
// EMIT_MIR core.ptr-drop_in_place.[String;42].AddMovesForPackedDrops.before.mir
89
fn main() {
910
let _fn = std::ptr::drop_in_place::<[String]> as unsafe fn(_);
11+
let _fn = std::ptr::drop_in_place::<[String; 42]> as unsafe fn(_);
1012
}

0 commit comments

Comments
 (0)