@@ -4,7 +4,9 @@ use std::assert_matches::debug_assert_matches;
44use std:: iter;
55use std:: ops:: { Range , RangeFrom } ;
66
7+ use either:: Either ;
78use rustc_abi:: { ExternAbi , FieldIdx } ;
9+ use rustc_hir:: LangItem ;
810use rustc_hir:: attrs:: { InlineAttr , OptimizeAttr } ;
911use rustc_hir:: def:: DefKind ;
1012use rustc_hir:: def_id:: DefId ;
@@ -116,6 +118,9 @@ trait Inliner<'tcx> {
116118 /// Has the caller body been changed?
117119 fn changed ( self ) -> bool ;
118120
121+ /// Whether to also attempt to inline `Drop` terminators (not just `Call`s)
122+ fn consider_drops ( & self ) -> bool ;
123+
119124 /// Should inlining happen for a given callee?
120125 fn should_inline_for_callee ( & self , def_id : DefId ) -> bool ;
121126
@@ -187,6 +192,10 @@ impl<'tcx> Inliner<'tcx> for ForceInliner<'tcx> {
187192 self . changed
188193 }
189194
195+ fn consider_drops ( & self ) -> bool {
196+ false
197+ }
198+
190199 fn should_inline_for_callee ( & self , def_id : DefId ) -> bool {
191200 ForceInline :: should_run_pass_for_callee ( self . tcx ( ) , def_id)
192201 }
@@ -272,6 +281,7 @@ struct NormalInliner<'tcx> {
272281 typing_env : ty:: TypingEnv < ' tcx > ,
273282 /// `DefId` of caller.
274283 def_id : DefId ,
284+ caller_is_coroutine : bool ,
275285 /// Stack of inlined instances.
276286 /// We only check the `DefId` and not the args because we want to
277287 /// avoid inlining cases of polymorphic recursion.
@@ -304,6 +314,7 @@ impl<'tcx> Inliner<'tcx> for NormalInliner<'tcx> {
304314 tcx,
305315 typing_env,
306316 def_id,
317+ caller_is_coroutine : tcx. is_coroutine ( def_id) ,
307318 history : Vec :: new ( ) ,
308319 top_down_counter : 0 ,
309320 changed : false ,
@@ -334,6 +345,10 @@ impl<'tcx> Inliner<'tcx> for NormalInliner<'tcx> {
334345 self . changed
335346 }
336347
348+ fn consider_drops ( & self ) -> bool {
349+ !self . caller_is_coroutine
350+ }
351+
337352 fn should_inline_for_callee ( & self , _: DefId ) -> bool {
338353 true
339354 }
@@ -542,57 +557,133 @@ fn process_blocks<'tcx, I: Inliner<'tcx>>(
542557 }
543558}
544559
560+ /// Returns a value indicating whether it's worth trying to inline a `drop` for `ty`.
561+ ///
562+ /// We only want to bother inlining things that have a change to need to do something.
563+ /// The `RemoveUnneededDrops` pass will handle things that obviously don't need
564+ /// dropping, and will do it more efficiently since it doesn't need to add inlining
565+ /// metadata, defensively add new blocks, etc.
566+ ///
567+ /// But this isn't the same as `needs_drop` because we want the opposite fallback:
568+ /// while `needs_drop` is true for a (non-Copy) type parameter, here we don't
569+ /// want to attempt inlining its drop because that'll never work.
570+ fn should_attempt_inline_drop_for_type < ' tcx > (
571+ tcx : TyCtxt < ' tcx > ,
572+ typing_env : ty:: TypingEnv < ' tcx > ,
573+ ty : Ty < ' tcx > ,
574+ ) -> bool {
575+ match ty. kind ( ) {
576+ ty:: Tuple ( elems) if elems. is_empty ( ) => false ,
577+
578+ // Even if these might have drops later, we can't inline them now.
579+ ty:: Param ( ..) | ty:: Alias ( ..) | ty:: Dynamic ( ..) | ty:: Foreign ( ..) => false ,
580+
581+ ty:: Array ( ..)
582+ | ty:: Adt ( ..)
583+ | ty:: Slice ( ..)
584+ | ty:: Tuple ( ..)
585+ | ty:: Closure ( ..)
586+ | ty:: CoroutineClosure ( ..)
587+ | ty:: Coroutine ( ..)
588+ | ty:: CoroutineWitness ( ..) => ty. needs_drop ( tcx, typing_env) ,
589+
590+ // Primitives we obviously don't need to inline a drop method
591+ ty:: Error ( ..)
592+ | ty:: Bool
593+ | ty:: Int ( ..)
594+ | ty:: Uint ( ..)
595+ | ty:: Float ( ..)
596+ | ty:: Never
597+ | ty:: FnDef ( ..)
598+ | ty:: FnPtr ( ..)
599+ | ty:: Char
600+ | ty:: RawPtr ( ..)
601+ | ty:: Ref ( ..)
602+ | ty:: Str => false ,
603+
604+ // FIXME: Unsure what to do with this, but not attempting inlining is safe
605+ ty:: Pat ( ..) | ty:: UnsafeBinder ( ..) => false ,
606+
607+ ty:: Infer ( ..) | ty:: Placeholder ( ..) | ty:: Bound ( ..) => {
608+ bug ! ( "weird type while inlining: {ty:?}" )
609+ }
610+ }
611+ }
612+
545613fn resolve_callsite < ' tcx , I : Inliner < ' tcx > > (
546614 inliner : & I ,
547615 caller_body : & Body < ' tcx > ,
548616 bb : BasicBlock ,
549617 bb_data : & BasicBlockData < ' tcx > ,
550618) -> Option < CallSite < ' tcx > > {
551619 let tcx = inliner. tcx ( ) ;
552- // Only consider direct calls to functions
553620 let terminator = bb_data. terminator ( ) ;
554621
555622 // 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- }
623+ let ( def_id, args, fn_span) = match & terminator. kind {
624+ TerminatorKind :: Call { func, fn_span, .. } => {
625+ let func_ty = func. ty ( caller_body, tcx) ;
626+ if let ty:: FnDef ( def_id, args) = * func_ty. kind ( ) {
627+ if !inliner. should_inline_for_callee ( def_id) {
628+ debug ! ( "not enabled" ) ;
629+ return None ;
630+ }
563631
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 ( ) ?;
632+ // Allow RemoveUnneededDrops to handle these, rather than inlining,
633+ // since it doesn't add the extra locals nor the metadata.
634+ if inliner. consider_drops ( )
635+ && tcx. is_lang_item ( def_id, LangItem :: DropInPlace )
636+ && let drop_ty = args. type_at ( 0 )
637+ && !should_attempt_inline_drop_for_type ( tcx, inliner. typing_env ( ) , drop_ty)
638+ {
639+ return None ;
640+ }
568641
569- if let InstanceKind :: Virtual ( ..) | InstanceKind :: Intrinsic ( _) = callee. def {
642+ ( def_id, args, * fn_span)
643+ } else {
570644 return None ;
571645 }
572-
573- if inliner. history ( ) . contains ( & callee. def_id ( ) ) {
646+ }
647+ TerminatorKind :: Drop { place, .. } if inliner. consider_drops ( ) => {
648+ let drop_ty = place. ty ( & caller_body. local_decls , tcx) . ty ;
649+ if !should_attempt_inline_drop_for_type ( tcx, inliner. typing_env ( ) , drop_ty) {
574650 return None ;
575651 }
576652
577- let fn_sig = tcx. fn_sig ( def_id) . instantiate ( tcx, args) ;
653+ let drop_def_id =
654+ tcx. require_lang_item ( LangItem :: DropInPlace , terminator. source_info . span ) ;
655+ let args = tcx. mk_args ( & [ drop_ty. into ( ) ] ) ;
656+ ( drop_def_id, args, rustc_span:: DUMMY_SP )
657+ }
658+ _ => return None ,
659+ } ;
578660
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- }
661+ // To resolve an instance its args have to be fully normalized.
662+ let args = tcx. try_normalize_erasing_regions ( inliner. typing_env ( ) , args) . ok ( ) ?;
663+ let callee = Instance :: try_resolve ( tcx, inliner. typing_env ( ) , def_id, args) . ok ( ) . flatten ( ) ?;
588664
589- let source_info = SourceInfo { span : fn_span, ..terminator. source_info } ;
665+ if let InstanceKind :: Virtual ( ..) | InstanceKind :: Intrinsic ( _) = callee. def {
666+ return None ;
667+ }
590668
591- return Some ( CallSite { callee, fn_sig, block : bb, source_info } ) ;
592- }
669+ if inliner. history ( ) . contains ( & callee. def_id ( ) ) {
670+ return None ;
671+ }
672+
673+ let fn_sig = tcx. fn_sig ( def_id) . instantiate ( tcx, args) ;
674+
675+ // Additionally, check that the body that we're inlining actually agrees
676+ // with the ABI of the trait that the item comes from.
677+ if let InstanceKind :: Item ( instance_def_id) = callee. def
678+ && tcx. def_kind ( instance_def_id) == DefKind :: AssocFn
679+ && let instance_fn_sig = tcx. fn_sig ( instance_def_id) . skip_binder ( )
680+ && instance_fn_sig. abi ( ) != fn_sig. abi ( )
681+ {
682+ return None ;
593683 }
594684
595- None
685+ let source_info = SourceInfo { span : fn_span, ..terminator. source_info } ;
686+ Some ( CallSite { callee, fn_sig, block : bb, source_info } )
596687}
597688
598689/// Attempts to inline a callsite into the caller body. When successful returns basic blocks
@@ -604,23 +695,38 @@ fn try_inlining<'tcx, I: Inliner<'tcx>>(
604695 callsite : & CallSite < ' tcx > ,
605696) -> Result < std:: ops:: Range < BasicBlock > , & ' static str > {
606697 let tcx = inliner. tcx ( ) ;
698+
607699 check_mir_is_available ( inliner, caller_body, callsite. callee ) ?;
608700
609701 let callee_attrs = tcx. codegen_fn_attrs ( callsite. callee . def_id ( ) ) ;
610702 check_inline:: is_inline_valid_on_fn ( tcx, callsite. callee . def_id ( ) ) ?;
611703 check_codegen_attributes ( inliner, callsite, callee_attrs) ?;
612704 inliner. check_codegen_attributes_extra ( callee_attrs) ?;
613705
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" ) ;
706+ let ( arg_tys_iter, destination_ty) = match & caller_body[ callsite. block ] . terminator ( ) . kind {
707+ TerminatorKind :: Call { args, destination, .. } => {
708+ for arg in args {
709+ if !arg. node . ty ( & caller_body. local_decls , tcx) . is_sized ( tcx, inliner. typing_env ( ) ) {
710+ // We do not allow inlining functions with unsized params. Inlining these functions
711+ // could create unsized locals, which are unsound and being phased out.
712+ return Err ( "call has unsized argument" ) ;
713+ }
714+ }
715+ (
716+ Either :: Left ( args. iter ( ) . map ( |arg| arg. node . ty ( & caller_body. local_decls , tcx) ) ) ,
717+ destination. ty ( & caller_body. local_decls , tcx) . ty ,
718+ )
622719 }
623- }
720+ TerminatorKind :: Drop { place, .. } => {
721+ let place_ty = place. ty ( & caller_body. local_decls , tcx) . ty ;
722+ let arg_ty = Ty :: new_mut_ptr ( tcx, place_ty) ;
723+ // We cheat a bit here. Obviously there *is* an argument to the
724+ // `drop_in_place`, but all the checks that look at it are ok to skip
725+ // since we're generating them with always the correct type.
726+ ( Either :: Right ( std:: iter:: once ( arg_ty) ) , tcx. types . unit )
727+ }
728+ _ => bug ! ( ) ,
729+ } ;
624730
625731 let callee_body = try_instance_mir ( tcx, callsite. callee . def ) ?;
626732 check_inline:: is_inline_valid_on_body ( tcx, callee_body) ?;
@@ -651,15 +757,15 @@ fn try_inlining<'tcx, I: Inliner<'tcx>>(
651757 return Err ( "implementation limitation -- return type mismatch" ) ;
652758 }
653759 if callsite. fn_sig . abi ( ) == ExternAbi :: RustCall {
654- let ( self_arg, arg_tuple) = match & args[ ..] {
655- [ arg_tuple] => ( None , arg_tuple) ,
656- [ self_arg, arg_tuple] => ( Some ( self_arg) , arg_tuple) ,
657- _ => bug ! ( "Expected `rust-call` to have 1 or 2 args" ) ,
760+ let ( self_arg_ty, arg_tuple_ty) = {
761+ let mut arg_tys_iter = arg_tys_iter. fuse ( ) ;
762+ match ( arg_tys_iter. next ( ) , arg_tys_iter. next ( ) , arg_tys_iter. next ( ) ) {
763+ ( Some ( arg_tuple) , None , None ) => ( None , arg_tuple) ,
764+ ( Some ( self_arg) , Some ( arg_tuple) , None ) => ( Some ( self_arg) , arg_tuple) ,
765+ _ => bug ! ( "Expected `rust-call` to have 1 or 2 args" ) ,
766+ }
658767 } ;
659768
660- let self_arg_ty = self_arg. map ( |self_arg| self_arg. node . ty ( & caller_body. local_decls , tcx) ) ;
661-
662- let arg_tuple_ty = arg_tuple. node . ty ( & caller_body. local_decls , tcx) ;
663769 let arg_tys = if callee_body. spread_arg . is_some ( ) {
664770 std:: slice:: from_ref ( & arg_tuple_ty)
665771 } else {
@@ -680,9 +786,8 @@ fn try_inlining<'tcx, I: Inliner<'tcx>>(
680786 }
681787 }
682788 } else {
683- for ( arg , input) in args . iter ( ) . zip ( callee_body. args_iter ( ) ) {
789+ for ( arg_ty , input) in arg_tys_iter . zip ( callee_body. args_iter ( ) ) {
684790 let input_type = callee_body. local_decls [ input] . ty ;
685- let arg_ty = arg. node . ty ( & caller_body. local_decls , tcx) ;
686791 if !util:: sub_types ( tcx, inliner. typing_env ( ) , input_type, arg_ty) {
687792 trace ! ( ?arg_ty, ?input_type) ;
688793 debug ! ( "failed to normalize argument type" ) ;
@@ -691,6 +796,59 @@ fn try_inlining<'tcx, I: Inliner<'tcx>>(
691796 }
692797 }
693798
799+ if inliner. consider_drops ( ) {
800+ let block_mut = & mut caller_body. basic_blocks . as_mut ( ) [ callsite. block ] ;
801+ let terminator = block_mut. terminator . as_mut ( ) . unwrap ( ) ;
802+ if let TerminatorKind :: Drop {
803+ place,
804+ target,
805+ unwind,
806+ replace : _,
807+ drop : None ,
808+ async_fut : None ,
809+ } = terminator. kind
810+ {
811+ // Rather than updating everything after here to also handle `Drop`,
812+ // just replace the terminator with a `Call`, since we'll need things
813+ // like the local for the argument anyway.
814+ let source_info = terminator. source_info ;
815+ let drop_ty = place. ty ( & caller_body. local_decls , tcx) . ty ;
816+
817+ // We shouldn't have gotten here if the shim is empty, though it's
818+ // not actually a *problem* if we do -- it's easy to inline nothing.
819+ debug_assert ! ( drop_ty. needs_drop( tcx, inliner. typing_env( ) ) ) ;
820+
821+ let drop_ty_mut = Ty :: new_mut_ptr ( tcx, drop_ty) ;
822+ let arg = caller_body. local_decls . push ( LocalDecl :: new ( drop_ty_mut, source_info. span ) ) ;
823+ block_mut. statements . push ( Statement :: new (
824+ source_info,
825+ StatementKind :: Assign ( Box :: new ( (
826+ Place :: from ( arg) ,
827+ Rvalue :: RawPtr ( RawPtrKind :: Mut , place) ,
828+ ) ) ) ,
829+ ) ) ;
830+ let unit_local =
831+ caller_body. local_decls . push ( LocalDecl :: new ( tcx. types . unit , source_info. span ) ) ;
832+ terminator. kind = TerminatorKind :: Call {
833+ func : Operand :: function_handle (
834+ tcx,
835+ callsite. callee . def_id ( ) ,
836+ [ drop_ty. into ( ) ] ,
837+ source_info. span ,
838+ ) ,
839+ args : Box :: new ( [ Spanned {
840+ node : Operand :: Move ( Place :: from ( arg) ) ,
841+ span : source_info. span ,
842+ } ] ) ,
843+ destination : Place :: from ( unit_local) ,
844+ target : Some ( target) ,
845+ unwind,
846+ call_source : CallSource :: Misc ,
847+ fn_span : source_info. span ,
848+ } ;
849+ }
850+ }
851+
694852 let old_blocks = caller_body. basic_blocks . next_index ( ) ;
695853 inline_call ( inliner, caller_body, callsite, callee_body) ;
696854 let new_blocks = old_blocks..caller_body. basic_blocks . next_index ( ) ;
0 commit comments