Rollup merge of #155184 - scottmcm:intercept-array-drop-shim, r=WaffleLapkin

Have arrays' `drop_glue` just unsize and call the slice version

It's silly to emit two loops (because of the drop ladder -- just one in panic=abort) for every array length that's dropped when we can just polymorphize to the slice version.

Built atop rust-lang/rust#154327 to avoid conflicts later, so draft for now.

r? @WaffleLapkin
This commit is contained in:
Jonathan Brouwer
2026-05-11 23:03:06 +02:00
committed by GitHub
4 changed files with 113 additions and 54 deletions
+1 -1
View File
@@ -292,7 +292,7 @@ pub fn requires_inline(&self, tcx: TyCtxt<'tcx>) -> bool {
use rustc_hir::definitions::DefPathData;
let def_id = match *self {
ty::InstanceKind::Item(def) => def,
ty::InstanceKind::DropGlue(_, Some(_)) => return false,
ty::InstanceKind::DropGlue(_, Some(ty)) => return ty.is_array(),
ty::InstanceKind::AsyncDropGlueCtorShim(_, ty) => return ty.is_coroutine(),
ty::InstanceKind::FutureDropPollShim(_, _, _) => return false,
ty::InstanceKind::AsyncDropGlue(_, _) => return false,
+42 -2
View File
@@ -317,16 +317,56 @@ fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option<Ty<'tcx>>)
let block = |blocks: &mut IndexVec<_, _>, kind| {
blocks.push(BasicBlockData::new(Some(Terminator { source_info, kind }), false))
};
block(&mut blocks, TerminatorKind::Goto { target: return_block });
if ty.is_some() {
block(&mut blocks, TerminatorKind::Goto { target: return_block });
}
block(&mut blocks, TerminatorKind::Return);
let source = MirSource::from_instance(ty::InstanceKind::DropGlue(def_id, ty));
let mut body =
new_body(source, blocks, local_decls_for_sig(&sig, span), sig.inputs().len(), span);
let Some(ty) = ty else {
return body;
};
let dropee_ptr = Place::from(Local::arg(0));
if ty.is_some() {
if let ty::Array(ety, _len) = *ty.kind() {
// Don't write out the elaboration for each array type.
// Instead, just delegate to the slice version.
let slice_ty = Ty::new_slice(tcx, ety);
let mut_slice_ty = Ty::new_ref(tcx, tcx.lifetimes.re_erased, slice_ty, ty::Mutability::Mut);
let erased_local = body.local_decls.push(LocalDecl::new(mut_slice_ty, span));
let start = &mut body.basic_blocks_mut()[START_BLOCK];
start.statements.push(Statement::new(
source_info,
StatementKind::Assign(Box::new((
Place::from(erased_local),
Rvalue::Cast(
CastKind::PointerCoercion(
ty::adjustment::PointerCoercion::Unsize,
CoercionSource::Implicit,
),
Operand::Move(dropee_ptr),
mut_slice_ty,
),
))),
));
start.terminator = Some(Terminator {
source_info,
kind: TerminatorKind::Call {
func: Operand::function_handle(tcx, def_id, [ty::GenericArg::from(slice_ty)], span),
args: Box::new([Spanned { span, node: Operand::Move(Place::from(erased_local)) }]),
destination: Place::from(RETURN_PLACE),
target: Some(return_block),
unwind: UnwindAction::Continue,
call_source: CallSource::Misc,
fn_span: span,
},
});
} else {
let patch = {
let typing_env = ty::TypingEnv::post_analysis(tcx, def_id);
let mut elaborator = DropShimElaborator {
+67
View File
@@ -0,0 +1,67 @@
//@ revisions: RAW OPT
//@ compile-flags: -C opt-level=z -C panic=abort
//@[RAW] compile-flags: -C no-prepopulate-passes -Z inline-mir
#![crate_type = "lib"]
// Ensure all the different array drop_glue functions just delegate to the slice one,
// rather than emitting two loops in each of the three.
// When this test was first written, the array drop glues came out in the
// seemingly-arbitrary order of 42, then 7, then 13, so to avoid potential
// fragility from that changing we don't check any particular order.
// RAW: ; core::ptr::drop_glue::<[array_drop_glue::NeedsDrop; [[N:7|13|42]]]>
// RAW-NEXT: inlinehint
// RAW: call core::ptr::drop_glue::<[array_drop_glue::NeedsDrop]>
// RAW-NEXT: noundef [[N]])
// RAW: }
// RAW: ; core::ptr::drop_glue::<[array_drop_glue::NeedsDrop; [[N:7|13|42]]]>
// RAW-NEXT: inlinehint
// RAW: call core::ptr::drop_glue::<[array_drop_glue::NeedsDrop]>
// RAW-NEXT: noundef [[N]])
// RAW: }
// RAW: ; core::ptr::drop_glue::<[array_drop_glue::NeedsDrop; [[N:7|13|42]]]>
// RAW-NEXT: inlinehint
// RAW: call core::ptr::drop_glue::<[array_drop_glue::NeedsDrop]>
// RAW-NEXT: noundef [[N]])
// RAW: }
// CHECK-LABEL: ; core::ptr::drop_glue::<[array_drop_glue::NeedsDrop]>
// CHECK-NOT: inlinehint
// OPT: add nuw nsw {{.+}} 1
// CHECK: }
#[no_mangle]
// CHECK-LABEL: @drop_arrays
pub fn drop_arrays(x: [NeedsDrop; 7], y: [NeedsDrop; 13], z: [NeedsDrop; 42]) {
// I don't remember the parameter drop order, so write out the order the test expects.
// RAW: call core::ptr::drop_glue::<[array_drop_glue::NeedsDrop; 7]>
// OPT: call core::ptr::drop_glue::<[array_drop_glue::NeedsDrop]>
// OPT-NEXT: noundef 7)
drop(x);
// RAW: call core::ptr::drop_glue::<[array_drop_glue::NeedsDrop; 13]>
// OPT: call core::ptr::drop_glue::<[array_drop_glue::NeedsDrop]>
// OPT-NEXT: noundef 13)
drop(y);
// RAW: call core::ptr::drop_glue::<[array_drop_glue::NeedsDrop; 42]>
// OPT: call core::ptr::drop_glue::<[array_drop_glue::NeedsDrop]>
// OPT-NEXT: noundef 42)
drop(z);
}
struct NeedsDrop(u32);
impl Drop for NeedsDrop {
#[inline]
fn drop(&mut self) {
do_the_drop(self);
}
}
unsafe extern "Rust" {
safe fn do_the_drop(_: &mut NeedsDrop);
}
@@ -2,62 +2,14 @@
fn std::ptr::drop_glue(_1: &mut [String; 42]) -> () {
let mut _0: ();
let mut _2: *mut [std::string::String; 42];
let mut _3: *mut [std::string::String];
let mut _4: usize;
let mut _5: usize;
let mut _6: *mut std::string::String;
let mut _7: bool;
let mut _8: *mut std::string::String;
let mut _9: bool;
let mut _2: &mut [std::string::String];
bb0: {
goto -> bb9;
_2 = move _1 as &mut [std::string::String] (PointerCoercion(Unsize, Implicit));
_0 = std::ptr::drop_glue::<[String]>(move _2) -> [return: bb1, unwind continue];
}
bb1: {
return;
}
bb2 (cleanup): {
resume;
}
bb3 (cleanup): {
_6 = &raw mut (*_3)[_5];
_5 = Add(move _5, const 1_usize);
drop((*_6)) -> [return: bb4, unwind terminate(cleanup)];
}
bb4 (cleanup): {
_7 = Eq(copy _5, copy _4);
switchInt(move _7) -> [0: bb3, otherwise: bb2];
}
bb5: {
_8 = &raw mut (*_3)[_5];
_5 = Add(move _5, const 1_usize);
drop((*_8)) -> [return: bb6, unwind: bb4];
}
bb6: {
_9 = Eq(copy _5, copy _4);
switchInt(move _9) -> [0: bb5, otherwise: bb1];
}
bb7: {
_4 = PtrMetadata(copy _3);
_5 = const 0_usize;
goto -> bb6;
}
bb8: {
goto -> bb7;
}
bb9: {
_2 = &raw mut (*_1);
_3 = move _2 as *mut [std::string::String] (PointerCoercion(Unsize, Implicit));
goto -> bb8;
}
}