@@ -720,6 +720,9 @@ struct MirUsedCollector<'a, 'tcx> {
720720 tcx : TyCtxt < ' tcx > ,
721721 body : & ' a mir:: Body < ' tcx > ,
722722 used_items : & ' a mut MonoItems < ' tcx > ,
723+ /// See the comment in `collect_items_of_instance` for the purpose of this set.
724+ /// Note that this contains *not-monomorphized* items!
725+ used_mentioned_items : & ' a mut FxHashSet < MentionedItem < ' tcx > > ,
723726 instance : Instance < ' tcx > ,
724727 /// Spans for move size lints already emitted. Helps avoid duplicate lints.
725728 move_size_spans : Vec < Span > ,
@@ -990,12 +993,18 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> {
990993 match terminator. kind {
991994 mir:: TerminatorKind :: Call { ref func, ref args, ref fn_span, .. } => {
992995 let callee_ty = func. ty ( self . body , tcx) ;
996+ // *Before* monomorphizing, record that we already handled this mention.
997+ if let ty:: FnDef ( def_id, args) = callee_ty. kind ( ) {
998+ self . used_mentioned_items . insert ( MentionedItem :: Fn ( * def_id, args) ) ;
999+ }
9931000 let callee_ty = self . monomorphize ( callee_ty) ;
9941001 self . check_fn_args_move_size ( callee_ty, args, * fn_span, location) ;
9951002 visit_fn_use ( self . tcx , callee_ty, true , source, & mut self . used_items )
9961003 }
9971004 mir:: TerminatorKind :: Drop { ref place, .. } => {
9981005 let ty = place. ty ( self . body , self . tcx ) . ty ;
1006+ // *Before* monomorphizing, record that we already handled this mention.
1007+ self . used_mentioned_items . insert ( MentionedItem :: Drop ( ty) ) ;
9991008 let ty = self . monomorphize ( ty) ;
10001009 visit_drop_use ( self . tcx , ty, true , source, self . used_items ) ;
10011010 }
@@ -1639,10 +1648,22 @@ fn collect_items_of_instance<'tcx>(
16391648 mode : CollectionMode ,
16401649) {
16411650 let body = tcx. instance_mir ( instance. def ) ;
1651+ // Naively, in "used" collection mode, all functions get added to *both* `used_items` and
1652+ // `mentioned_items`. Mentioned items processing will then notice that they have already been
1653+ // visited, but at that point each mentioned item has been monomorphized, added to the
1654+ // `mentioned_items` worklist, and checked in the global set of visited items. To removes that
1655+ // overhead, we have a special optimization that avoids adding items to `mentioned_items` when
1656+ // they are already added in `used_items`. We could just scan `used_items`, but that's a linear
1657+ // scan and not very efficient. Furthermore we can only do that *after* monomorphizing the
1658+ // mentioned item. So instead we collect all pre-monomorphized `MentionedItem` that were already
1659+ // added to `used_items` in a hash set, which can efficiently query in the
1660+ // `body.mentioned_items` loop below.
1661+ let mut used_mentioned_items = FxHashSet :: < MentionedItem < ' tcx > > :: default ( ) ;
16421662 let mut collector = MirUsedCollector {
16431663 tcx,
16441664 body,
16451665 used_items,
1666+ used_mentioned_items : & mut used_mentioned_items,
16461667 instance,
16471668 move_size_spans : vec ! [ ] ,
16481669 visiting_call_terminator : false ,
@@ -1662,10 +1683,13 @@ fn collect_items_of_instance<'tcx>(
16621683 }
16631684 }
16641685
1665- // Always gather mentioned items.
1686+ // Always gather mentioned items. We try to avoid processing items that we have already added to
1687+ // `used_items` above.
16661688 for item in & body. mentioned_items {
1667- let item_mono = collector. monomorphize ( item. node ) ;
1668- visit_mentioned_item ( tcx, & item_mono, item. span , mentioned_items) ;
1689+ if !collector. used_mentioned_items . contains ( & item. node ) {
1690+ let item_mono = collector. monomorphize ( item. node ) ;
1691+ visit_mentioned_item ( tcx, & item_mono, item. span , mentioned_items) ;
1692+ }
16691693 }
16701694}
16711695
0 commit comments