@@ -7,16 +7,16 @@ use rustc_attr::InlineAttr;
77use rustc_hir:: def:: DefKind ;
88use rustc_hir:: def_id:: DefId ;
99use rustc_index:: bit_set:: BitSet ;
10- use rustc_index:: Idx ;
10+ use rustc_index:: { Idx , IndexVec } ;
1111use rustc_middle:: bug;
1212use rustc_middle:: middle:: codegen_fn_attrs:: { CodegenFnAttrFlags , CodegenFnAttrs } ;
1313use rustc_middle:: mir:: visit:: * ;
1414use rustc_middle:: mir:: * ;
1515use rustc_middle:: ty:: {
16- self , Instance , InstanceKind , ParamEnv , Ty , TyCtxt , TypeFlags , TypeVisitableExt ,
16+ self , GenericArg , Instance , InstanceKind , ParamEnv , Ty , TyCtxt , TypeFlags , TypeVisitableExt ,
1717} ;
1818use rustc_session:: config:: { DebugInfo , OptLevel } ;
19- use rustc_span:: source_map:: Spanned ;
19+ use rustc_span:: source_map:: { dummy_spanned , Spanned } ;
2020use rustc_span:: sym;
2121use rustc_target:: abi:: FieldIdx ;
2222use rustc_target:: spec:: abi:: Abi ;
@@ -94,6 +94,10 @@ fn inline<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool {
9494
9595 let param_env = tcx. param_env_reveal_all_normalized ( def_id) ;
9696 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+ } ;
97101
98102 let mut this = Inliner {
99103 tcx,
@@ -105,6 +109,8 @@ fn inline<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool {
105109 codegen_fn_attrs. inline,
106110 InlineAttr :: Hint | InlineAttr :: Always
107111 ) && body_is_forwarder ( body) ,
112+ drop_in_place_fn,
113+ unit_local : None ,
108114 } ;
109115 let blocks = START_BLOCK ..body. basic_blocks . next_index ( ) ;
110116 this. process_blocks ( body, blocks) ;
@@ -127,9 +133,78 @@ struct Inliner<'tcx> {
127133 /// Indicates that the caller is #[inline] and just calls another function,
128134 /// and thus we can inline less into it as it'll be inlined itself.
129135 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 > ,
130141}
131142
132143impl < ' 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+
133208 fn process_blocks ( & mut self , caller_body : & mut Body < ' tcx > , blocks : Range < BasicBlock > ) {
134209 // How many callsites in this body are we allowed to inline? We need to limit this in order
135210 // to prevent super-linear growth in MIR size
@@ -140,12 +215,18 @@ impl<'tcx> Inliner<'tcx> {
140215 } ;
141216 let mut inlined_count = 0 ;
142217 for bb in blocks {
143- let bb_data = & caller_body[ bb] ;
144- if bb_data. is_cleanup {
218+ if caller_body[ bb] . is_cleanup {
145219 continue ;
146220 }
147221
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 {
149230 continue ;
150231 } ;
151232
@@ -509,7 +590,9 @@ impl<'tcx> Inliner<'tcx> {
509590 return Err ( "Body is tainted" ) ;
510591 }
511592
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+ {
513596 self . tcx . sess . opts . unstable_opts . inline_mir_forwarder_threshold . unwrap_or ( 30 )
514597 } else if cross_crate_inlinable {
515598 self . tcx . sess . opts . unstable_opts . inline_mir_hint_threshold . unwrap_or ( 100 )
0 commit comments