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