Skip to content

Commit e08aae3

Browse files
committed
Allow inlining all the drops
1 parent 17c8a4e commit e08aae3

File tree

69 files changed

+1618
-1103
lines changed

Some content is hidden

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

69 files changed

+1618
-1103
lines changed

compiler/rustc_mir_transform/src/inline.rs

Lines changed: 87 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}
1313
use rustc_middle::mir::visit::*;
1414
use rustc_middle::mir::*;
1515
use rustc_middle::ty::{
16-
self, Instance, InstanceKind, ParamEnv, Ty, TyCtxt, TypeFlags, TypeVisitableExt,
16+
self, GenericArg, Instance, InstanceKind, ParamEnv, Ty, TyCtxt, TypeFlags, TypeVisitableExt,
1717
};
1818
use rustc_session::config::{DebugInfo, OptLevel};
19-
use rustc_span::source_map::Spanned;
19+
use rustc_span::source_map::{dummy_spanned, Spanned};
2020
use rustc_span::sym;
2121
use rustc_target::abi::FieldIdx;
2222
use rustc_target::spec::abi::Abi;
@@ -75,6 +75,87 @@ impl<'tcx> MirPass<'tcx> for Inline {
7575
}
7676
}
7777

78+
/// Change all the `Drop(place)` terminators to `drop_in_place(&place)` calls.
79+
///
80+
/// This needs to be done before starting the inlining analysis or else the cycle
81+
/// detection logic will miss things. But it's a waste of time to do the work of
82+
/// adding locals unless we're going to try to inline things, so might as well
83+
/// leave the drop terminators alone in lower optimization levels.
84+
fn lower_drops_to_calls<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, param_env: ParamEnv<'tcx>) {
85+
// I wish this could just be `require_lang_item`, but there are so many
86+
// obnoxious `no_core` tests that I'd have to update to do that.
87+
let Some(drop_in_place_fn) = tcx.lang_items().drop_in_place_fn() else {
88+
error!("No `drop_in_place` function; not even trying to simplify drops!");
89+
return;
90+
};
91+
92+
// We need a unit local to store the "result" of the `drop_in_place` call
93+
let mut unit_local = None;
94+
95+
// Changing `Drop` to `Call` actually preserves the CFG because
96+
// it keeps the same `target` and the same `unwind` action.
97+
for block in body.basic_blocks.as_mut_preserves_cfg().iter_mut() {
98+
let terminator = block.terminator.as_mut().unwrap();
99+
let TerminatorKind::Drop { place: dropped_place, target, unwind, replace: _ } =
100+
terminator.kind
101+
else {
102+
continue;
103+
};
104+
105+
if block.is_cleanup {
106+
// We don't inline into cleanup blocks, so don't rewrite drops in them either.
107+
continue;
108+
}
109+
110+
let dropped_ty = dropped_place.ty(&body.local_decls, tcx).ty;
111+
if !dropped_ty.needs_drop(tcx, param_env) {
112+
// Leave it for RemoveUnneededDrops to remove, since that's
113+
// cheaper than doing the work to inline an empty shim.
114+
continue;
115+
}
116+
117+
if !tcx.consider_optimizing(|| format!("Drop -> drop_in_place on type {dropped_ty:?}")) {
118+
return;
119+
}
120+
121+
let dropped_operand = if let [PlaceElem::Deref] = **dropped_place.projection
122+
&& body.local_decls[dropped_place.local].ty.is_mutable_ptr()
123+
{
124+
Operand::Copy(Place::from(dropped_place.local))
125+
} else {
126+
let dropped_ty_ptr = Ty::new_mut_ptr(tcx, dropped_ty);
127+
let ptr_local =
128+
body.local_decls.push(LocalDecl::new(dropped_ty_ptr, terminator.source_info.span));
129+
block.statements.push(Statement {
130+
source_info: terminator.source_info,
131+
kind: StatementKind::Assign(Box::new((
132+
Place::from(ptr_local),
133+
Rvalue::AddressOf(Mutability::Mut, dropped_place),
134+
))),
135+
});
136+
Operand::Move(Place::from(ptr_local))
137+
};
138+
let unit_local = *unit_local.get_or_insert_with(|| {
139+
let unit_ty = tcx.types.unit;
140+
body.local_decls.push(LocalDecl::new(unit_ty, terminator.source_info.span))
141+
});
142+
terminator.kind = TerminatorKind::Call {
143+
func: Operand::function_handle(
144+
tcx,
145+
drop_in_place_fn,
146+
[GenericArg::from(dropped_ty)],
147+
terminator.source_info.span,
148+
),
149+
args: Box::new([dummy_spanned(dropped_operand)]),
150+
destination: Place::from(unit_local),
151+
target: Some(target),
152+
unwind,
153+
call_source: CallSource::Misc,
154+
fn_span: terminator.source_info.span,
155+
};
156+
}
157+
}
158+
78159
fn inline<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool {
79160
let def_id = body.source.def_id().expect_local();
80161

@@ -94,6 +175,7 @@ fn inline<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool {
94175

95176
let param_env = tcx.param_env_reveal_all_normalized(def_id);
96177
let codegen_fn_attrs = tcx.codegen_fn_attrs(def_id);
178+
lower_drops_to_calls(tcx, body, param_env);
97179

98180
let mut this = Inliner {
99181
tcx,
@@ -509,7 +591,9 @@ impl<'tcx> Inliner<'tcx> {
509591
return Err("Body is tainted");
510592
}
511593

512-
let mut threshold = if self.caller_is_inline_forwarder {
594+
let mut threshold = if self.caller_is_inline_forwarder
595+
|| matches!(callee_body.source.instance, InstanceKind::DropGlue(..))
596+
{
513597
self.tcx.sess.opts.unstable_opts.inline_mir_forwarder_threshold.unwrap_or(30)
514598
} else if cross_crate_inlinable {
515599
self.tcx.sess.opts.unstable_opts.inline_mir_hint_threshold.unwrap_or(100)

compiler/rustc_mir_transform/src/remove_unneeded_drops.rs

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
//! useful because (unlike MIR building) it runs after type checking, so it can make use of
55
//! `Reveal::All` to provide more precise type information.
66
7+
use rustc_hir::LangItem;
78
use rustc_middle::mir::*;
89
use rustc_middle::ty::TyCtxt;
910

@@ -21,18 +22,28 @@ impl<'tcx> MirPass<'tcx> for RemoveUnneededDrops {
2122

2223
for block in body.basic_blocks.as_mut() {
2324
let terminator = block.terminator_mut();
24-
if let TerminatorKind::Drop { place, target, .. } = terminator.kind {
25-
let ty = place.ty(&body.local_decls, tcx);
26-
if ty.ty.needs_drop(tcx, param_env) {
27-
continue;
25+
let (dropped_ty, target) = match terminator.kind {
26+
TerminatorKind::Drop { place, target, .. } => {
27+
(place.ty(&body.local_decls, tcx).ty, target)
2828
}
29-
if !tcx.consider_optimizing(|| format!("RemoveUnneededDrops {did:?} ")) {
30-
continue;
29+
TerminatorKind::Call { ref func, target, .. }
30+
if let Some((def_id, generics)) = func.const_fn_def()
31+
&& tcx.is_lang_item(def_id, LangItem::DropInPlace) =>
32+
{
33+
(generics.type_at(0), target.unwrap())
3134
}
32-
debug!("SUCCESS: replacing `drop` with goto({:?})", target);
33-
terminator.kind = TerminatorKind::Goto { target };
34-
should_simplify = true;
35+
_ => continue,
36+
};
37+
38+
if dropped_ty.needs_drop(tcx, param_env) {
39+
continue;
40+
}
41+
if !tcx.consider_optimizing(|| format!("RemoveUnneededDrops {did:?} ")) {
42+
continue;
3543
}
44+
debug!("SUCCESS: replacing `drop` with goto({:?})", target);
45+
terminator.kind = TerminatorKind::Goto { target };
46+
should_simplify = true;
3647
}
3748

3849
// if we applied optimizations, we potentially have some cfg to cleanup to

tests/codegen/drop-in-place-noalias.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
//@ compile-flags: -O -C no-prepopulate-passes
1+
//@ compile-flags: -O -C no-prepopulate-passes -Z inline-mir=no
22

33
// Tests that the compiler can apply `noalias` and other &mut attributes to `drop_in_place`.
44
// Note that non-Unpin types should not get `noalias`, matching &mut behavior.
55

6+
// Needs to not run MIR inlining or else everything inlines away.
7+
68
#![crate_type = "lib"]
79

810
use std::marker::PhantomPinned;

tests/codegen/drop.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
//@ needs-unwind - this test verifies the amount of drop calls when unwinding is used
2-
//@ compile-flags: -C no-prepopulate-passes
2+
//@ compile-flags: -C no-prepopulate-passes -Z inline-mir=no
3+
4+
// Needs to disable MIR inlining or else some of the calls are directly to
5+
// `<SomeUniqueName as Drop>::drop` (rather than via `drop_in_place`).
36

47
#![crate_type = "lib"]
58

tests/codegen/issues/issue-112509-slice-get-andthen-get.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33

44
// CHECK-LABEL: @write_u8_variant_a
55
// CHECK-NEXT: {{.*}}:
6-
// CHECK-NEXT: icmp ugt
76
// CHECK-NEXT: getelementptr
7+
// CHECK-NEXT: icmp ugt
88
// CHECK-NEXT: select i1 {{.+}} null
99
// CHECK-NEXT: insertvalue
1010
// CHECK-NEXT: insertvalue

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

Lines changed: 42 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,32 +5,34 @@
55
let mut _0: ();
66
let _1: A;
77
let mut _2: std::boxed::Box<[bool]>;
8+
let mut _3: *mut A;
9+
let mut _4: ();
810
scope 1 {
911
debug a => _1;
1012
}
1113
scope 2 (inlined <Box<[bool]> as Default>::default) {
12-
let _3: std::ptr::Unique<[bool]>;
13-
let mut _4: std::ptr::Unique<[bool; 0]>;
14+
let _5: std::ptr::Unique<[bool]>;
15+
let mut _6: std::ptr::Unique<[bool; 0]>;
1416
scope 3 {
1517
}
1618
scope 4 (inlined Unique::<[bool; 0]>::dangling) {
17-
let mut _5: std::ptr::NonNull<[bool; 0]>;
19+
let mut _7: std::ptr::NonNull<[bool; 0]>;
1820
scope 5 (inlined NonNull::<[bool; 0]>::dangling) {
19-
let _6: *mut [bool; 0];
21+
let _8: *mut [bool; 0];
2022
scope 6 {
2123
scope 10 (inlined NonNull::<[bool; 0]>::new_unchecked) {
22-
let mut _8: bool;
23-
let _9: ();
24-
let mut _10: *mut ();
25-
let mut _11: *const [bool; 0];
24+
let mut _10: bool;
25+
let _11: ();
26+
let mut _12: *mut ();
27+
let mut _13: *const [bool; 0];
2628
scope 11 (inlined core::ub_checks::check_language_ub) {
2729
scope 12 (inlined core::ub_checks::check_language_ub::runtime) {
2830
}
2931
}
3032
}
3133
}
3234
scope 7 (inlined dangling_mut::<[bool; 0]>) {
33-
let mut _7: usize;
35+
let mut _9: usize;
3436
scope 8 (inlined align_of::<[bool; 0]>) {
3537
}
3638
scope 9 (inlined without_provenance_mut::<[bool; 0]>) {
@@ -39,58 +41,61 @@
3941
}
4042
}
4143
}
44+
scope 13 (inlined std::ptr::drop_in_place::<A> - shim(Some(A))) {
45+
}
4246

4347
bb0: {
4448
StorageLive(_1);
4549
StorageLive(_2);
46-
StorageLive(_3);
47-
StorageLive(_9);
48-
StorageLive(_4);
4950
StorageLive(_5);
51+
StorageLive(_11);
5052
StorageLive(_6);
5153
StorageLive(_7);
52-
_7 = const 1_usize;
53-
_6 = const {0x1 as *mut [bool; 0]};
54-
StorageDead(_7);
55-
StorageLive(_11);
5654
StorageLive(_8);
57-
_8 = UbChecks();
58-
switchInt(move _8) -> [0: bb4, otherwise: bb2];
55+
StorageLive(_9);
56+
_9 = const 1_usize;
57+
_8 = const {0x1 as *mut [bool; 0]};
58+
StorageDead(_9);
59+
StorageLive(_13);
60+
StorageLive(_10);
61+
_10 = UbChecks();
62+
switchInt(move _10) -> [0: bb3, otherwise: bb1];
5963
}
6064

6165
bb1: {
62-
StorageDead(_1);
63-
return;
66+
StorageLive(_12);
67+
_12 = const {0x1 as *mut ()};
68+
_11 = NonNull::<T>::new_unchecked::precondition_check(const {0x1 as *mut ()}) -> [return: bb2, unwind unreachable];
6469
}
6570

6671
bb2: {
67-
StorageLive(_10);
68-
_10 = const {0x1 as *mut ()};
69-
_9 = NonNull::<T>::new_unchecked::precondition_check(const {0x1 as *mut ()}) -> [return: bb3, unwind unreachable];
72+
StorageDead(_12);
73+
goto -> bb3;
7074
}
7175

7276
bb3: {
7377
StorageDead(_10);
74-
goto -> bb4;
75-
}
76-
77-
bb4: {
78+
_13 = const {0x1 as *const [bool; 0]};
79+
_7 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }};
80+
StorageDead(_13);
7881
StorageDead(_8);
79-
_11 = const {0x1 as *const [bool; 0]};
80-
_5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }};
81-
StorageDead(_11);
82+
_6 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }};
83+
StorageDead(_7);
84+
_5 = const Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }};
8285
StorageDead(_6);
83-
_4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }};
84-
StorageDead(_5);
85-
_3 = const Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }};
86-
StorageDead(_4);
8786
_2 = const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global);
88-
StorageDead(_9);
89-
StorageDead(_3);
87+
StorageDead(_11);
88+
StorageDead(_5);
9089
_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) }};
9190
StorageDead(_2);
9291
_0 = const ();
93-
drop(_1) -> [return: bb1, unwind unreachable];
92+
_3 = &raw mut _1;
93+
drop(((*_3).0: std::boxed::Box<[bool]>)) -> [return: bb4, unwind unreachable];
94+
}
95+
96+
bb4: {
97+
StorageDead(_1);
98+
return;
9499
}
95100
}
96101

0 commit comments

Comments
 (0)