@@ -7,16 +7,16 @@ use rustc_attr::InlineAttr;
7
7
use rustc_hir:: def:: DefKind ;
8
8
use rustc_hir:: def_id:: DefId ;
9
9
use rustc_index:: bit_set:: BitSet ;
10
- use rustc_index:: Idx ;
10
+ use rustc_index:: { Idx , IndexVec } ;
11
11
use rustc_middle:: bug;
12
12
use rustc_middle:: middle:: codegen_fn_attrs:: { CodegenFnAttrFlags , CodegenFnAttrs } ;
13
13
use rustc_middle:: mir:: visit:: * ;
14
14
use rustc_middle:: mir:: * ;
15
15
use rustc_middle:: ty:: {
16
- self , Instance , InstanceKind , ParamEnv , Ty , TyCtxt , TypeFlags , TypeVisitableExt ,
16
+ self , GenericArg , Instance , InstanceKind , ParamEnv , Ty , TyCtxt , TypeFlags , TypeVisitableExt ,
17
17
} ;
18
18
use rustc_session:: config:: { DebugInfo , OptLevel } ;
19
- use rustc_span:: source_map:: Spanned ;
19
+ use rustc_span:: source_map:: { dummy_spanned , Spanned } ;
20
20
use rustc_span:: sym;
21
21
use rustc_target:: abi:: FieldIdx ;
22
22
use rustc_target:: spec:: abi:: Abi ;
@@ -94,6 +94,10 @@ fn inline<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool {
94
94
95
95
let param_env = tcx. param_env_reveal_all_normalized ( def_id) ;
96
96
let codegen_fn_attrs = tcx. codegen_fn_attrs ( def_id) ;
97
+ let Some ( drop_in_place_fn) = tcx. lang_items ( ) . drop_in_place_fn ( ) else {
98
+ error ! ( "No `drop_in_place` function; not even trying to inline!" ) ;
99
+ return false ;
100
+ } ;
97
101
98
102
let mut this = Inliner {
99
103
tcx,
@@ -105,6 +109,8 @@ fn inline<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool {
105
109
codegen_fn_attrs. inline,
106
110
InlineAttr :: Hint | InlineAttr :: Always
107
111
) && body_is_forwarder ( body) ,
112
+ drop_in_place_fn,
113
+ unit_local : None ,
108
114
} ;
109
115
let blocks = START_BLOCK ..body. basic_blocks . next_index ( ) ;
110
116
this. process_blocks ( body, blocks) ;
@@ -127,9 +133,78 @@ struct Inliner<'tcx> {
127
133
/// Indicates that the caller is #[inline] and just calls another function,
128
134
/// and thus we can inline less into it as it'll be inlined itself.
129
135
caller_is_inline_forwarder : bool ,
136
+ /// The compiler-magic function that actually drops something.
137
+ drop_in_place_fn : DefId ,
138
+ /// When lowering `Drop(place)` to `drop_in_place(&place)`, we need a unit
139
+ /// local to use as the target of the call, but don't want multiple.
140
+ unit_local : Option < Local > ,
130
141
}
131
142
132
143
impl < ' tcx > Inliner < ' tcx > {
144
+ fn lower_drop_to_call (
145
+ & mut self ,
146
+ block : & mut BasicBlockData < ' tcx > ,
147
+ local_decls : & mut IndexVec < Local , LocalDecl < ' tcx > > ,
148
+ ) {
149
+ let terminator = block. terminator . as_mut ( ) . unwrap ( ) ;
150
+ let TerminatorKind :: Drop { place : dropped_place, target, unwind, replace : _ } =
151
+ terminator. kind
152
+ else {
153
+ return ;
154
+ } ;
155
+
156
+ let dropped_ty = dropped_place. ty ( local_decls, self . tcx ) . ty ;
157
+ if matches ! ( dropped_ty. kind( ) , ty:: Alias ( ..) | ty:: Param ( ..) ) {
158
+ // Not worth the extra locals, since we'll probably not be able to
159
+ // get MIR for it. If something non-generic happens to inline this
160
+ // block later, we'll have the opportunity to handle it then.
161
+ return ;
162
+ }
163
+
164
+ if !dropped_ty. needs_drop ( self . tcx , self . param_env ) {
165
+ // Leave it for other passes to remove, which is cheaper than
166
+ // doing the work to inline an empty shim.
167
+ return ;
168
+ }
169
+
170
+ self . changed = true ;
171
+
172
+ let dropped_operand = if let [ PlaceElem :: Deref ] = * * dropped_place. projection
173
+ && local_decls[ dropped_place. local ] . ty . is_mutable_ptr ( )
174
+ {
175
+ Operand :: Copy ( Place :: from ( dropped_place. local ) )
176
+ } else {
177
+ let dropped_ty_ptr = Ty :: new_mut_ptr ( self . tcx , dropped_ty) ;
178
+ let ptr_local =
179
+ local_decls. push ( LocalDecl :: new ( dropped_ty_ptr, terminator. source_info . span ) ) ;
180
+ block. statements . push ( Statement {
181
+ source_info : terminator. source_info ,
182
+ kind : StatementKind :: Assign ( Box :: new ( (
183
+ Place :: from ( ptr_local) ,
184
+ Rvalue :: AddressOf ( Mutability :: Mut , dropped_place) ,
185
+ ) ) ) ,
186
+ } ) ;
187
+ Operand :: Move ( Place :: from ( ptr_local) )
188
+ } ;
189
+ let unit_local = * self . unit_local . get_or_insert_with ( || {
190
+ local_decls. push ( LocalDecl :: new ( self . tcx . types . unit , terminator. source_info . span ) )
191
+ } ) ;
192
+ terminator. kind = TerminatorKind :: Call {
193
+ func : Operand :: function_handle (
194
+ self . tcx ,
195
+ self . drop_in_place_fn ,
196
+ [ GenericArg :: from ( dropped_ty) ] ,
197
+ terminator. source_info . span ,
198
+ ) ,
199
+ args : Box :: new ( [ dummy_spanned ( dropped_operand) ] ) ,
200
+ destination : Place :: from ( unit_local) ,
201
+ target : Some ( target) ,
202
+ unwind,
203
+ call_source : CallSource :: Misc ,
204
+ fn_span : terminator. source_info . span ,
205
+ } ;
206
+ }
207
+
133
208
fn process_blocks ( & mut self , caller_body : & mut Body < ' tcx > , blocks : Range < BasicBlock > ) {
134
209
// How many callsites in this body are we allowed to inline? We need to limit this in order
135
210
// to prevent super-linear growth in MIR size
@@ -140,12 +215,18 @@ impl<'tcx> Inliner<'tcx> {
140
215
} ;
141
216
let mut inlined_count = 0 ;
142
217
for bb in blocks {
143
- let bb_data = & caller_body[ bb] ;
144
- if bb_data. is_cleanup {
218
+ if caller_body[ bb] . is_cleanup {
145
219
continue ;
146
220
}
147
221
148
- let Some ( callsite) = self . resolve_callsite ( caller_body, bb, bb_data) else {
222
+ // Changing `Drop` to `Call` actually preserves the CFG because it
223
+ // keeps the same `target` and `unwind` action.
224
+ self . lower_drop_to_call (
225
+ & mut caller_body. basic_blocks . as_mut_preserves_cfg ( ) [ bb] ,
226
+ & mut caller_body. local_decls ,
227
+ ) ;
228
+
229
+ let Some ( callsite) = self . resolve_callsite ( caller_body, bb, & caller_body[ bb] ) else {
149
230
continue ;
150
231
} ;
151
232
@@ -509,7 +590,9 @@ impl<'tcx> Inliner<'tcx> {
509
590
return Err ( "Body is tainted" ) ;
510
591
}
511
592
512
- let mut threshold = if self . caller_is_inline_forwarder {
593
+ let mut threshold = if self . caller_is_inline_forwarder
594
+ || matches ! ( callee_body. source. instance, InstanceKind :: DropGlue ( ..) )
595
+ {
513
596
self . tcx . sess . opts . unstable_opts . inline_mir_forwarder_threshold . unwrap_or ( 30 )
514
597
} else if cross_crate_inlinable {
515
598
self . tcx . sess . opts . unstable_opts . inline_mir_hint_threshold . unwrap_or ( 100 )
0 commit comments