@@ -38,7 +38,76 @@ function propagate_conditional(rt::InterConditional, cond::Conditional)
38
38
return Conditional (cond. slot, new_thentype, new_elsetype)
39
39
end
40
40
41
- function abstract_call_gf_by_type (interp:: AbstractInterpreter , @nospecialize (f),
41
+ mutable struct SafeBox{T}
42
+ x:: T
43
+ SafeBox {T} (x:: T ) where T = new {T} (x)
44
+ SafeBox (@nospecialize x) = new {Any} (x)
45
+ end
46
+ getindex (box:: SafeBox ) = box. x
47
+ setindex! (box:: SafeBox{T} , x:: T ) where T = setfield! (box, :x , x)
48
+
49
+ struct FailedMethodMatch
50
+ reason:: String
51
+ end
52
+
53
+ struct MethodMatchTarget
54
+ match:: MethodMatch
55
+ edges:: Vector{Union{Nothing,CodeInstance}}
56
+ edge_idx:: Int
57
+ end
58
+
59
+ struct MethodMatches
60
+ applicable:: Vector{MethodMatchTarget}
61
+ info:: MethodMatchInfo
62
+ valid_worlds:: WorldRange
63
+ end
64
+ any_ambig (result:: MethodLookupResult ) = result. ambig
65
+ any_ambig (info:: MethodMatchInfo ) = any_ambig (info. results)
66
+ any_ambig (m:: MethodMatches ) = any_ambig (m. info)
67
+ fully_covering (info:: MethodMatchInfo ) = info. fullmatch
68
+ fully_covering (m:: MethodMatches ) = fully_covering (m. info)
69
+
70
+ struct UnionSplitMethodMatches
71
+ applicable:: Vector{MethodMatchTarget}
72
+ applicable_argtypes:: Vector{Vector{Any}}
73
+ info:: UnionSplitInfo
74
+ valid_worlds:: WorldRange
75
+ end
76
+ any_ambig (info:: UnionSplitInfo ) = any (any_ambig, info. split)
77
+ any_ambig (m:: UnionSplitMethodMatches ) = any_ambig (m. info)
78
+ fully_covering (info:: UnionSplitInfo ) = all (fully_covering, info. split)
79
+ fully_covering (m:: UnionSplitMethodMatches ) = fully_covering (m. info)
80
+
81
+ nmatches (info:: MethodMatchInfo ) = length (info. results)
82
+ function nmatches (info:: UnionSplitInfo )
83
+ n = 0
84
+ for mminfo in info. split
85
+ n += nmatches (mminfo)
86
+ end
87
+ return n
88
+ end
89
+
90
+ # intermediate state for computing gfresult
91
+ mutable struct CallInferenceState
92
+ inferidx:: Int
93
+ rettype
94
+ exctype
95
+ all_effects:: Effects
96
+ const_results:: Union{Nothing,Vector{Union{Nothing,ConstResult}}} # keeps the results of inference with the extended lattice elements (if happened)
97
+ conditionals:: Union{Nothing,Tuple{Vector{Any},Vector{Any}}} # keeps refinement information of call argument types when the return type is boolean
98
+ slotrefinements:: Union{Nothing,Vector{Any}} # keeps refinement information on slot types obtained from call signature
99
+
100
+ # some additional fields for untyped objects (just to avoid capturing)
101
+ func
102
+ matches:: Union{MethodMatches,UnionSplitMethodMatches}
103
+ function CallInferenceState (@nospecialize (func), matches:: Union{MethodMatches,UnionSplitMethodMatches} )
104
+ return new (#= inferidx=# 1 , #= rettype=# Bottom, #= exctype=# Bottom, #= all_effects=# EFFECTS_TOTAL,
105
+ #= const_results=# nothing , #= conditionals=# nothing , #= slotrefinements=# nothing ,
106
+ func, matches)
107
+ end
108
+ end
109
+
110
+ function abstract_call_gf_by_type (interp:: AbstractInterpreter , @nospecialize (func),
42
111
arginfo:: ArgInfo , si:: StmtInfo , @nospecialize (atype),
43
112
sv:: AbsIntState , max_methods:: Int )
44
113
𝕃ₚ, 𝕃ᵢ = ipo_lattice (interp), typeinf_lattice (interp)
@@ -50,12 +119,12 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
50
119
return Future (CallMeta (Any, Any, Effects (), NoCallInfo ()))
51
120
end
52
121
53
- (; valid_worlds, applicable, info ) = matches
122
+ (; valid_worlds, applicable) = matches
54
123
update_valid_age! (sv, valid_worlds) # need to record the negative world now, since even if we don't generate any useful information, inlining might want to add an invoke edge and it won't have this information anymore
55
124
if bail_out_toplevel_call (interp, sv)
56
- napplicable = length (applicable)
125
+ local napplicable = length (applicable)
57
126
for i = 1 : napplicable
58
- sig = applicable[i]. match. spec_types
127
+ local sig = applicable[i]. match. spec_types
59
128
if ! isdispatchtuple (sig)
60
129
# only infer fully concrete call sites in top-level expressions (ignoring even isa_compileable_sig matches)
61
130
add_remark! (interp, sv, " Refusing to infer non-concrete call site in top-level expression" )
@@ -66,26 +135,17 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
66
135
67
136
# final result
68
137
gfresult = Future {CallMeta} ()
69
- # intermediate work for computing gfresult
70
- rettype = exctype = Bottom
71
- conditionals = nothing # keeps refinement information of call argument types when the return type is boolean
72
- const_results = nothing # or const_results::Vector{Union{Nothing,ConstResult}} if any const results are available
73
- fargs = arginfo. fargs
74
- all_effects = EFFECTS_TOTAL
75
- slotrefinements = nothing # keeps refinement information on slot types obtained from call signature
138
+ state = CallInferenceState (func, matches)
76
139
77
140
# split the for loop off into a function, so that we can pause and restart it at will
78
- i:: Int = 1
79
- f = Core. Box (f)
80
- atype = Core. Box (atype)
81
141
function infercalls (interp, sv)
82
142
local napplicable = length (applicable)
83
143
local multiple_matches = napplicable > 1
84
- while i <= napplicable
85
- (; match, edges, edge_idx) = applicable[i ]
86
- method = match. method
87
- sig = match. spec_types
88
- if bail_out_call (interp, InferenceLoopState (rettype, all_effects), sv)
144
+ while state . inferidx <= napplicable
145
+ (; match, edges, edge_idx) = applicable[state . inferidx ]
146
+ local method = match. method
147
+ local sig = match. spec_types
148
+ if bail_out_call (interp, InferenceLoopState (state . rettype, state . all_effects), sv)
89
149
add_remark! (interp, sv, " Call inference reached maximally imprecise information: bailing on doing more abstract inference." )
90
150
break
91
151
end
@@ -108,10 +168,11 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
108
168
this_exct = exct
109
169
# try constant propagation with argtypes for this match
110
170
# this is in preparation for inlining, or improving the return result
111
- this_argtypes = isa (matches, MethodMatches) ? argtypes : matches. applicable_argtypes[i]
112
- this_arginfo = ArgInfo (fargs, this_argtypes)
171
+ local matches = state. matches
172
+ this_argtypes = isa (matches, MethodMatches) ? argtypes : matches. applicable_argtypes[state. inferidx]
173
+ this_arginfo = ArgInfo (arginfo. fargs, this_argtypes)
113
174
const_call_result = abstract_call_method_with_const_args (interp,
114
- mresult[], f . contents , this_arginfo, si, match, sv)
175
+ mresult[], state . func , this_arginfo, si, match, sv)
115
176
const_result = volatile_inf_result
116
177
if const_call_result != = nothing
117
178
this_const_conditional = ignorelimited (const_call_result. rt)
@@ -146,12 +207,13 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
146
207
end
147
208
end
148
209
149
- all_effects = merge_effects (all_effects, effects)
210
+ state . all_effects = merge_effects (state . all_effects, effects)
150
211
if const_result != = nothing
212
+ local const_results = state. const_results
151
213
if const_results === nothing
152
- const_results = fill! (Vector {Union{Nothing,ConstResult}} (undef, napplicable), nothing )
214
+ const_results = state . const_results = fill! (Vector {Union{Nothing,ConstResult}} (undef, napplicable), nothing )
153
215
end
154
- const_results[i ] = const_result
216
+ const_results[state . inferidx ] = const_result
155
217
end
156
218
@assert ! (this_conditional isa Conditional || this_rt isa MustAlias) " invalid lattice element returned from inter-procedural context"
157
219
if can_propagate_conditional (this_conditional, argtypes)
@@ -162,12 +224,14 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
162
224
this_rt = this_conditional
163
225
end
164
226
165
- rettype = rettype ⊔ ₚ this_rt
166
- exctype = exctype ⊔ ₚ this_exct
167
- if has_conditional (𝕃ₚ, sv) && this_conditional != = Bottom && is_lattice_bool (𝕃ₚ, rettype) && fargs != = nothing
227
+ state. rettype = state. rettype ⊔ ₚ this_rt
228
+ state. exctype = state. exctype ⊔ ₚ this_exct
229
+ if has_conditional (𝕃ₚ, sv) && this_conditional != = Bottom && is_lattice_bool (𝕃ₚ, state. rettype) && arginfo. fargs != = nothing
230
+ local conditionals = state. conditionals
168
231
if conditionals === nothing
169
- conditionals = Any[Bottom for _ in 1 : length (argtypes)],
170
- Any[Bottom for _ in 1 : length (argtypes)]
232
+ conditionals = state. conditionals = (
233
+ Any[Bottom for _ in 1 : length (argtypes)],
234
+ Any[Bottom for _ in 1 : length (argtypes)])
171
235
end
172
236
for i = 1 : length (argtypes)
173
237
cnd = conditional_argtype (𝕃ᵢ, this_conditional, match. spec_types, argtypes, i)
@@ -177,7 +241,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
177
241
end
178
242
edges[edge_idx] = edge
179
243
180
- i += 1
244
+ state . inferidx += 1
181
245
return true
182
246
end # function handle1
183
247
if isready (mresult) && handle1 (interp, sv)
@@ -188,30 +252,33 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
188
252
end
189
253
end # while
190
254
191
- seenall = i > napplicable
255
+ seenall = state. inferidx > napplicable
256
+ retinfo = state. matches. info
192
257
if seenall # small optimization to skip some work that is already implied
258
+ local const_results = state. const_results
193
259
if const_results != = nothing
194
- @assert napplicable == nmatches (info ) == length (const_results)
195
- info = ConstCallInfo (info , const_results)
260
+ @assert napplicable == nmatches (retinfo ) == length (const_results)
261
+ retinfo = ConstCallInfo (retinfo , const_results)
196
262
end
197
- if ! fully_covering (matches) || any_ambig (matches)
263
+ if ! fully_covering (state . matches) || any_ambig (state . matches)
198
264
# Account for the fact that we may encounter a MethodError with a non-covered or ambiguous signature.
199
- all_effects = Effects (all_effects; nothrow= false )
200
- exctype = exctype ⊔ ₚ MethodError
265
+ state . all_effects = Effects (state . all_effects; nothrow= false )
266
+ state . exctype = state . exctype ⊔ ₚ MethodError
201
267
end
268
+ local fargs = arginfo. fargs
202
269
if sv isa InferenceState && fargs != = nothing
203
- slotrefinements = collect_slot_refinements (𝕃ᵢ, applicable, argtypes, fargs, sv)
270
+ state . slotrefinements = collect_slot_refinements (𝕃ᵢ, applicable, argtypes, fargs, sv)
204
271
end
205
- rettype = from_interprocedural! (interp, rettype, sv, arginfo, conditionals)
206
- if call_result_unused (si) && ! (rettype === Bottom)
272
+ state . rettype = from_interprocedural! (interp, state . rettype, sv, arginfo, state . conditionals)
273
+ if call_result_unused (si) && ! (state . rettype === Bottom)
207
274
add_remark! (interp, sv, " Call result type was widened because the return value is unused" )
208
275
# We're mainly only here because the optimizer might want this code,
209
276
# but we ourselves locally don't typically care about it locally
210
277
# (beyond checking if it always throws).
211
278
# So avoid adding an edge, since we don't want to bother attempting
212
279
# to improve our result even if it does change (to always throw),
213
280
# and avoid keeping track of a more complex result type.
214
- rettype = Any
281
+ state . rettype = Any
215
282
end
216
283
# if from_interprocedural added any pclimitations to the set inherited from the arguments,
217
284
# some of those may be part of our cycles, so those can be deleted now
@@ -230,23 +297,24 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
230
297
end
231
298
else
232
299
# there is unanalyzed candidate, widen type and effects to the top
233
- rettype = exctype = Any
234
- all_effects = Effects ()
235
- const_results = nothing
300
+ state . rettype = state . exctype = Any
301
+ state . all_effects = Effects ()
302
+ state . const_results = nothing
236
303
end
237
304
238
305
# Also considering inferring the compilation signature for this method, so
239
306
# it is available to the compiler in case it ends up needing it for the invoke.
240
- if isa (sv, InferenceState) && infer_compilation_signature (interp) && (! is_removable_if_unused (all_effects) || ! call_result_unused (si))
241
- i = 1
307
+ if (isa (sv, InferenceState) && infer_compilation_signature (interp) &&
308
+ (! is_removable_if_unused (state. all_effects) || ! call_result_unused (si)))
309
+ inferidx = SafeBox {Int} (1 )
242
310
function infercalls2 (interp, sv)
243
311
local napplicable = length (applicable)
244
312
local multiple_matches = napplicable > 1
245
- while i <= napplicable
246
- (; match, edges, edge_idx) = applicable[i ]
247
- i += 1
248
- method = match. method
249
- sig = match. spec_types
313
+ while inferidx[] <= napplicable
314
+ (; match, edges, edge_idx) = applicable[inferidx[] ]
315
+ inferidx[] += 1
316
+ local method = match. method
317
+ local sig = match. spec_types
250
318
mi = specialize_method (match; preexisting= true )
251
319
if mi === nothing || ! const_prop_methodinstance_heuristic (interp, mi, arginfo, sv)
252
320
csig = get_compileable_sig (method, sig, match. sparams)
@@ -265,55 +333,14 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
265
333
infercalls2 (interp, sv) || push! (sv. tasks, infercalls2)
266
334
end
267
335
268
- gfresult[] = CallMeta (rettype, exctype, all_effects, info, slotrefinements)
336
+ gfresult[] = CallMeta (state . rettype, state . exctype, state . all_effects, retinfo, state . slotrefinements)
269
337
return true
270
338
end # function infercalls
271
339
# start making progress on the first call
272
340
infercalls (interp, sv) || push! (sv. tasks, infercalls)
273
341
return gfresult
274
342
end
275
343
276
- struct FailedMethodMatch
277
- reason:: String
278
- end
279
-
280
- struct MethodMatchTarget
281
- match:: MethodMatch
282
- edges:: Vector{Union{Nothing,CodeInstance}}
283
- edge_idx:: Int
284
- end
285
-
286
- struct MethodMatches
287
- applicable:: Vector{MethodMatchTarget}
288
- info:: MethodMatchInfo
289
- valid_worlds:: WorldRange
290
- end
291
- any_ambig (result:: MethodLookupResult ) = result. ambig
292
- any_ambig (info:: MethodMatchInfo ) = any_ambig (info. results)
293
- any_ambig (m:: MethodMatches ) = any_ambig (m. info)
294
- fully_covering (info:: MethodMatchInfo ) = info. fullmatch
295
- fully_covering (m:: MethodMatches ) = fully_covering (m. info)
296
-
297
- struct UnionSplitMethodMatches
298
- applicable:: Vector{MethodMatchTarget}
299
- applicable_argtypes:: Vector{Vector{Any}}
300
- info:: UnionSplitInfo
301
- valid_worlds:: WorldRange
302
- end
303
- any_ambig (info:: UnionSplitInfo ) = any (any_ambig, info. split)
304
- any_ambig (m:: UnionSplitMethodMatches ) = any_ambig (m. info)
305
- fully_covering (info:: UnionSplitInfo ) = all (fully_covering, info. split)
306
- fully_covering (m:: UnionSplitMethodMatches ) = fully_covering (m. info)
307
-
308
- nmatches (info:: MethodMatchInfo ) = length (info. results)
309
- function nmatches (info:: UnionSplitInfo )
310
- n = 0
311
- for mminfo in info. split
312
- n += nmatches (mminfo)
313
- end
314
- return n
315
- end
316
-
317
344
function find_method_matches (interp:: AbstractInterpreter , argtypes:: Vector{Any} , @nospecialize (atype);
318
345
max_union_splitting:: Int = InferenceParams (interp). max_union_splitting,
319
346
max_methods:: Int = InferenceParams (interp). max_methods)
0 commit comments