]>
Commit | Line | Data |
---|---|---|
5869c6ff | 1 | use rustc_data_structures::fx::{FxHashMap, FxHashSet}; |
6a06907d | 2 | use rustc_data_structures::sso::SsoHashSet; |
5869c6ff XL |
3 | use rustc_data_structures::stack::ensure_sufficient_stack; |
4 | use rustc_hir::def_id::{DefId, LocalDefId}; | |
5 | use rustc_middle::mir::TerminatorKind; | |
6 | use rustc_middle::ty::TypeFoldable; | |
7 | use rustc_middle::ty::{self, subst::SubstsRef, InstanceDef, TyCtxt}; | |
136023e0 | 8 | use 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))] |
5869c6ff XL |
13 | crate fn mir_callgraph_reachable( |
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 | )] |
5869c6ff XL |
36 | fn process( |
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. | |
92 | if callee.needs_subst() { | |
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 | ||
140 | crate 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 | } |