1
1
use rustc_data_structures:: fx:: { FxHashMap , FxHashSet , FxIndexSet } ;
2
2
use rustc_data_structures:: stack:: ensure_sufficient_stack;
3
+ use rustc_data_structures:: unord:: UnordSet ;
3
4
use rustc_hir:: def_id:: { DefId , LocalDefId } ;
4
5
use rustc_middle:: mir:: TerminatorKind ;
5
6
use rustc_middle:: ty:: { self , GenericArgsRef , InstanceKind , TyCtxt , TypeVisitableExt } ;
6
7
use rustc_session:: Limit ;
7
8
use rustc_span:: sym;
8
9
use tracing:: { instrument, trace} ;
9
10
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 > (
14
62
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 ,
16
70
) -> 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 ;
59
73
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
+ } ;
64
87
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
+ }
70
92
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
+ }
110
98
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 ;
137
127
}
138
128
}
139
- false
140
129
}
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
+
141
144
// FIXME(-Znext-solver=no): Remove this hack when trait solver overflow can return an error.
142
145
// In code like that pointed out in #128887, the type complexity we ask the solver to deal with
143
146
// 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>(
146
149
// the default recursion limits are quite generous for us. If we need to recurse 64 times
147
150
// into the call graph, we're probably not going to find any useful MIR inlining.
148
151
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
+ }
149
167
process (
150
168
tcx,
151
- ty:: TypingEnv :: post_analysis ( tcx, target) ,
169
+ typing_env,
170
+ root_instance,
152
171
root,
153
- target,
154
- & mut Vec :: new ( ) ,
155
172
& mut FxHashSet :: default ( ) ,
173
+ & mut involved,
156
174
& mut FxHashMap :: default ( ) ,
157
175
recursion_limit,
158
- )
176
+ ) ;
177
+ involved. into ( )
159
178
}
160
179
161
180
pub ( crate ) fn mir_inliner_callees < ' tcx > (
0 commit comments