@@ -4,7 +4,9 @@ use std::assert_matches::debug_assert_matches;
4
4
use std:: iter;
5
5
use std:: ops:: { Range , RangeFrom } ;
6
6
7
+ use either:: Either ;
7
8
use rustc_abi:: { ExternAbi , FieldIdx } ;
9
+ use rustc_hir:: LangItem ;
8
10
use rustc_hir:: attrs:: { InlineAttr , OptimizeAttr } ;
9
11
use rustc_hir:: def:: DefKind ;
10
12
use rustc_hir:: def_id:: DefId ;
@@ -116,6 +118,9 @@ trait Inliner<'tcx> {
116
118
/// Has the caller body been changed?
117
119
fn changed ( self ) -> bool ;
118
120
121
+ /// Whether to also attempt to inline `Drop` terminators (not just `Call`s)
122
+ fn consider_drops ( & self ) -> bool ;
123
+
119
124
/// Should inlining happen for a given callee?
120
125
fn should_inline_for_callee ( & self , def_id : DefId ) -> bool ;
121
126
@@ -187,6 +192,10 @@ impl<'tcx> Inliner<'tcx> for ForceInliner<'tcx> {
187
192
self . changed
188
193
}
189
194
195
+ fn consider_drops ( & self ) -> bool {
196
+ false
197
+ }
198
+
190
199
fn should_inline_for_callee ( & self , def_id : DefId ) -> bool {
191
200
ForceInline :: should_run_pass_for_callee ( self . tcx ( ) , def_id)
192
201
}
@@ -272,6 +281,7 @@ struct NormalInliner<'tcx> {
272
281
typing_env : ty:: TypingEnv < ' tcx > ,
273
282
/// `DefId` of caller.
274
283
def_id : DefId ,
284
+ caller_is_coroutine : bool ,
275
285
/// Stack of inlined instances.
276
286
/// We only check the `DefId` and not the args because we want to
277
287
/// avoid inlining cases of polymorphic recursion.
@@ -304,6 +314,7 @@ impl<'tcx> Inliner<'tcx> for NormalInliner<'tcx> {
304
314
tcx,
305
315
typing_env,
306
316
def_id,
317
+ caller_is_coroutine : tcx. is_coroutine ( def_id) ,
307
318
history : Vec :: new ( ) ,
308
319
top_down_counter : 0 ,
309
320
changed : false ,
@@ -334,6 +345,10 @@ impl<'tcx> Inliner<'tcx> for NormalInliner<'tcx> {
334
345
self . changed
335
346
}
336
347
348
+ fn consider_drops ( & self ) -> bool {
349
+ !self . caller_is_coroutine
350
+ }
351
+
337
352
fn should_inline_for_callee ( & self , _: DefId ) -> bool {
338
353
true
339
354
}
@@ -542,57 +557,133 @@ fn process_blocks<'tcx, I: Inliner<'tcx>>(
542
557
}
543
558
}
544
559
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
+
545
613
fn resolve_callsite < ' tcx , I : Inliner < ' tcx > > (
546
614
inliner : & I ,
547
615
caller_body : & Body < ' tcx > ,
548
616
bb : BasicBlock ,
549
617
bb_data : & BasicBlockData < ' tcx > ,
550
618
) -> Option < CallSite < ' tcx > > {
551
619
let tcx = inliner. tcx ( ) ;
552
- // Only consider direct calls to functions
553
620
let terminator = bb_data. terminator ( ) ;
554
621
555
622
// 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
+ }
563
631
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
+ }
568
641
569
- if let InstanceKind :: Virtual ( ..) | InstanceKind :: Intrinsic ( _) = callee. def {
642
+ ( def_id, args, * fn_span)
643
+ } else {
570
644
return None ;
571
645
}
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) {
574
650
return None ;
575
651
}
576
652
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
+ } ;
578
660
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 ( ) ?;
588
664
589
- let source_info = SourceInfo { span : fn_span, ..terminator. source_info } ;
665
+ if let InstanceKind :: Virtual ( ..) | InstanceKind :: Intrinsic ( _) = callee. def {
666
+ return None ;
667
+ }
590
668
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 ;
593
683
}
594
684
595
- None
685
+ let source_info = SourceInfo { span : fn_span, ..terminator. source_info } ;
686
+ Some ( CallSite { callee, fn_sig, block : bb, source_info } )
596
687
}
597
688
598
689
/// 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>>(
604
695
callsite : & CallSite < ' tcx > ,
605
696
) -> Result < std:: ops:: Range < BasicBlock > , & ' static str > {
606
697
let tcx = inliner. tcx ( ) ;
698
+
607
699
check_mir_is_available ( inliner, caller_body, callsite. callee ) ?;
608
700
609
701
let callee_attrs = tcx. codegen_fn_attrs ( callsite. callee . def_id ( ) ) ;
610
702
check_inline:: is_inline_valid_on_fn ( tcx, callsite. callee . def_id ( ) ) ?;
611
703
check_codegen_attributes ( inliner, callsite, callee_attrs) ?;
612
704
inliner. check_codegen_attributes_extra ( callee_attrs) ?;
613
705
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
+ )
622
719
}
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
+ } ;
624
730
625
731
let callee_body = try_instance_mir ( tcx, callsite. callee . def ) ?;
626
732
check_inline:: is_inline_valid_on_body ( tcx, callee_body) ?;
@@ -651,15 +757,15 @@ fn try_inlining<'tcx, I: Inliner<'tcx>>(
651
757
return Err ( "implementation limitation -- return type mismatch" ) ;
652
758
}
653
759
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
+ }
658
767
} ;
659
768
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) ;
663
769
let arg_tys = if callee_body. spread_arg . is_some ( ) {
664
770
std:: slice:: from_ref ( & arg_tuple_ty)
665
771
} else {
@@ -680,9 +786,8 @@ fn try_inlining<'tcx, I: Inliner<'tcx>>(
680
786
}
681
787
}
682
788
} 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 ( ) ) {
684
790
let input_type = callee_body. local_decls [ input] . ty ;
685
- let arg_ty = arg. node . ty ( & caller_body. local_decls , tcx) ;
686
791
if !util:: sub_types ( tcx, inliner. typing_env ( ) , input_type, arg_ty) {
687
792
trace ! ( ?arg_ty, ?input_type) ;
688
793
debug ! ( "failed to normalize argument type" ) ;
@@ -691,6 +796,59 @@ fn try_inlining<'tcx, I: Inliner<'tcx>>(
691
796
}
692
797
}
693
798
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
+
694
852
let old_blocks = caller_body. basic_blocks . next_index ( ) ;
695
853
inline_call ( inliner, caller_body, callsite, callee_body) ;
696
854
let new_blocks = old_blocks..caller_body. basic_blocks . next_index ( ) ;
0 commit comments