]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_mir_transform/src/inline/cycle.rs
New upstream version 1.59.0+dfsg1
[rustc.git] / compiler / rustc_mir_transform / src / inline / cycle.rs
CommitLineData
5869c6ff 1use rustc_data_structures::fx::{FxHashMap, FxHashSet};
6a06907d 2use rustc_data_structures::sso::SsoHashSet;
5869c6ff
XL
3use rustc_data_structures::stack::ensure_sufficient_stack;
4use rustc_hir::def_id::{DefId, LocalDefId};
5use rustc_middle::mir::TerminatorKind;
6use rustc_middle::ty::TypeFoldable;
7use rustc_middle::ty::{self, subst::SubstsRef, InstanceDef, TyCtxt};
136023e0 8use rustc_session::Limit;
5869c6ff
XL
9
10// FIXME: check whether it is cheaper to precompute the entire call graph instead of invoking
11// this query riddiculously often.
6a06907d 12#[instrument(level = "debug", skip(tcx, root, target))]
a2a8927a 13crate fn mir_callgraph_reachable<'tcx>(
5869c6ff
XL
14 tcx: TyCtxt<'tcx>,
15 (root, target): (ty::Instance<'tcx>, LocalDefId),
16) -> bool {
17 trace!(%root, target = %tcx.def_path_str(target.to_def_id()));
18 let param_env = tcx.param_env_reveal_all_normalized(target);
19 assert_ne!(
20 root.def_id().expect_local(),
21 target,
22 "you should not call `mir_callgraph_reachable` on immediate self recursion"
23 );
24 assert!(
25 matches!(root.def, InstanceDef::Item(_)),
26 "you should not call `mir_callgraph_reachable` on shims"
27 );
28 assert!(
29 !tcx.is_constructor(root.def_id()),
30 "you should not call `mir_callgraph_reachable` on enum/struct constructor functions"
31 );
6a06907d
XL
32 #[instrument(
33 level = "debug",
136023e0 34 skip(tcx, param_env, target, stack, seen, recursion_limiter, caller, recursion_limit)
6a06907d 35 )]
a2a8927a 36 fn process<'tcx>(
5869c6ff
XL
37 tcx: TyCtxt<'tcx>,
38 param_env: ty::ParamEnv<'tcx>,
39 caller: ty::Instance<'tcx>,
40 target: LocalDefId,
41 stack: &mut Vec<ty::Instance<'tcx>>,
42 seen: &mut FxHashSet<ty::Instance<'tcx>>,
43 recursion_limiter: &mut FxHashMap<DefId, usize>,
136023e0 44 recursion_limit: Limit,
5869c6ff
XL
45 ) -> bool {
46 trace!(%caller);
47 for &(callee, substs) in tcx.mir_inliner_callees(caller.def) {
48 let substs = caller.subst_mir_and_normalize_erasing_regions(tcx, param_env, substs);
49 let callee = match ty::Instance::resolve(tcx, param_env, callee, substs).unwrap() {
50 Some(callee) => callee,
51 None => {
52 trace!(?callee, "cannot resolve, skipping");
53 continue;
54 }
55 };
56
57 // Found a path.
58 if callee.def_id() == target.to_def_id() {
59 return true;
60 }
61
62 if tcx.is_constructor(callee.def_id()) {
63 trace!("constructors always have MIR");
64 // Constructor functions cannot cause a query cycle.
65 continue;
66 }
67
68 match callee.def {
69 InstanceDef::Item(_) => {
70 // If there is no MIR available (either because it was not in metadata or
71 // because it has no MIR because it's an extern function), then the inliner
72 // won't cause cycles on this.
73 if !tcx.is_mir_available(callee.def_id()) {
74 trace!(?callee, "no mir available, skipping");
75 continue;
76 }
77 }
78 // These have no own callable MIR.
79 InstanceDef::Intrinsic(_) | InstanceDef::Virtual(..) => continue,
80 // These have MIR and if that MIR is inlined, substituted and then inlining is run
81 // again, a function item can end up getting inlined. Thus we'll be able to cause
82 // a cycle that way
83 InstanceDef::VtableShim(_)
84 | InstanceDef::ReifyShim(_)
85 | InstanceDef::FnPtrShim(..)
86 | InstanceDef::ClosureOnceShim { .. }
87 | InstanceDef::CloneShim(..) => {}
88 InstanceDef::DropGlue(..) => {
89 // FIXME: A not fully substituted drop shim can cause ICEs if one attempts to
90 // have its MIR built. Likely oli-obk just screwed up the `ParamEnv`s, so this
91 // needs some more analysis.
94222f64 92 if callee.definitely_needs_subst(tcx) {
5869c6ff
XL
93 continue;
94 }
95 }
96 }
97
98 if seen.insert(callee) {
99 let recursion = recursion_limiter.entry(callee.def_id()).or_default();
100 trace!(?callee, recursion = *recursion);
136023e0 101 if recursion_limit.value_within_limit(*recursion) {
5869c6ff
XL
102 *recursion += 1;
103 stack.push(callee);
104 let found_recursion = ensure_sufficient_stack(|| {
136023e0
XL
105 process(
106 tcx,
107 param_env,
108 callee,
109 target,
110 stack,
111 seen,
112 recursion_limiter,
113 recursion_limit,
114 )
5869c6ff
XL
115 });
116 if found_recursion {
117 return true;
118 }
119 stack.pop();
120 } else {
121 // Pessimistically assume that there could be recursion.
122 return true;
123 }
124 }
125 }
126 false
127 }
128 process(
129 tcx,
130 param_env,
131 root,
132 target,
133 &mut Vec::new(),
134 &mut FxHashSet::default(),
135 &mut FxHashMap::default(),
136023e0 136 tcx.recursion_limit(),
5869c6ff
XL
137 )
138}
139
140crate fn mir_inliner_callees<'tcx>(
141 tcx: TyCtxt<'tcx>,
142 instance: ty::InstanceDef<'tcx>,
143) -> &'tcx [(DefId, SubstsRef<'tcx>)] {
144 let steal;
145 let guard;
146 let body = match (instance, instance.def_id().as_local()) {
147 (InstanceDef::Item(_), Some(def_id)) => {
148 let def = ty::WithOptConstParam::unknown(def_id);
149 steal = tcx.mir_promoted(def).0;
150 guard = steal.borrow();
151 &*guard
152 }
153 // Functions from other crates and MIR shims
154 _ => tcx.instance_mir(instance),
155 };
6a06907d 156 let mut calls = SsoHashSet::new();
5869c6ff
XL
157 for bb_data in body.basic_blocks() {
158 let terminator = bb_data.terminator();
159 if let TerminatorKind::Call { func, .. } = &terminator.kind {
160 let ty = func.ty(&body.local_decls, tcx);
161 let call = match ty.kind() {
162 ty::FnDef(def_id, substs) => (*def_id, *substs),
163 _ => continue,
164 };
6a06907d 165 calls.insert(call);
5869c6ff
XL
166 }
167 }
6a06907d 168 tcx.arena.alloc_from_iter(calls.iter().copied())
5869c6ff 169}