@@ -5,6 +5,7 @@ use std::iter;
55use std:: ops:: { Range , RangeFrom } ;
66
77use rustc_abi:: { ExternAbi , FieldIdx } ;
8+ use rustc_hir:: LangItem ;
89use rustc_hir:: attrs:: { InlineAttr , OptimizeAttr } ;
910use rustc_hir:: def:: DefKind ;
1011use rustc_hir:: def_id:: DefId ;
@@ -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 }
@@ -542,57 +556,133 @@ fn process_blocks<'tcx, I: Inliner<'tcx>>(
542556 }
543557}
544558
559+ /// Returns a value indicating whether it's worth trying to inline a `drop` for `ty`.
560+ ///
561+ /// We only want to bother inlining things that have a change to need to do something.
562+ /// The `RemoveUnneededDrops` pass will handle things that obviously don't need
563+ /// dropping, and will do it more efficiently since it doesn't need to add inlining
564+ /// metadata, defensively add new blocks, etc.
565+ ///
566+ /// But this isn't the same as `needs_drop` because we want the opposite fallback:
567+ /// while `needs_drop` is true for a (non-Copy) type parameter, here we don't
568+ /// want to attempt inlining its drop because that'll never work.
569+ fn should_attempt_inline_drop_for_type < ' tcx > (
570+ tcx : TyCtxt < ' tcx > ,
571+ typing_env : ty:: TypingEnv < ' tcx > ,
572+ ty : Ty < ' tcx > ,
573+ ) -> bool {
574+ match ty. kind ( ) {
575+ ty:: Tuple ( elems) if elems. is_empty ( ) => false ,
576+
577+ // Even if these might have drops later, we can't inline them now.
578+ ty:: Param ( ..) | ty:: Alias ( ..) | ty:: Dynamic ( ..) | ty:: Foreign ( ..) => false ,
579+
580+ ty:: Array ( ..)
581+ | ty:: Adt ( ..)
582+ | ty:: Slice ( ..)
583+ | ty:: Tuple ( ..)
584+ | ty:: Closure ( ..)
585+ | ty:: CoroutineClosure ( ..)
586+ | ty:: Coroutine ( ..)
587+ | ty:: CoroutineWitness ( ..) => ty. needs_drop ( tcx, typing_env) ,
588+
589+ // Primitives we obviously don't need to inline a drop method
590+ ty:: Error ( ..)
591+ | ty:: Bool
592+ | ty:: Int ( ..)
593+ | ty:: Uint ( ..)
594+ | ty:: Float ( ..)
595+ | ty:: Never
596+ | ty:: FnDef ( ..)
597+ | ty:: FnPtr ( ..)
598+ | ty:: Char
599+ | ty:: RawPtr ( ..)
600+ | ty:: Ref ( ..)
601+ | ty:: Str => false ,
602+
603+ // FIXME: Unsure what to do with this, but not attempting inlining is safe
604+ ty:: Pat ( ..) | ty:: UnsafeBinder ( ..) => false ,
605+
606+ ty:: Infer ( ..) | ty:: Placeholder ( ..) | ty:: Bound ( ..) => {
607+ bug ! ( "weird type while inlining: {ty:?}" )
608+ }
609+ }
610+ }
611+
545612fn resolve_callsite < ' tcx , I : Inliner < ' tcx > > (
546613 inliner : & I ,
547614 caller_body : & Body < ' tcx > ,
548615 bb : BasicBlock ,
549616 bb_data : & BasicBlockData < ' tcx > ,
550617) -> Option < CallSite < ' tcx > > {
551618 let tcx = inliner. tcx ( ) ;
552- // Only consider direct calls to functions
553619 let terminator = bb_data. terminator ( ) ;
554620
555621 // 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- }
622+ let ( def_id, args, fn_span) = match & terminator. kind {
623+ TerminatorKind :: Call { func, fn_span, .. } => {
624+ let func_ty = func. ty ( caller_body, tcx) ;
625+ if let ty:: FnDef ( def_id, args) = * func_ty. kind ( ) {
626+ if !inliner. should_inline_for_callee ( def_id) {
627+ debug ! ( "not enabled" ) ;
628+ return None ;
629+ }
563630
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 ( ) ?;
631+ // Allow RemoveUnneededDrops to handle these, rather than inlining,
632+ // since it doesn't add the extra locals nor the metadata.
633+ if inliner. consider_drops ( )
634+ && tcx. is_lang_item ( def_id, LangItem :: DropInPlace )
635+ && let drop_ty = args. type_at ( 0 )
636+ && !should_attempt_inline_drop_for_type ( tcx, inliner. typing_env ( ) , drop_ty)
637+ {
638+ return None ;
639+ }
568640
569- if let InstanceKind :: Virtual ( ..) | InstanceKind :: Intrinsic ( _) = callee. def {
641+ ( def_id, args, * fn_span)
642+ } else {
570643 return None ;
571644 }
572-
573- if inliner. history ( ) . contains ( & callee. def_id ( ) ) {
645+ }
646+ TerminatorKind :: Drop { place, .. } if inliner. consider_drops ( ) => {
647+ let drop_ty = place. ty ( & caller_body. local_decls , tcx) . ty ;
648+ if !should_attempt_inline_drop_for_type ( tcx, inliner. typing_env ( ) , drop_ty) {
574649 return None ;
575650 }
576651
577- let fn_sig = tcx. fn_sig ( def_id) . instantiate ( tcx, args) ;
652+ let drop_def_id =
653+ tcx. require_lang_item ( LangItem :: DropInPlace , terminator. source_info . span ) ;
654+ let args = tcx. mk_args ( & [ drop_ty. into ( ) ] ) ;
655+ ( drop_def_id, args, rustc_span:: DUMMY_SP )
656+ }
657+ _ => return None ,
658+ } ;
578659
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- }
660+ // To resolve an instance its args have to be fully normalized.
661+ let args = tcx. try_normalize_erasing_regions ( inliner. typing_env ( ) , args) . ok ( ) ?;
662+ let callee = Instance :: try_resolve ( tcx, inliner. typing_env ( ) , def_id, args) . ok ( ) . flatten ( ) ?;
663+
664+ if let InstanceKind :: Virtual ( ..) | InstanceKind :: Intrinsic ( _) = callee. def {
665+ return None ;
666+ }
588667
589- let source_info = SourceInfo { span : fn_span, ..terminator. source_info } ;
668+ if inliner. history ( ) . contains ( & callee. def_id ( ) ) {
669+ return None ;
670+ }
590671
591- return Some ( CallSite { callee, fn_sig, block : bb, source_info } ) ;
592- }
672+ let fn_sig = tcx. fn_sig ( def_id) . instantiate ( tcx, args) ;
673+
674+ // Additionally, check that the body that we're inlining actually agrees
675+ // with the ABI of the trait that the item comes from.
676+ if let InstanceKind :: Item ( instance_def_id) = callee. def
677+ && tcx. def_kind ( instance_def_id) == DefKind :: AssocFn
678+ && let instance_fn_sig = tcx. fn_sig ( instance_def_id) . skip_binder ( )
679+ && instance_fn_sig. abi ( ) != fn_sig. abi ( )
680+ {
681+ return None ;
593682 }
594683
595- None
684+ let source_info = SourceInfo { span : fn_span, ..terminator. source_info } ;
685+ Some ( CallSite { callee, fn_sig, block : bb, source_info } )
596686}
597687
598688/// Attempts to inline a callsite into the caller body. When successful returns basic blocks
@@ -604,23 +694,33 @@ fn try_inlining<'tcx, I: Inliner<'tcx>>(
604694 callsite : & CallSite < ' tcx > ,
605695) -> Result < std:: ops:: Range < BasicBlock > , & ' static str > {
606696 let tcx = inliner. tcx ( ) ;
697+
607698 check_mir_is_available ( inliner, caller_body, callsite. callee ) ?;
608699
609700 let callee_attrs = tcx. codegen_fn_attrs ( callsite. callee . def_id ( ) ) ;
610701 check_inline:: is_inline_valid_on_fn ( tcx, callsite. callee . def_id ( ) ) ?;
611702 check_codegen_attributes ( inliner, callsite, callee_attrs) ?;
612703 inliner. check_codegen_attributes_extra ( callee_attrs) ?;
613704
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" ) ;
705+ let ( args, destination_ty) = match & caller_body[ callsite. block ] . terminator ( ) . kind {
706+ TerminatorKind :: Call { args, destination, .. } => {
707+ for arg in args {
708+ if !arg. node . ty ( & caller_body. local_decls , tcx) . is_sized ( tcx, inliner. typing_env ( ) ) {
709+ // We do not allow inlining functions with unsized params. Inlining these functions
710+ // could create unsized locals, which are unsound and being phased out.
711+ return Err ( "call has unsized argument" ) ;
712+ }
713+ }
714+ ( & * * args, destination. ty ( & caller_body. local_decls , tcx) . ty )
622715 }
623- }
716+ TerminatorKind :: Drop { .. } => {
717+ // We cheat a bit here. Obviously there *is* an argument to the
718+ // `drop_in_place`, but all the checks that look at it are ok to skip
719+ // since we're generating them with always the correct type.
720+ ( & [ ] [ ..] , tcx. types . unit )
721+ }
722+ _ => bug ! ( ) ,
723+ } ;
624724
625725 let callee_body = try_instance_mir ( tcx, callsite. callee . def ) ?;
626726 check_inline:: is_inline_valid_on_body ( tcx, callee_body) ?;
@@ -691,6 +791,59 @@ fn try_inlining<'tcx, I: Inliner<'tcx>>(
691791 }
692792 }
693793
794+ if inliner. consider_drops ( ) {
795+ let block_mut = & mut caller_body. basic_blocks . as_mut ( ) [ callsite. block ] ;
796+ let terminator = block_mut. terminator . as_mut ( ) . unwrap ( ) ;
797+ if let TerminatorKind :: Drop {
798+ place,
799+ target,
800+ unwind,
801+ replace : _,
802+ drop : None ,
803+ async_fut : None ,
804+ } = terminator. kind
805+ {
806+ // Rather than updating everything after here to also handle `Drop`,
807+ // just replace the terminator with a `Call`, since we'll need things
808+ // like the local for the argument anyway.
809+ let source_info = terminator. source_info ;
810+ let drop_ty = place. ty ( & caller_body. local_decls , tcx) . ty ;
811+
812+ // We shouldn't have gotten here if the shim is empty, though it's
813+ // not actually a *problem* if we do -- it's easy to inline nothing.
814+ debug_assert ! ( drop_ty. needs_drop( tcx, inliner. typing_env( ) ) ) ;
815+
816+ let drop_ty_mut = Ty :: new_mut_ptr ( tcx, drop_ty) ;
817+ let arg = caller_body. local_decls . push ( LocalDecl :: new ( drop_ty_mut, source_info. span ) ) ;
818+ block_mut. statements . push ( Statement :: new (
819+ source_info,
820+ StatementKind :: Assign ( Box :: new ( (
821+ Place :: from ( arg) ,
822+ Rvalue :: RawPtr ( RawPtrKind :: Mut , place) ,
823+ ) ) ) ,
824+ ) ) ;
825+ let unit_local =
826+ caller_body. local_decls . push ( LocalDecl :: new ( tcx. types . unit , source_info. span ) ) ;
827+ terminator. kind = TerminatorKind :: Call {
828+ func : Operand :: function_handle (
829+ tcx,
830+ callsite. callee . def_id ( ) ,
831+ [ drop_ty. into ( ) ] ,
832+ source_info. span ,
833+ ) ,
834+ args : Box :: new ( [ Spanned {
835+ node : Operand :: Move ( Place :: from ( arg) ) ,
836+ span : source_info. span ,
837+ } ] ) ,
838+ destination : Place :: from ( unit_local) ,
839+ target : Some ( target) ,
840+ unwind,
841+ call_source : CallSource :: Misc ,
842+ fn_span : source_info. span ,
843+ } ;
844+ }
845+ }
846+
694847 let old_blocks = caller_body. basic_blocks . next_index ( ) ;
695848 inline_call ( inliner, caller_body, callsite, callee_body) ;
696849 let new_blocks = old_blocks..caller_body. basic_blocks . next_index ( ) ;
0 commit comments