Skip to content

Commit 8fae7b1

Browse files
Auto merge of #142583 - cjgillot:inline-drop, r=<try>
Inline drops in MIR r? `@ghost` <!-- homu-ignore:start --> <!-- If this PR is related to an unstable feature or an otherwise tracked effort, please link to the relevant tracking issue here. If you don't know of a related tracking issue or there are none, feel free to ignore this. This PR will get automatically assigned to a reviewer. In case you would like a specific user to review your work, you can assign it to them by using r? <reviewer name> --> <!-- homu-ignore:end -->
2 parents d9ca9bd + b76e9cb commit 8fae7b1

File tree

43 files changed

+2222
-637
lines changed

Some content is hidden

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

43 files changed

+2222
-637
lines changed

compiler/rustc_mir_transform/src/inline.rs

Lines changed: 145 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use std::ops::{Range, RangeFrom};
66

77
use rustc_abi::{ExternAbi, FieldIdx};
88
use rustc_attr_data_structures::{InlineAttr, OptimizeAttr};
9+
use rustc_hir::LangItem;
910
use rustc_hir::def::DefKind;
1011
use rustc_hir::def_id::DefId;
1112
use rustc_index::Idx;
@@ -553,46 +554,53 @@ fn resolve_callsite<'tcx, I: Inliner<'tcx>>(
553554
let terminator = bb_data.terminator();
554555

555556
// FIXME(explicit_tail_calls): figure out if we can inline tail calls
556-
if let TerminatorKind::Call { ref func, fn_span, .. } = terminator.kind {
557-
let func_ty = func.ty(caller_body, tcx);
558-
if let ty::FnDef(def_id, args) = *func_ty.kind() {
559-
if !inliner.should_inline_for_callee(def_id) {
560-
debug!("not enabled");
561-
return None;
562-
}
563-
564-
// To resolve an instance its args have to be fully normalized.
565-
let args = tcx.try_normalize_erasing_regions(inliner.typing_env(), args).ok()?;
566-
let callee =
567-
Instance::try_resolve(tcx, inliner.typing_env(), def_id, args).ok().flatten()?;
557+
let (def_id, args, fn_span) = match terminator.kind {
558+
TerminatorKind::Call { ref func, fn_span, .. } => {
559+
let func_ty = func.ty(caller_body, tcx);
560+
let ty::FnDef(def_id, args) = *func_ty.kind() else { return None };
561+
(def_id, args, fn_span)
562+
}
563+
TerminatorKind::Drop { place, .. } => {
564+
let ty = place.ty(caller_body, tcx).ty;
565+
let def_id = tcx.require_lang_item(LangItem::DropInPlace, terminator.source_info.span);
566+
let args = tcx.mk_args(&[ty.into()]);
567+
(def_id, args, terminator.source_info.span)
568+
}
569+
_ => return None,
570+
};
568571

569-
if let InstanceKind::Virtual(..) | InstanceKind::Intrinsic(_) = callee.def {
570-
return None;
571-
}
572+
if !inliner.should_inline_for_callee(def_id) {
573+
debug!("not enabled");
574+
return None;
575+
}
572576

573-
if inliner.history().contains(&callee.def_id()) {
574-
return None;
575-
}
577+
// To resolve an instance its args have to be fully normalized.
578+
let args = tcx.try_normalize_erasing_regions(inliner.typing_env(), args).ok()?;
579+
let callee = Instance::try_resolve(tcx, inliner.typing_env(), def_id, args).ok().flatten()?;
576580

577-
let fn_sig = tcx.fn_sig(def_id).instantiate(tcx, args);
581+
if let InstanceKind::Virtual(..) | InstanceKind::Intrinsic(_) = callee.def {
582+
return None;
583+
}
578584

579-
// Additionally, check that the body that we're inlining actually agrees
580-
// with the ABI of the trait that the item comes from.
581-
if let InstanceKind::Item(instance_def_id) = callee.def
582-
&& tcx.def_kind(instance_def_id) == DefKind::AssocFn
583-
&& let instance_fn_sig = tcx.fn_sig(instance_def_id).skip_binder()
584-
&& instance_fn_sig.abi() != fn_sig.abi()
585-
{
586-
return None;
587-
}
585+
if inliner.history().contains(&callee.def_id()) {
586+
return None;
587+
}
588588

589-
let source_info = SourceInfo { span: fn_span, ..terminator.source_info };
589+
let fn_sig = tcx.fn_sig(def_id).instantiate(tcx, args);
590590

591-
return Some(CallSite { callee, fn_sig, block: bb, source_info });
592-
}
591+
// Additionally, check that the body that we're inlining actually agrees
592+
// with the ABI of the trait that the item comes from.
593+
if let InstanceKind::Item(instance_def_id) = callee.def
594+
&& tcx.def_kind(instance_def_id) == DefKind::AssocFn
595+
&& let instance_fn_sig = tcx.fn_sig(instance_def_id).skip_binder()
596+
&& instance_fn_sig.abi() != fn_sig.abi()
597+
{
598+
return None;
593599
}
594600

595-
None
601+
let source_info = SourceInfo { span: fn_span, ..terminator.source_info };
602+
603+
Some(CallSite { callee, fn_sig, block: bb, source_info })
596604
}
597605

598606
/// Attempts to inline a callsite into the caller body. When successful returns basic blocks
@@ -603,6 +611,23 @@ fn try_inlining<'tcx, I: Inliner<'tcx>>(
603611
caller_body: &mut Body<'tcx>,
604612
callsite: &CallSite<'tcx>,
605613
) -> Result<std::ops::Range<BasicBlock>, &'static str> {
614+
// Fast path to inline trivial drops.
615+
if let InstanceKind::DropGlue(_, None) = callsite.callee.def {
616+
let terminator = caller_body[callsite.block].terminator_mut();
617+
let target = match terminator.kind {
618+
TerminatorKind::Call { target, .. } => target,
619+
TerminatorKind::Drop { target, .. } => Some(target),
620+
_ => bug!("unexpected terminator kind {:?}", terminator.kind),
621+
};
622+
if let Some(target) = target {
623+
terminator.kind = TerminatorKind::Goto { target };
624+
} else {
625+
terminator.kind = TerminatorKind::Unreachable;
626+
}
627+
let next_block = caller_body.basic_blocks.next_index();
628+
return Ok(next_block..next_block);
629+
}
630+
606631
let tcx = inliner.tcx();
607632
check_mir_is_available(inliner, caller_body, callsite.callee)?;
608633

@@ -611,17 +636,6 @@ fn try_inlining<'tcx, I: Inliner<'tcx>>(
611636
check_codegen_attributes(inliner, callsite, callee_attrs)?;
612637
inliner.check_codegen_attributes_extra(callee_attrs)?;
613638

614-
let terminator = caller_body[callsite.block].terminator.as_ref().unwrap();
615-
let TerminatorKind::Call { args, destination, .. } = &terminator.kind else { bug!() };
616-
let destination_ty = destination.ty(&caller_body.local_decls, tcx).ty;
617-
for arg in args {
618-
if !arg.node.ty(&caller_body.local_decls, tcx).is_sized(tcx, inliner.typing_env()) {
619-
// We do not allow inlining functions with unsized params. Inlining these functions
620-
// could create unsized locals, which are unsound and being phased out.
621-
return Err("call has unsized argument");
622-
}
623-
}
624-
625639
let callee_body = try_instance_mir(tcx, callsite.callee.def)?;
626640
check_inline::is_inline_valid_on_body(tcx, callee_body)?;
627641
inliner.check_callee_mir_body(callsite, callee_body, callee_attrs)?;
@@ -642,54 +656,73 @@ fn try_inlining<'tcx, I: Inliner<'tcx>>(
642656
return Err("implementation limitation -- callee body failed validation");
643657
}
644658

645-
// Check call signature compatibility.
646-
// Normally, this shouldn't be required, but trait normalization failure can create a
647-
// validation ICE.
648-
let output_type = callee_body.return_ty();
649-
if !util::sub_types(tcx, inliner.typing_env(), output_type, destination_ty) {
650-
trace!(?output_type, ?destination_ty);
651-
return Err("implementation limitation -- return type mismatch");
652-
}
653-
if callsite.fn_sig.abi() == ExternAbi::RustCall {
654-
let (self_arg, arg_tuple) = match &args[..] {
655-
[arg_tuple] => (None, arg_tuple),
656-
[self_arg, arg_tuple] => (Some(self_arg), arg_tuple),
657-
_ => bug!("Expected `rust-call` to have 1 or 2 args"),
658-
};
659+
let terminator = caller_body[callsite.block].terminator.as_ref().unwrap();
660+
match &terminator.kind {
661+
TerminatorKind::Call { args, destination, .. } => {
662+
let destination_ty = destination.ty(&caller_body.local_decls, tcx).ty;
663+
for arg in args {
664+
if !arg.node.ty(&caller_body.local_decls, tcx).is_sized(tcx, inliner.typing_env()) {
665+
// We do not allow inlining functions with unsized params. Inlining these functions
666+
// could create unsized locals, which are unsound and being phased out.
667+
return Err("call has unsized argument");
668+
}
669+
}
659670

660-
let self_arg_ty = self_arg.map(|self_arg| self_arg.node.ty(&caller_body.local_decls, tcx));
671+
// Check call signature compatibility.
672+
// Normally, this shouldn't be required, but trait normalization failure can create a
673+
// validation ICE.
674+
let output_type = callee_body.return_ty();
675+
if !util::sub_types(tcx, inliner.typing_env(), output_type, destination_ty) {
676+
trace!(?output_type, ?destination_ty);
677+
return Err("implementation limitation -- return type mismatch");
678+
}
679+
if callsite.fn_sig.abi() == ExternAbi::RustCall {
680+
let (self_arg, arg_tuple) = match &args[..] {
681+
[arg_tuple] => (None, arg_tuple),
682+
[self_arg, arg_tuple] => (Some(self_arg), arg_tuple),
683+
_ => bug!("Expected `rust-call` to have 1 or 2 args"),
684+
};
661685

662-
let arg_tuple_ty = arg_tuple.node.ty(&caller_body.local_decls, tcx);
663-
let arg_tys = if callee_body.spread_arg.is_some() {
664-
std::slice::from_ref(&arg_tuple_ty)
665-
} else {
666-
let ty::Tuple(arg_tuple_tys) = *arg_tuple_ty.kind() else {
667-
bug!("Closure arguments are not passed as a tuple");
668-
};
669-
arg_tuple_tys.as_slice()
670-
};
686+
let self_arg_ty =
687+
self_arg.map(|self_arg| self_arg.node.ty(&caller_body.local_decls, tcx));
671688

672-
for (arg_ty, input) in
673-
self_arg_ty.into_iter().chain(arg_tys.iter().copied()).zip(callee_body.args_iter())
674-
{
675-
let input_type = callee_body.local_decls[input].ty;
676-
if !util::sub_types(tcx, inliner.typing_env(), input_type, arg_ty) {
677-
trace!(?arg_ty, ?input_type);
678-
debug!("failed to normalize tuple argument type");
679-
return Err("implementation limitation");
680-
}
681-
}
682-
} else {
683-
for (arg, input) in args.iter().zip(callee_body.args_iter()) {
684-
let input_type = callee_body.local_decls[input].ty;
685-
let arg_ty = arg.node.ty(&caller_body.local_decls, tcx);
686-
if !util::sub_types(tcx, inliner.typing_env(), input_type, arg_ty) {
687-
trace!(?arg_ty, ?input_type);
688-
debug!("failed to normalize argument type");
689-
return Err("implementation limitation -- arg mismatch");
689+
let arg_tuple_ty = arg_tuple.node.ty(&caller_body.local_decls, tcx);
690+
let arg_tys = if callee_body.spread_arg.is_some() {
691+
std::slice::from_ref(&arg_tuple_ty)
692+
} else {
693+
let ty::Tuple(arg_tuple_tys) = *arg_tuple_ty.kind() else {
694+
bug!("Closure arguments are not passed as a tuple");
695+
};
696+
arg_tuple_tys.as_slice()
697+
};
698+
699+
for (arg_ty, input) in self_arg_ty
700+
.into_iter()
701+
.chain(arg_tys.iter().copied())
702+
.zip(callee_body.args_iter())
703+
{
704+
let input_type = callee_body.local_decls[input].ty;
705+
if !util::sub_types(tcx, inliner.typing_env(), input_type, arg_ty) {
706+
trace!(?arg_ty, ?input_type);
707+
debug!("failed to normalize tuple argument type");
708+
return Err("implementation limitation");
709+
}
710+
}
711+
} else {
712+
for (arg, input) in args.iter().zip(callee_body.args_iter()) {
713+
let input_type = callee_body.local_decls[input].ty;
714+
let arg_ty = arg.node.ty(&caller_body.local_decls, tcx);
715+
if !util::sub_types(tcx, inliner.typing_env(), input_type, arg_ty) {
716+
trace!(?arg_ty, ?input_type);
717+
debug!("failed to normalize argument type");
718+
return Err("implementation limitation -- arg mismatch");
719+
}
720+
}
690721
}
691722
}
692-
}
723+
TerminatorKind::Drop { .. } => {}
724+
_ => bug!(),
725+
};
693726

694727
let old_blocks = caller_body.basic_blocks.next_index();
695728
inline_call(inliner, caller_body, callsite, callee_body);
@@ -854,9 +887,31 @@ fn inline_call<'tcx, I: Inliner<'tcx>>(
854887
) {
855888
let tcx = inliner.tcx();
856889
let terminator = caller_body[callsite.block].terminator.take().unwrap();
857-
let TerminatorKind::Call { func, args, destination, unwind, target, .. } = terminator.kind
858-
else {
859-
bug!("unexpected terminator kind {:?}", terminator.kind);
890+
let (args, destination, unwind, target) = match terminator.kind {
891+
TerminatorKind::Call { args, destination, unwind, target, .. } => {
892+
(args, destination, unwind, target)
893+
}
894+
TerminatorKind::Drop { place, unwind, target, .. } => {
895+
// `drop_in_place` takes a `*mut`, so we need to take the address to pass it.
896+
let place_ty = place.ty(caller_body, tcx).ty;
897+
let place_addr_ty = Ty::new_mut_ptr(tcx, place_ty);
898+
let arg_place: Place<'tcx> =
899+
new_call_temp(caller_body, callsite, place_addr_ty, Some(target)).into();
900+
caller_body[callsite.block].statements.push(Statement {
901+
source_info: callsite.source_info,
902+
kind: StatementKind::Assign(Box::new((
903+
arg_place,
904+
Rvalue::RawPtr(RawPtrKind::Mut, place),
905+
))),
906+
});
907+
let arg = Spanned { span: terminator.source_info.span, node: Operand::Move(arg_place) };
908+
909+
// Create a dummy destination place as calls have one.
910+
let destination: Place<'tcx> =
911+
new_call_temp(caller_body, callsite, tcx.types.unit, Some(target)).into();
912+
(vec![arg].into_boxed_slice(), destination, unwind, Some(target))
913+
}
914+
_ => bug!("unexpected terminator kind {:?}", terminator.kind),
860915
};
861916

862917
let return_block = if let Some(block) = target {
@@ -1014,7 +1069,7 @@ fn inline_call<'tcx, I: Inliner<'tcx>>(
10141069
// the actually used items. By doing this we can entirely avoid visiting the callee!
10151070
// We need to reconstruct the `required_item` for the callee so that we can find and
10161071
// remove it.
1017-
let callee_item = MentionedItem::Fn(func.ty(caller_body, tcx));
1072+
let callee_item = MentionedItem::Fn(callsite.callee.ty(tcx, inliner.typing_env()));
10181073
let caller_mentioned_items = caller_body.mentioned_items.as_mut().unwrap();
10191074
if let Some(idx) = caller_mentioned_items.iter().position(|item| item.node == callee_item) {
10201075
// We found the callee, so remove it and add its items instead.

tests/mir-opt/c_unwind_terminate.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,14 @@ fn panic() {
1414
// EMIT_MIR c_unwind_terminate.test.AbortUnwindingCalls.after.mir
1515
extern "C" fn test() {
1616
// CHECK-LABEL: fn test(
17+
// CHECK: panic
18+
// CHECK-SAME: unwind: [[panic_unwind:bb.*]]]
1719
// CHECK: drop
18-
// CHECK-SAME: unwind: [[unwind:bb.*]]]
19-
// CHECK: [[unwind]] (cleanup)
20+
// CHECK-SAME: unwind: [[drop_unwind:bb.*]]]
21+
// CHECK: [[panic_unwind]] (cleanup)
22+
// CHECK-NEXT: drop
23+
// CHECK-SAME: terminate(cleanup)
24+
// CHECK: [[drop_unwind]] (cleanup)
2025
// CHECK-NEXT: terminate(abi)
2126
let _val = Noise;
2227
panic();

tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
let mut _0: ();
66
let _1: A;
77
let mut _2: std::boxed::Box<[bool]>;
8+
let mut _8: *mut A;
9+
let mut _9: ();
810
scope 1 {
911
debug a => _1;
1012
}
@@ -37,6 +39,8 @@
3739
}
3840
}
3941
}
42+
scope 14 (inlined drop_in_place::<A> - shim(Some(A))) {
43+
}
4044

4145
bb0: {
4246
StorageLive(_1);
@@ -60,10 +64,15 @@
6064
_1 = const A {{ foo: Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC2, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global) }};
6165
StorageDead(_2);
6266
_0 = const ();
63-
drop(_1) -> [return: bb1, unwind unreachable];
67+
StorageLive(_8);
68+
_8 = &raw mut _1;
69+
StorageLive(_9);
70+
drop(((*_8).0: std::boxed::Box<[bool]>)) -> [return: bb1, unwind unreachable];
6471
}
6572

6673
bb1: {
74+
StorageDead(_9);
75+
StorageDead(_8);
6776
StorageDead(_1);
6877
return;
6978
}

tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
let mut _0: ();
66
let _1: A;
77
let mut _2: std::boxed::Box<[bool]>;
8+
let mut _8: *mut A;
9+
let mut _9: ();
810
scope 1 {
911
debug a => _1;
1012
}
@@ -37,6 +39,8 @@
3739
}
3840
}
3941
}
42+
scope 14 (inlined drop_in_place::<A> - shim(Some(A))) {
43+
}
4044

4145
bb0: {
4246
StorageLive(_1);
@@ -60,16 +64,21 @@
6064
_1 = const A {{ foo: Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC2, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global) }};
6165
StorageDead(_2);
6266
_0 = const ();
63-
drop(_1) -> [return: bb1, unwind: bb2];
67+
StorageLive(_8);
68+
_8 = &raw mut _1;
69+
StorageLive(_9);
70+
drop(((*_8).0: std::boxed::Box<[bool]>)) -> [return: bb2, unwind: bb1];
6471
}
6572

66-
bb1: {
67-
StorageDead(_1);
68-
return;
73+
bb1 (cleanup): {
74+
resume;
6975
}
7076

71-
bb2 (cleanup): {
72-
resume;
77+
bb2: {
78+
StorageDead(_9);
79+
StorageDead(_8);
80+
StorageDead(_1);
81+
return;
7382
}
7483
}
7584

0 commit comments

Comments
 (0)