about summary refs log tree commit diff
path: root/compiler/rustc_mir_transform/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_mir_transform/src')
-rw-r--r--compiler/rustc_mir_transform/src/inline.rs27
-rw-r--r--compiler/rustc_mir_transform/src/lib.rs5
-rw-r--r--compiler/rustc_mir_transform/src/mentioned_items.rs57
-rw-r--r--compiler/rustc_mir_transform/src/shim.rs4
4 files changed, 91 insertions, 2 deletions
diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs
index f6a0945c222..0f6a7ae982c 100644
--- a/compiler/rustc_mir_transform/src/inline.rs
+++ b/compiler/rustc_mir_transform/src/inline.rs
@@ -565,7 +565,8 @@ impl<'tcx> Inliner<'tcx> {
         mut callee_body: Body<'tcx>,
     ) {
         let terminator = caller_body[callsite.block].terminator.take().unwrap();
-        let TerminatorKind::Call { args, destination, unwind, target, .. } = terminator.kind else {
+        let TerminatorKind::Call { func, args, destination, unwind, target, .. } = terminator.kind
+        else {
             bug!("unexpected terminator kind {:?}", terminator.kind);
         };
 
@@ -717,6 +718,30 @@ impl<'tcx> Inliner<'tcx> {
                 Const::Val(..) | Const::Unevaluated(..) => true,
             },
         ));
+        // Now that we incorporated the callee's `required_consts`, we can remove the callee from
+        // `mentioned_items` -- but we have to take their `mentioned_items` in return. This does
+        // some extra work here to save the monomorphization collector work later. It helps a lot,
+        // since monomorphization can avoid a lot of work when the "mentioned items" are similar to
+        // the actually used items. By doing this we can entirely avoid visiting the callee!
+        let callee_item = {
+            // We need to reconstruct the `required_item` for the callee so that we can find and
+            // remove it.
+            let func_ty = func.ty(caller_body, self.tcx);
+            match func_ty.kind() {
+                ty::FnDef(def_id, args) => MentionedItem::Fn(*def_id, args),
+                _ => bug!(),
+            }
+        };
+        if let Some(idx) =
+            caller_body.mentioned_items.iter().position(|item| item.node == callee_item)
+        {
+            // We found the callee, so remove it and add its items instead.
+            caller_body.mentioned_items.remove(idx);
+            caller_body.mentioned_items.extend(callee_body.mentioned_items);
+        } else {
+            // If we can't find the callee, there's no point in adding its items.
+            // Probably it already got removed by being inlined elsewhere in the same function.
+        }
     }
 
     fn make_call_args(
diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs
index afe228be127..74e7d51ea96 100644
--- a/compiler/rustc_mir_transform/src/lib.rs
+++ b/compiler/rustc_mir_transform/src/lib.rs
@@ -89,6 +89,7 @@ mod lint;
 mod lower_intrinsics;
 mod lower_slice_len;
 mod match_branches;
+mod mentioned_items;
 mod multiple_return_terminators;
 mod normalize_array_len;
 mod nrvo;
@@ -566,6 +567,10 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         tcx,
         body,
         &[
+            // Before doing anything, remember which items are being mentioned so that the set of items
+            // visited does not depend on the optimization level.
+            &mentioned_items::MentionedItems,
+            // Add some UB checks before any UB gets optimized away.
             &check_alignment::CheckAlignment,
             // Before inlining: trim down MIR with passes to reduce inlining work.
 
diff --git a/compiler/rustc_mir_transform/src/mentioned_items.rs b/compiler/rustc_mir_transform/src/mentioned_items.rs
new file mode 100644
index 00000000000..ed363b4f252
--- /dev/null
+++ b/compiler/rustc_mir_transform/src/mentioned_items.rs
@@ -0,0 +1,57 @@
+use rustc_middle::mir::visit::Visitor;
+use rustc_middle::mir::{self, ConstOperand, Location, MentionedItem, MirPass};
+use rustc_middle::ty::{self, TyCtxt};
+use rustc_session::Session;
+use rustc_span::source_map::Spanned;
+
+pub struct MentionedItems;
+
+struct MentionedItemsVisitor<'a, 'tcx> {
+    tcx: TyCtxt<'tcx>,
+    body: &'a mir::Body<'tcx>,
+    mentioned_items: &'a mut Vec<Spanned<MentionedItem<'tcx>>>,
+}
+
+impl<'tcx> MirPass<'tcx> for MentionedItems {
+    fn is_enabled(&self, _sess: &Session) -> bool {
+        // If this pass is skipped the collector assume that nothing got mentioned! We could
+        // potentially skip it in opt-level 0 if we are sure that opt-level will never *remove* uses
+        // of anything, but that still seems fragile. Furthermore, even debug builds use level 1, so
+        // special-casing level 0 is just not worth it.
+        true
+    }
+
+    fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut mir::Body<'tcx>) {
+        debug_assert!(body.mentioned_items.is_empty());
+        let mut mentioned_items = Vec::new();
+        MentionedItemsVisitor { tcx, body, mentioned_items: &mut mentioned_items }.visit_body(body);
+        body.mentioned_items = mentioned_items;
+    }
+}
+
+impl<'tcx> Visitor<'tcx> for MentionedItemsVisitor<'_, 'tcx> {
+    fn visit_constant(&mut self, constant: &ConstOperand<'tcx>, _: Location) {
+        let const_ = constant.const_;
+        // This is how function items get referenced: via constants of `FnDef` type. This handles
+        // both functions that are called and those that are just turned to function pointers.
+        if let ty::FnDef(def_id, args) = const_.ty().kind() {
+            debug!("adding to required_items: {def_id:?}");
+            self.mentioned_items
+                .push(Spanned { node: MentionedItem::Fn(*def_id, args), span: constant.span });
+        }
+    }
+
+    fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) {
+        self.super_terminator(terminator, location);
+        match terminator.kind {
+            // We don't need to handle `Call` as we already handled all function type operands in
+            // `visit_constant`. But we do need to handle `Drop`.
+            mir::TerminatorKind::Drop { place, .. } => {
+                let ty = place.ty(self.body, self.tcx).ty;
+                let span = self.body.source_info(location).span;
+                self.mentioned_items.push(Spanned { node: MentionedItem::Drop(ty), span });
+            }
+            _ => {}
+        }
+    }
+}
diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs
index 733e2f93b25..dd2b24ad669 100644
--- a/compiler/rustc_mir_transform/src/shim.rs
+++ b/compiler/rustc_mir_transform/src/shim.rs
@@ -17,7 +17,7 @@ use std::iter;
 
 use crate::{
     abort_unwinding_calls, add_call_guards, add_moves_for_packed_drops, deref_separator,
-    pass_manager as pm, remove_noop_landing_pads, simplify,
+    mentioned_items, pass_manager as pm, remove_noop_landing_pads, simplify,
 };
 use rustc_middle::mir::patch::MirPatch;
 use rustc_mir_dataflow::elaborate_drops::{self, DropElaborator, DropFlagMode, DropStyle};
@@ -147,6 +147,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
                     tcx,
                     &mut body,
                     &[
+                        &mentioned_items::MentionedItems,
                         &abort_unwinding_calls::AbortUnwindingCalls,
                         &add_call_guards::CriticalCallEdges,
                     ],
@@ -178,6 +179,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
         tcx,
         &mut result,
         &[
+            &mentioned_items::MentionedItems,
             &add_moves_for_packed_drops::AddMovesForPackedDrops,
             &deref_separator::Derefer,
             &remove_noop_landing_pads::RemoveNoopLandingPads,