@@ -5,6 +5,7 @@ use std::iter;
5
5
use std:: ops:: { Range , RangeFrom } ;
6
6
7
7
use rustc_abi:: { ExternAbi , FieldIdx } ;
8
+ use rustc_hir:: LangItem ;
8
9
use rustc_hir:: attrs:: { InlineAttr , OptimizeAttr } ;
9
10
use rustc_hir:: def:: DefKind ;
10
11
use rustc_hir:: def_id:: DefId ;
@@ -116,6 +117,9 @@ trait Inliner<'tcx> {
116
117
/// Has the caller body been changed?
117
118
fn changed ( self ) -> bool ;
118
119
120
+ /// Whether to also attempt to inline `Drop` terminators (not just `Call`s)
121
+ fn consider_drops ( & self ) -> bool ;
122
+
119
123
/// Should inlining happen for a given callee?
120
124
fn should_inline_for_callee ( & self , def_id : DefId ) -> bool ;
121
125
@@ -187,6 +191,10 @@ impl<'tcx> Inliner<'tcx> for ForceInliner<'tcx> {
187
191
self . changed
188
192
}
189
193
194
+ fn consider_drops ( & self ) -> bool {
195
+ false
196
+ }
197
+
190
198
fn should_inline_for_callee ( & self , def_id : DefId ) -> bool {
191
199
ForceInline :: should_run_pass_for_callee ( self . tcx ( ) , def_id)
192
200
}
@@ -272,6 +280,7 @@ struct NormalInliner<'tcx> {
272
280
typing_env : ty:: TypingEnv < ' tcx > ,
273
281
/// `DefId` of caller.
274
282
def_id : DefId ,
283
+ caller_is_coroutine : bool ,
275
284
/// Stack of inlined instances.
276
285
/// We only check the `DefId` and not the args because we want to
277
286
/// avoid inlining cases of polymorphic recursion.
@@ -304,6 +313,7 @@ impl<'tcx> Inliner<'tcx> for NormalInliner<'tcx> {
304
313
tcx,
305
314
typing_env,
306
315
def_id,
316
+ caller_is_coroutine : tcx. is_coroutine ( def_id) ,
307
317
history : Vec :: new ( ) ,
308
318
top_down_counter : 0 ,
309
319
changed : false ,
@@ -334,6 +344,10 @@ impl<'tcx> Inliner<'tcx> for NormalInliner<'tcx> {
334
344
self . changed
335
345
}
336
346
347
+ fn consider_drops ( & self ) -> bool {
348
+ !self . caller_is_coroutine
349
+ }
350
+
337
351
fn should_inline_for_callee ( & self , _: DefId ) -> bool {
338
352
true
339
353
}
@@ -542,57 +556,133 @@ fn process_blocks<'tcx, I: Inliner<'tcx>>(
542
556
}
543
557
}
544
558
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
+
545
612
fn resolve_callsite < ' tcx , I : Inliner < ' tcx > > (
546
613
inliner : & I ,
547
614
caller_body : & Body < ' tcx > ,
548
615
bb : BasicBlock ,
549
616
bb_data : & BasicBlockData < ' tcx > ,
550
617
) -> Option < CallSite < ' tcx > > {
551
618
let tcx = inliner. tcx ( ) ;
552
- // Only consider direct calls to functions
553
619
let terminator = bb_data. terminator ( ) ;
554
620
555
621
// 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
+ }
563
630
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
+ }
568
640
569
- if let InstanceKind :: Virtual ( ..) | InstanceKind :: Intrinsic ( _) = callee. def {
641
+ ( def_id, args, * fn_span)
642
+ } else {
570
643
return None ;
571
644
}
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) {
574
649
return None ;
575
650
}
576
651
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
+ } ;
578
659
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
+ }
588
667
589
- let source_info = SourceInfo { span : fn_span, ..terminator. source_info } ;
668
+ if inliner. history ( ) . contains ( & callee. def_id ( ) ) {
669
+ return None ;
670
+ }
590
671
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 ;
593
682
}
594
683
595
- None
684
+ let source_info = SourceInfo { span : fn_span, ..terminator. source_info } ;
685
+ Some ( CallSite { callee, fn_sig, block : bb, source_info } )
596
686
}
597
687
598
688
/// 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>>(
604
694
callsite : & CallSite < ' tcx > ,
605
695
) -> Result < std:: ops:: Range < BasicBlock > , & ' static str > {
606
696
let tcx = inliner. tcx ( ) ;
697
+
607
698
check_mir_is_available ( inliner, caller_body, callsite. callee ) ?;
608
699
609
700
let callee_attrs = tcx. codegen_fn_attrs ( callsite. callee . def_id ( ) ) ;
610
701
check_inline:: is_inline_valid_on_fn ( tcx, callsite. callee . def_id ( ) ) ?;
611
702
check_codegen_attributes ( inliner, callsite, callee_attrs) ?;
612
703
inliner. check_codegen_attributes_extra ( callee_attrs) ?;
613
704
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 )
622
715
}
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
+ } ;
624
724
625
725
let callee_body = try_instance_mir ( tcx, callsite. callee . def ) ?;
626
726
check_inline:: is_inline_valid_on_body ( tcx, callee_body) ?;
@@ -691,6 +791,59 @@ fn try_inlining<'tcx, I: Inliner<'tcx>>(
691
791
}
692
792
}
693
793
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
+
694
847
let old_blocks = caller_body. basic_blocks . next_index ( ) ;
695
848
inline_call ( inliner, caller_body, callsite, callee_body) ;
696
849
let new_blocks = old_blocks..caller_body. basic_blocks . next_index ( ) ;
0 commit comments