1 use rustc_data_structures
::fx
::{FxHashMap, FxHashSet}
;
2 use rustc_data_structures
::stack
::ensure_sufficient_stack
;
3 use rustc_hir
::def_id
::{DefId, LocalDefId}
;
4 use rustc_middle
::mir
::TerminatorKind
;
5 use rustc_middle
::ty
::TypeFoldable
;
6 use rustc_middle
::ty
::{self, subst::SubstsRef, InstanceDef, TyCtxt}
;
8 // FIXME: check whether it is cheaper to precompute the entire call graph instead of invoking
9 // this query riddiculously often.
10 #[instrument(skip(tcx, root, target))]
11 crate fn mir_callgraph_reachable(
13 (root
, target
): (ty
::Instance
<'tcx
>, LocalDefId
),
15 trace
!(%root
, target
= %tcx
.def_path_str(target
.to_def_id()));
16 let param_env
= tcx
.param_env_reveal_all_normalized(target
);
18 root
.def_id().expect_local(),
20 "you should not call `mir_callgraph_reachable` on immediate self recursion"
23 matches
!(root
.def
, InstanceDef
::Item(_
)),
24 "you should not call `mir_callgraph_reachable` on shims"
27 !tcx
.is_constructor(root
.def_id()),
28 "you should not call `mir_callgraph_reachable` on enum/struct constructor functions"
30 #[instrument(skip(tcx, param_env, target, stack, seen, recursion_limiter, caller))]
33 param_env
: ty
::ParamEnv
<'tcx
>,
34 caller
: ty
::Instance
<'tcx
>,
36 stack
: &mut Vec
<ty
::Instance
<'tcx
>>,
37 seen
: &mut FxHashSet
<ty
::Instance
<'tcx
>>,
38 recursion_limiter
: &mut FxHashMap
<DefId
, usize>,
41 for &(callee
, substs
) in tcx
.mir_inliner_callees(caller
.def
) {
42 let substs
= caller
.subst_mir_and_normalize_erasing_regions(tcx
, param_env
, substs
);
43 let callee
= match ty
::Instance
::resolve(tcx
, param_env
, callee
, substs
).unwrap() {
44 Some(callee
) => callee
,
46 trace
!(?callee
, "cannot resolve, skipping");
52 if callee
.def_id() == target
.to_def_id() {
56 if tcx
.is_constructor(callee
.def_id()) {
57 trace
!("constructors always have MIR");
58 // Constructor functions cannot cause a query cycle.
63 InstanceDef
::Item(_
) => {
64 // If there is no MIR available (either because it was not in metadata or
65 // because it has no MIR because it's an extern function), then the inliner
66 // won't cause cycles on this.
67 if !tcx
.is_mir_available(callee
.def_id()) {
68 trace
!(?callee
, "no mir available, skipping");
72 // These have no own callable MIR.
73 InstanceDef
::Intrinsic(_
) | InstanceDef
::Virtual(..) => continue,
74 // These have MIR and if that MIR is inlined, substituted and then inlining is run
75 // again, a function item can end up getting inlined. Thus we'll be able to cause
77 InstanceDef
::VtableShim(_
)
78 | InstanceDef
::ReifyShim(_
)
79 | InstanceDef
::FnPtrShim(..)
80 | InstanceDef
::ClosureOnceShim { .. }
81 | InstanceDef
::CloneShim(..) => {}
82 InstanceDef
::DropGlue(..) => {
83 // FIXME: A not fully substituted drop shim can cause ICEs if one attempts to
84 // have its MIR built. Likely oli-obk just screwed up the `ParamEnv`s, so this
85 // needs some more analysis.
86 if callee
.needs_subst() {
92 if seen
.insert(callee
) {
93 let recursion
= recursion_limiter
.entry(callee
.def_id()).or_default();
94 trace
!(?callee
, recursion
= *recursion
);
95 if tcx
.sess
.recursion_limit().value_within_limit(*recursion
) {
98 let found_recursion
= ensure_sufficient_stack(|| {
99 process(tcx
, param_env
, callee
, target
, stack
, seen
, recursion_limiter
)
106 // Pessimistically assume that there could be recursion.
119 &mut FxHashSet
::default(),
120 &mut FxHashMap
::default(),
124 crate fn mir_inliner_callees
<'tcx
>(
126 instance
: ty
::InstanceDef
<'tcx
>,
127 ) -> &'tcx
[(DefId
, SubstsRef
<'tcx
>)] {
130 let body
= match (instance
, instance
.def_id().as_local()) {
131 (InstanceDef
::Item(_
), Some(def_id
)) => {
132 let def
= ty
::WithOptConstParam
::unknown(def_id
);
133 steal
= tcx
.mir_promoted(def
).0;
134 guard
= steal
.borrow();
137 // Functions from other crates and MIR shims
138 _
=> tcx
.instance_mir(instance
),
140 let mut calls
= Vec
::new();
141 for bb_data
in body
.basic_blocks() {
142 let terminator
= bb_data
.terminator();
143 if let TerminatorKind
::Call { func, .. }
= &terminator
.kind
{
144 let ty
= func
.ty(&body
.local_decls
, tcx
);
145 let call
= match ty
.kind() {
146 ty
::FnDef(def_id
, substs
) => (*def_id
, *substs
),
149 // We've seen this before
150 if calls
.contains(&call
) {
156 tcx
.arena
.alloc_slice(&calls
)