11use rustc_data_structures:: fx:: { FxHashMap , FxHashSet , FxIndexSet } ;
22use rustc_data_structures:: stack:: ensure_sufficient_stack;
3+ use rustc_data_structures:: unord:: UnordSet ;
34use rustc_hir:: def_id:: { DefId , LocalDefId } ;
45use rustc_middle:: mir:: TerminatorKind ;
56use rustc_middle:: ty:: { self , GenericArgsRef , InstanceKind , TyCtxt , TypeVisitableExt } ;
67use rustc_session:: Limit ;
78use rustc_span:: sym;
89use tracing:: { instrument, trace} ;
910
10- // FIXME: check whether it is cheaper to precompute the entire call graph instead of invoking
11- // this query ridiculously often.
12- #[ instrument( level = "debug" , skip( tcx, root, target) ) ]
13- pub ( crate ) fn mir_callgraph_reachable < ' tcx > (
11+ fn should_recurse < ' tcx > ( tcx : TyCtxt < ' tcx > , callee : ty:: Instance < ' tcx > ) -> bool {
12+ match callee. def {
13+ // If there is no MIR available (either because it was not in metadata or
14+ // because it has no MIR because it's an extern function), then the inliner
15+ // won't cause cycles on this.
16+ InstanceKind :: Item ( _) => {
17+ if !tcx. is_mir_available ( callee. def_id ( ) ) {
18+ return false ;
19+ }
20+ }
21+
22+ // These have no own callable MIR.
23+ InstanceKind :: Intrinsic ( _) | InstanceKind :: Virtual ( ..) => return false ,
24+
25+ // These have MIR and if that MIR is inlined, instantiated and then inlining is run
26+ // again, a function item can end up getting inlined. Thus we'll be able to cause
27+ // a cycle that way
28+ InstanceKind :: VTableShim ( _)
29+ | InstanceKind :: ReifyShim ( ..)
30+ | InstanceKind :: FnPtrShim ( ..)
31+ | InstanceKind :: ClosureOnceShim { .. }
32+ | InstanceKind :: ConstructCoroutineInClosureShim { .. }
33+ | InstanceKind :: ThreadLocalShim { .. }
34+ | InstanceKind :: CloneShim ( ..) => { }
35+
36+ // This shim does not call any other functions, thus there can be no recursion.
37+ InstanceKind :: FnPtrAddrShim ( ..) => return false ,
38+
39+ // FIXME: A not fully instantiated drop shim can cause ICEs if one attempts to
40+ // have its MIR built. Likely oli-obk just screwed up the `ParamEnv`s, so this
41+ // needs some more analysis.
42+ InstanceKind :: DropGlue ( ..)
43+ | InstanceKind :: FutureDropPollShim ( ..)
44+ | InstanceKind :: AsyncDropGlue ( ..)
45+ | InstanceKind :: AsyncDropGlueCtorShim ( ..) => {
46+ if callee. has_param ( ) {
47+ return false ;
48+ }
49+ }
50+ }
51+
52+ crate :: pm:: should_run_pass ( tcx, & crate :: inline:: Inline , crate :: pm:: Optimizations :: Allowed )
53+ || crate :: inline:: ForceInline :: should_run_pass_for_callee ( tcx, callee. def . def_id ( ) )
54+ }
55+
56+ #[ instrument(
57+ level = "debug" ,
58+ skip( tcx, typing_env, seen, involved, recursion_limiter, recursion_limit) ,
59+ ret
60+ ) ]
61+ fn process < ' tcx > (
1462 tcx : TyCtxt < ' tcx > ,
15- ( root, target) : ( ty:: Instance < ' tcx > , LocalDefId ) ,
63+ typing_env : ty:: TypingEnv < ' tcx > ,
64+ caller : ty:: Instance < ' tcx > ,
65+ target : LocalDefId ,
66+ seen : & mut FxHashSet < ty:: Instance < ' tcx > > ,
67+ involved : & mut FxHashSet < ty:: Instance < ' tcx > > ,
68+ recursion_limiter : & mut FxHashMap < DefId , usize > ,
69+ recursion_limit : Limit ,
1670) -> bool {
17- trace ! ( %root, target = %tcx. def_path_str( target) ) ;
18- assert_ne ! (
19- root. def_id( ) . expect_local( ) ,
20- target,
21- "you should not call `mir_callgraph_reachable` on immediate self recursion"
22- ) ;
23- assert ! (
24- matches!( root. def, InstanceKind :: Item ( _) ) ,
25- "you should not call `mir_callgraph_reachable` on shims"
26- ) ;
27- assert ! (
28- !tcx. is_constructor( root. def_id( ) ) ,
29- "you should not call `mir_callgraph_reachable` on enum/struct constructor functions"
30- ) ;
31- #[ instrument(
32- level = "debug" ,
33- skip( tcx, typing_env, target, stack, seen, recursion_limiter, caller, recursion_limit)
34- ) ]
35- fn process < ' tcx > (
36- tcx : TyCtxt < ' tcx > ,
37- typing_env : ty:: TypingEnv < ' tcx > ,
38- caller : ty:: Instance < ' tcx > ,
39- target : LocalDefId ,
40- stack : & mut Vec < ty:: Instance < ' tcx > > ,
41- seen : & mut FxHashSet < ty:: Instance < ' tcx > > ,
42- recursion_limiter : & mut FxHashMap < DefId , usize > ,
43- recursion_limit : Limit ,
44- ) -> bool {
45- trace ! ( %caller) ;
46- for & ( callee, args) in tcx. mir_inliner_callees ( caller. def ) {
47- let Ok ( args) = caller. try_instantiate_mir_and_normalize_erasing_regions (
48- tcx,
49- typing_env,
50- ty:: EarlyBinder :: bind ( args) ,
51- ) else {
52- trace ! ( ?caller, ?typing_env, ?args, "cannot normalize, skipping" ) ;
53- continue ;
54- } ;
55- let Ok ( Some ( callee) ) = ty:: Instance :: try_resolve ( tcx, typing_env, callee, args) else {
56- trace ! ( ?callee, "cannot resolve, skipping" ) ;
57- continue ;
58- } ;
71+ trace ! ( %caller) ;
72+ let mut cycle_found = false ;
5973
60- // Found a path.
61- if callee. def_id ( ) == target. to_def_id ( ) {
62- return true ;
63- }
74+ for & ( callee, args) in tcx. mir_inliner_callees ( caller. def ) {
75+ let Ok ( args) = caller. try_instantiate_mir_and_normalize_erasing_regions (
76+ tcx,
77+ typing_env,
78+ ty:: EarlyBinder :: bind ( args) ,
79+ ) else {
80+ trace ! ( ?caller, ?typing_env, ?args, "cannot normalize, skipping" ) ;
81+ continue ;
82+ } ;
83+ let Ok ( Some ( callee) ) = ty:: Instance :: try_resolve ( tcx, typing_env, callee, args) else {
84+ trace ! ( ?callee, "cannot resolve, skipping" ) ;
85+ continue ;
86+ } ;
6487
65- if tcx. is_constructor ( callee. def_id ( ) ) {
66- trace ! ( "constructors always have MIR" ) ;
67- // Constructor functions cannot cause a query cycle.
68- continue ;
69- }
88+ // Found a path.
89+ if callee. def_id ( ) == target. to_def_id ( ) {
90+ cycle_found = true ;
91+ }
7092
71- match callee. def {
72- InstanceKind :: Item ( _) => {
73- // If there is no MIR available (either because it was not in metadata or
74- // because it has no MIR because it's an extern function), then the inliner
75- // won't cause cycles on this.
76- if !tcx. is_mir_available ( callee. def_id ( ) ) {
77- trace ! ( ?callee, "no mir available, skipping" ) ;
78- continue ;
79- }
80- }
81- // These have no own callable MIR.
82- InstanceKind :: Intrinsic ( _) | InstanceKind :: Virtual ( ..) => continue ,
83- // These have MIR and if that MIR is inlined, instantiated and then inlining is run
84- // again, a function item can end up getting inlined. Thus we'll be able to cause
85- // a cycle that way
86- InstanceKind :: VTableShim ( _)
87- | InstanceKind :: ReifyShim ( ..)
88- | InstanceKind :: FnPtrShim ( ..)
89- | InstanceKind :: ClosureOnceShim { .. }
90- | InstanceKind :: ConstructCoroutineInClosureShim { .. }
91- | InstanceKind :: ThreadLocalShim { .. }
92- | InstanceKind :: CloneShim ( ..) => { }
93-
94- // This shim does not call any other functions, thus there can be no recursion.
95- InstanceKind :: FnPtrAddrShim ( ..) => {
96- continue ;
97- }
98- InstanceKind :: DropGlue ( ..)
99- | InstanceKind :: FutureDropPollShim ( ..)
100- | InstanceKind :: AsyncDropGlue ( ..)
101- | InstanceKind :: AsyncDropGlueCtorShim ( ..) => {
102- // FIXME: A not fully instantiated drop shim can cause ICEs if one attempts to
103- // have its MIR built. Likely oli-obk just screwed up the `ParamEnv`s, so this
104- // needs some more analysis.
105- if callee. has_param ( ) {
106- continue ;
107- }
108- }
109- }
93+ if tcx. is_constructor ( callee. def_id ( ) ) {
94+ trace ! ( "constructors always have MIR" ) ;
95+ // Constructor functions cannot cause a query cycle.
96+ continue ;
97+ }
11098
111- if seen. insert ( callee) {
112- let recursion = recursion_limiter. entry ( callee. def_id ( ) ) . or_default ( ) ;
113- trace ! ( ?callee, recursion = * recursion) ;
114- if recursion_limit. value_within_limit ( * recursion) {
115- * recursion += 1 ;
116- stack. push ( callee) ;
117- let found_recursion = ensure_sufficient_stack ( || {
118- process (
119- tcx,
120- typing_env,
121- callee,
122- target,
123- stack,
124- seen,
125- recursion_limiter,
126- recursion_limit,
127- )
128- } ) ;
129- if found_recursion {
130- return true ;
131- }
132- stack. pop ( ) ;
133- } else {
134- // Pessimistically assume that there could be recursion.
135- return true ;
136- }
99+ if !should_recurse ( tcx, callee) {
100+ continue ;
101+ }
102+
103+ if seen. insert ( callee) {
104+ let recursion = recursion_limiter. entry ( callee. def_id ( ) ) . or_default ( ) ;
105+ trace ! ( ?callee, recursion = * recursion) ;
106+ let found_recursion = if recursion_limit. value_within_limit ( * recursion) {
107+ * recursion += 1 ;
108+ ensure_sufficient_stack ( || {
109+ process (
110+ tcx,
111+ typing_env,
112+ callee,
113+ target,
114+ seen,
115+ involved,
116+ recursion_limiter,
117+ recursion_limit,
118+ )
119+ } )
120+ } else {
121+ // Pessimistically assume that there could be recursion.
122+ true
123+ } ;
124+ if found_recursion {
125+ involved. insert ( callee) ;
126+ cycle_found = true ;
137127 }
138128 }
139- false
140129 }
130+
131+ cycle_found
132+ }
133+
134+ #[ instrument( level = "debug" , skip( tcx) , ret) ]
135+ pub ( crate ) fn mir_callgraph_cyclic < ' tcx > (
136+ tcx : TyCtxt < ' tcx > ,
137+ root : LocalDefId ,
138+ ) -> UnordSet < ty:: Instance < ' tcx > > {
139+ assert ! (
140+ !tcx. is_constructor( root. to_def_id( ) ) ,
141+ "you should not call `mir_callgraph_reachable` on enum/struct constructor functions"
142+ ) ;
143+
141144 // FIXME(-Znext-solver=no): Remove this hack when trait solver overflow can return an error.
142145 // In code like that pointed out in #128887, the type complexity we ask the solver to deal with
143146 // grows as we recurse into the call graph. If we use the same recursion limit here and in the
@@ -146,16 +149,32 @@ pub(crate) fn mir_callgraph_reachable<'tcx>(
146149 // the default recursion limits are quite generous for us. If we need to recurse 64 times
147150 // into the call graph, we're probably not going to find any useful MIR inlining.
148151 let recursion_limit = tcx. recursion_limit ( ) / 2 ;
152+ let mut involved = FxHashSet :: default ( ) ;
153+ let typing_env = ty:: TypingEnv :: post_analysis ( tcx, root) ;
154+ let Ok ( Some ( root_instance) ) = ty:: Instance :: try_resolve (
155+ tcx,
156+ typing_env,
157+ root. to_def_id ( ) ,
158+ ty:: GenericArgs :: identity_for_item ( tcx, root. to_def_id ( ) ) ,
159+ ) else {
160+ trace ! ( "cannot resolve, skipping" ) ;
161+ return involved. into ( ) ;
162+ } ;
163+ if !should_recurse ( tcx, root_instance) {
164+ trace ! ( "cannot walk, skipping" ) ;
165+ return involved. into ( ) ;
166+ }
149167 process (
150168 tcx,
151- ty:: TypingEnv :: post_analysis ( tcx, target) ,
169+ typing_env,
170+ root_instance,
152171 root,
153- target,
154- & mut Vec :: new ( ) ,
155172 & mut FxHashSet :: default ( ) ,
173+ & mut involved,
156174 & mut FxHashMap :: default ( ) ,
157175 recursion_limit,
158- )
176+ ) ;
177+ involved. into ( )
159178}
160179
161180pub ( crate ) fn mir_inliner_callees < ' tcx > (
0 commit comments