@@ -6,6 +6,7 @@ use std::ops::{Range, RangeFrom};
66
77use rustc_abi:: { ExternAbi , FieldIdx } ;
88use rustc_attr_data_structures:: { InlineAttr , OptimizeAttr } ;
9+ use rustc_hir:: LangItem ;
910use rustc_hir:: def:: DefKind ;
1011use rustc_hir:: def_id:: DefId ;
1112use rustc_index:: Idx ;
@@ -116,6 +117,9 @@ trait Inliner<'tcx> {
116117 /// Has the caller body been changed?
117118 fn changed ( self ) -> bool ;
118119
120+ /// Whether to also attempt to inline `Drop` terminators (not just `Call`s)
121+ fn consider_drops ( & self ) -> bool ;
122+
119123 /// Should inlining happen for a given callee?
120124 fn should_inline_for_callee ( & self , def_id : DefId ) -> bool ;
121125
@@ -187,6 +191,10 @@ impl<'tcx> Inliner<'tcx> for ForceInliner<'tcx> {
187191 self . changed
188192 }
189193
194+ fn consider_drops ( & self ) -> bool {
195+ false
196+ }
197+
190198 fn should_inline_for_callee ( & self , def_id : DefId ) -> bool {
191199 ForceInline :: should_run_pass_for_callee ( self . tcx ( ) , def_id)
192200 }
@@ -272,6 +280,7 @@ struct NormalInliner<'tcx> {
272280 typing_env : ty:: TypingEnv < ' tcx > ,
273281 /// `DefId` of caller.
274282 def_id : DefId ,
283+ caller_is_coroutine : bool ,
275284 /// Stack of inlined instances.
276285 /// We only check the `DefId` and not the args because we want to
277286 /// avoid inlining cases of polymorphic recursion.
@@ -304,6 +313,7 @@ impl<'tcx> Inliner<'tcx> for NormalInliner<'tcx> {
304313 tcx,
305314 typing_env,
306315 def_id,
316+ caller_is_coroutine : tcx. is_coroutine ( def_id) ,
307317 history : Vec :: new ( ) ,
308318 top_down_counter : 0 ,
309319 changed : false ,
@@ -334,6 +344,10 @@ impl<'tcx> Inliner<'tcx> for NormalInliner<'tcx> {
334344 self . changed
335345 }
336346
347+ fn consider_drops ( & self ) -> bool {
348+ !self . caller_is_coroutine
349+ }
350+
337351 fn should_inline_for_callee ( & self , _: DefId ) -> bool {
338352 true
339353 }
@@ -549,50 +563,77 @@ fn resolve_callsite<'tcx, I: Inliner<'tcx>>(
549563 bb_data : & BasicBlockData < ' tcx > ,
550564) -> Option < CallSite < ' tcx > > {
551565 let tcx = inliner. tcx ( ) ;
552- // Only consider direct calls to functions
553566 let terminator = bb_data. terminator ( ) ;
554567
555568 // 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- }
569+ let ( def_id, args, fn_span) = match & terminator. kind {
570+ TerminatorKind :: Call { func, fn_span, .. } => {
571+ let func_ty = func. ty ( caller_body, tcx) ;
572+ if let ty:: FnDef ( def_id, args) = * func_ty. kind ( ) {
573+ if !inliner. should_inline_for_callee ( def_id) {
574+ debug ! ( "not enabled" ) ;
575+ return None ;
576+ }
563577
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 ( ) ?;
578+ // Allow RemoveUnneededDrops to handle these, rather than inlining,
579+ // since it doesn't add the extra locals nor the metadata.
580+ if inliner. consider_drops ( )
581+ && tcx. is_lang_item ( def_id, LangItem :: DropInPlace )
582+ && let drop_ty = args. type_at ( 0 )
583+ && !drop_ty. needs_drop ( tcx, inliner. typing_env ( ) )
584+ {
585+ return None ;
586+ }
568587
569- if let InstanceKind :: Virtual ( ..) | InstanceKind :: Intrinsic ( _) = callee. def {
588+ ( def_id, args, * fn_span)
589+ } else {
570590 return None ;
571591 }
572-
573- if inliner. history ( ) . contains ( & callee. def_id ( ) ) {
592+ }
593+ TerminatorKind :: Drop { place, .. } if inliner. consider_drops ( ) => {
594+ let drop_ty = place. ty ( & caller_body. local_decls , tcx) . ty ;
595+ if !drop_ty. needs_drop ( tcx, inliner. typing_env ( ) )
596+ // FIXME: Box's shim looks like it inlines fine, but it's quite
597+ // a bit of MIR right now, so is currently skipped to avoid churn.
598+ || drop_ty. is_box ( )
599+ {
574600 return None ;
575601 }
576602
577- let fn_sig = tcx. fn_sig ( def_id) . instantiate ( tcx, args) ;
603+ let drop_def_id =
604+ tcx. require_lang_item ( LangItem :: DropInPlace , terminator. source_info . span ) ;
605+ let args = tcx. mk_args ( & [ drop_ty. into ( ) ] ) ;
606+ ( drop_def_id, args, rustc_span:: DUMMY_SP )
607+ }
608+ _ => return None ,
609+ } ;
578610
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- }
611+ // To resolve an instance its args have to be fully normalized.
612+ let args = tcx. try_normalize_erasing_regions ( inliner. typing_env ( ) , args) . ok ( ) ?;
613+ let callee = Instance :: try_resolve ( tcx, inliner. typing_env ( ) , def_id, args) . ok ( ) . flatten ( ) ?;
588614
589- let source_info = SourceInfo { span : fn_span, ..terminator. source_info } ;
615+ if let InstanceKind :: Virtual ( ..) | InstanceKind :: Intrinsic ( _) = callee. def {
616+ return None ;
617+ }
590618
591- return Some ( CallSite { callee, fn_sig, block : bb, source_info } ) ;
592- }
619+ if inliner. history ( ) . contains ( & callee. def_id ( ) ) {
620+ return None ;
621+ }
622+
623+ let fn_sig = tcx. fn_sig ( def_id) . instantiate ( tcx, args) ;
624+
625+ // Additionally, check that the body that we're inlining actually agrees
626+ // with the ABI of the trait that the item comes from.
627+ if let InstanceKind :: Item ( instance_def_id) = callee. def
628+ && tcx. def_kind ( instance_def_id) == DefKind :: AssocFn
629+ && let instance_fn_sig = tcx. fn_sig ( instance_def_id) . skip_binder ( )
630+ && instance_fn_sig. abi ( ) != fn_sig. abi ( )
631+ {
632+ return None ;
593633 }
594634
595- None
635+ let source_info = SourceInfo { span : fn_span, ..terminator. source_info } ;
636+ Some ( CallSite { callee, fn_sig, block : bb, source_info } )
596637}
597638
598639/// Attempts to inline a callsite into the caller body. When successful returns basic blocks
@@ -604,14 +645,72 @@ fn try_inlining<'tcx, I: Inliner<'tcx>>(
604645 callsite : & CallSite < ' tcx > ,
605646) -> Result < std:: ops:: Range < BasicBlock > , & ' static str > {
606647 let tcx = inliner. tcx ( ) ;
648+
607649 check_mir_is_available ( inliner, caller_body, callsite. callee ) ?;
608650
609651 let callee_attrs = tcx. codegen_fn_attrs ( callsite. callee . def_id ( ) ) ;
610652 check_inline:: is_inline_valid_on_fn ( tcx, callsite. callee . def_id ( ) ) ?;
611653 check_codegen_attributes ( inliner, callsite, callee_attrs) ?;
612654 inliner. check_codegen_attributes_extra ( callee_attrs) ?;
613655
614- let terminator = caller_body[ callsite. block ] . terminator . as_ref ( ) . unwrap ( ) ;
656+ let callee_body = try_instance_mir ( tcx, callsite. callee . def ) ?;
657+ check_inline:: is_inline_valid_on_body ( tcx, callee_body) ?;
658+ inliner. check_callee_mir_body ( callsite, callee_body, callee_attrs) ?;
659+
660+ if inliner. consider_drops ( ) {
661+ let block_mut = & mut caller_body. basic_blocks . as_mut ( ) [ callsite. block ] ;
662+ let terminator = block_mut. terminator . as_mut ( ) . unwrap ( ) ;
663+ if let TerminatorKind :: Drop {
664+ place,
665+ target,
666+ unwind,
667+ replace : _,
668+ drop : None ,
669+ async_fut : None ,
670+ } = terminator. kind
671+ {
672+ // Rather than updating everything after here to also handle `Drop`,
673+ // just replace the terminator with a `Call`, since we'll need things
674+ // like the local for the argument anyway.
675+ let source_info = terminator. source_info ;
676+ let drop_ty = place. ty ( & caller_body. local_decls , tcx) . ty ;
677+ if !drop_ty. needs_drop ( tcx, inliner. typing_env ( ) ) {
678+ //return None;
679+ bug ! ( "Why does it think {drop_ty:?} needs a drop *now*?" ) ;
680+ }
681+
682+ let drop_ty_mut = Ty :: new_mut_ptr ( tcx, drop_ty) ;
683+ let arg = caller_body. local_decls . push ( LocalDecl :: new ( drop_ty_mut, source_info. span ) ) ;
684+ block_mut. statements . push ( Statement :: new (
685+ source_info,
686+ StatementKind :: Assign ( Box :: new ( (
687+ Place :: from ( arg) ,
688+ Rvalue :: RawPtr ( RawPtrKind :: Mut , place) ,
689+ ) ) ) ,
690+ ) ) ;
691+ let unit_local =
692+ caller_body. local_decls . push ( LocalDecl :: new ( tcx. types . unit , source_info. span ) ) ;
693+ terminator. kind = TerminatorKind :: Call {
694+ func : Operand :: function_handle (
695+ tcx,
696+ callsite. callee . def_id ( ) ,
697+ [ drop_ty. into ( ) ] ,
698+ source_info. span ,
699+ ) ,
700+ args : Box :: new ( [ Spanned {
701+ node : Operand :: Move ( Place :: from ( arg) ) ,
702+ span : source_info. span ,
703+ } ] ) ,
704+ destination : Place :: from ( unit_local) ,
705+ target : Some ( target) ,
706+ unwind,
707+ call_source : CallSource :: Misc ,
708+ fn_span : source_info. span ,
709+ } ;
710+ }
711+ }
712+
713+ let terminator = caller_body[ callsite. block ] . terminator ( ) ;
615714 let TerminatorKind :: Call { args, destination, .. } = & terminator. kind else { bug ! ( ) } ;
616715 let destination_ty = destination. ty ( & caller_body. local_decls , tcx) . ty ;
617716 for arg in args {
@@ -622,10 +721,6 @@ fn try_inlining<'tcx, I: Inliner<'tcx>>(
622721 }
623722 }
624723
625- let callee_body = try_instance_mir ( tcx, callsite. callee . def ) ?;
626- check_inline:: is_inline_valid_on_body ( tcx, callee_body) ?;
627- inliner. check_callee_mir_body ( callsite, callee_body, callee_attrs) ?;
628-
629724 let Ok ( callee_body) = callsite. callee . try_instantiate_mir_and_normalize_erasing_regions (
630725 tcx,
631726 inliner. typing_env ( ) ,
0 commit comments