• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JuliaLang / julia / 1293

03 Oct 2025 01:05AM UTC coverage: 76.952% (-0.1%) from 77.068%
1293

push

buildkite

web-flow
Support superscript small q (#59544)

Co-authored-by: Steven G. Johnson <stevenj@alum.mit.edu>

61282 of 79637 relevant lines covered (76.95%)

21700458.33 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.76
/Compiler/src/typeinfer.jl
1
# This file is a part of Julia. License is MIT: https://julialang.org/license
2

3
"""
4
The module `Core.Compiler.Timings` provides a simple implementation of nested timers that
5
can be used to measure the exclusive time spent inferring each method instance that is
6
recursively inferred during type inference.
7

8
This is meant to be internal to the compiler, and makes some specific assumptions about
9
being used for this purpose alone.
10
"""
11
module Timings
12

13
using ..Core
14
using ..Compiler: -, +, :, Vector, length, first, empty!, push!, pop!, @inline,
15
    @inbounds, copy, backtrace, _time_ns
16

17
# What we record for any given frame we infer during type inference.
18
struct InferenceFrameInfo
19
    mi::Core.MethodInstance
20
    world::UInt64
21
    sptypes::Vector{Compiler.VarState}
22
    slottypes::Vector{Any}
23
    nargs::Int
24
end
25

26
function _typeinf_identifier(frame::Compiler.InferenceState)
×
27
    mi_info = InferenceFrameInfo(
×
28
        frame.linfo,
29
        frame_world(sv),
30
        copy(frame.sptypes),
31
        copy(frame.slottypes),
32
        length(frame.result.argtypes),
33
    )
34
    return mi_info
×
35
end
36

37
_typeinf_identifier(frame::InferenceFrameInfo) = frame
×
38

39
"""
40
    Compiler.Timing(mi_info, start_time, ...)
41

42
Internal type containing the timing result for running type inference on a single
43
MethodInstance.
44
"""
45
struct Timing
46
    mi_info::InferenceFrameInfo
47
    start_time::UInt64
48
    cur_start_time::UInt64
49
    time::UInt64
50
    children::Core.Array{Timing,1}
51
    bt         # backtrace collected upon initial entry to typeinf
52
end
53
Timing(mi_info, start_time, cur_start_time, time, children) = Timing(mi_info, start_time, cur_start_time, time, children, nothing)
×
54
Timing(mi_info, start_time) = Timing(mi_info, start_time, start_time, UInt64(0), Timing[])
×
55

56
# We keep a stack of the Timings for each of the MethodInstances currently being timed.
57
# Since type inference currently operates via a depth-first search (during abstract
58
# evaluation), this vector operates like a call stack. The last node in _timings is the
59
# node currently being inferred, and its parent is directly before it, etc.
60
# Each Timing also contains its own vector for all of its children, so that the tree
61
# call structure through type inference is recorded. (It's recorded as a tree, not a graph,
62
# because we create a new node for duplicates.)
63
const _timings = Timing[]
64
# ROOT() is an empty function used as the top-level Timing node to measure all time spent
65
# *not* in type inference during a given recording trace. It is used as a "dummy" node.
66
function ROOT() end
×
67
const ROOTmi = Compiler.specialize_method(
68
    first(Compiler.methods(ROOT)), Tuple{typeof(ROOT)}, Core.svec())
69
"""
70
    Compiler.reset_timings()
71

72
Empty out the previously recorded type inference timings (`Compiler._timings`), and
73
start the ROOT() timer again. `ROOT()` measures all time spent _outside_ inference.
74
"""
75
function reset_timings() end
×
76
push!(_timings, Timing(
77
    # The MethodInstance for ROOT(), and default empty values for other fields.
78
    InferenceFrameInfo(ROOTmi, 0x0, Compiler.VarState[], Any[Core.Const(ROOT)], 1),
79
    _time_ns()))
80
function close_current_timer() end
×
81
function enter_new_timer(frame) end
×
82
function exit_current_timer(_expected_frame_) end
×
83

84
end  # module Timings
85

86
"""
87
    Compiler.__set_measure_typeinf(onoff::Bool)
88

89
If set to `true`, record per-method-instance timings within type inference in the Compiler.
90
"""
91
__set_measure_typeinf(onoff::Bool) = __measure_typeinf__[] = onoff
×
92
const __measure_typeinf__ = RefValue{Bool}(false)
93

94
function result_edges(::AbstractInterpreter, caller::InferenceState)
95
    result = caller.result
307,145✔
96
    opt = result.src
307,145✔
97
    if isa(opt, OptimizationState)
307,145✔
98
        return Core.svec(opt.inlining.edges...)
134,323✔
99
    else
100
        return Core.svec(caller.edges...)
172,822✔
101
    end
102
end
103

104
function finish!(interp::AbstractInterpreter, caller::InferenceState, validation_world::UInt, time_before::UInt64)
307,145✔
105
    result = caller.result
307,145✔
106
    edges = result_edges(interp, caller)
479,967✔
107
    #@assert last(result.valid_worlds) <= get_world_counter() || isempty(edges)
108
    if caller.cache_mode === CACHE_MODE_LOCAL
307,145✔
109
        @assert !isdefined(result, :ci)
176,755✔
110
        result.src = transform_result_for_local_cache(interp, result, edges)
353,510✔
111
    elseif isdefined(result, :ci)
130,390✔
112
        ci = result.ci
128,527✔
113
        mi = result.linfo
128,527✔
114
        # if we aren't cached, we don't need this edge
115
        # but our caller might, so let's just make it anyways
116
        if last(result.valid_worlds) >= validation_world
128,527✔
117
            # if we can record all of the backedges in the global reverse-cache,
118
            # we can now widen our applicability in the global cache too
119
            store_backedges(ci, edges)
128,527✔
120
        end
121
        inferred_result = nothing
128,527✔
122
        uncompressed = result.src
128,527✔
123
        const_flag = is_result_constabi_eligible(result)
151,482✔
124
        debuginfo = nothing
128,527✔
125
        discard_src = caller.cache_mode === CACHE_MODE_NULL || const_flag
128,527✔
126
        if !discard_src
128,527✔
127
            inferred_result = transform_result_for_cache(interp, result, edges)
126,499✔
128
            if inferred_result !== nothing
126,499✔
129
                uncompressed = inferred_result
65,874✔
130
                debuginfo = get_debuginfo(inferred_result)
65,874✔
131
                # Inlining may fast-path the global cache via `VolatileInferenceResult`, so store it back here
132
                result.src = inferred_result
65,874✔
133
            else
134
                if isa(result.src, OptimizationState)
60,625✔
135
                    debuginfo = get_debuginfo(ir_to_codeinf!(result.src))
3,209✔
136
                elseif isa(result.src, CodeInfo)
57,416✔
137
                    debuginfo = get_debuginfo(result.src)
57,416✔
138
                end
139
            end
140
            # TODO: do we want to augment edges here with any :invoke targets that we got from inlining (such that we didn't have a direct edge to it already)?
141
            if inferred_result isa CodeInfo
126,499✔
142
                if may_compress(interp)
65,142✔
143
                    nslots = length(inferred_result.slotflags)
65,142✔
144
                    resize!(inferred_result.slottypes::Vector{Any}, nslots)
65,142✔
145
                    resize!(inferred_result.slotnames, nslots)
65,142✔
146
                end
147
                inferred_result = maybe_compress_codeinfo(interp, mi, inferred_result)
111,575✔
148
                result.is_src_volatile = false
65,142✔
149
            elseif ci.owner === nothing
61,357✔
150
                # The global cache can only handle objects that codegen understands
151
                inferred_result = nothing
2,429✔
152
            end
153
        end
154
        if debuginfo === nothing
128,527✔
155
            debuginfo = DebugInfo(mi)
2,760✔
156
        end
157
        min_world, max_world = first(result.valid_worlds), last(result.valid_worlds)
128,527✔
158
        ipo_effects = encode_effects(result.ipo_effects)
128,527✔
159
        time_now = _time_ns()
128,527✔
160
        time_self_ns = caller.time_self_ns + (time_now - time_before)
128,527✔
161
        time_total = (time_now - caller.time_start - caller.time_paused) * 1e-9
128,527✔
162
        ccall(:jl_update_codeinst, Cvoid, (Any, Any, Int32, UInt, UInt, UInt32, Any, Float64, Float64, Float64, Any, Any),
128,527✔
163
            ci, inferred_result, const_flag, min_world, max_world, ipo_effects,
164
            result.analysis_results, time_total, caller.time_caches, time_self_ns * 1e-9, debuginfo, edges)
165
        if is_cached(caller) # CACHE_MODE_GLOBAL
128,527✔
166
            cache_result!(interp, result, ci)
128,468✔
167
        end
168
        engine_reject(interp, ci)
128,527✔
169
        codegen = codegen_cache(interp)
128,527✔
170
        if !discard_src && codegen !== nothing && (isa(uncompressed, CodeInfo) || isa(uncompressed, OptimizationState))
130,983✔
171
            if isa(uncompressed, OptimizationState)
10,879✔
172
                uncompressed = ir_to_codeinf!(uncompressed, edges)
2,456✔
173
            end
174
            # record that the caller could use this result to generate code when required, if desired, to avoid repeating n^2 work
175
            codegen[ci] = uncompressed
10,879✔
176
            if bootstrapping_compiler && inferred_result == nothing
10,879✔
177
                # This is necessary to get decent bootstrapping performance
178
                # when compiling the compiler to inject everything eagerly
179
                # where codegen can start finding and using it right away
180
                if mi.def isa Method && isa_compileable_sig(mi) && is_cached(caller)
×
181
                    ccall(:jl_add_codeinst_to_jit, Cvoid, (Any, Any), ci, uncompressed)
×
182
                end
183
            end
184
        end
185
    end
186
    return nothing
307,145✔
187
end
188

189
function cache_result!(interp::AbstractInterpreter, result::InferenceResult, ci::CodeInstance)
190
    mi = result.linfo
128,468✔
191
    code_cache(interp)[mi] = ci
128,468✔
192
end
193

194
function finish!(interp::AbstractInterpreter, mi::MethodInstance, ci::CodeInstance, src::CodeInfo)
×
195
    user_edges = src.edges
×
196
    edges = user_edges isa SimpleVector ? user_edges : user_edges === nothing ? Core.svec() : Core.svec(user_edges...)
×
197
    const_flag = false
×
198
    di = src.debuginfo
×
199
    rettype = Any
×
200
    exctype = Any
×
201
    const_flags = 0x0
×
202
    ipo_effects = zero(UInt32)
×
203
    min_world = src.min_world
×
204
    max_world = src.max_world
×
205
    if max_world >= get_world_counter()
×
206
        max_world = typemax(UInt)
×
207
    end
208
    if max_world == typemax(UInt)
×
209
        # if we can record all of the backedges in the global reverse-cache,
210
        # we can now widen our applicability in the global cache too
211
        store_backedges(ci, edges)
×
212
    end
213
    ccall(:jl_fill_codeinst, Cvoid, (Any, Any, Any, Any, Int32, UInt, UInt, UInt32, Any, Any, Any),
×
214
        ci, rettype, exctype, nothing, const_flags, min_world, max_world, ipo_effects, nothing, di, edges)
215
    ccall(:jl_update_codeinst, Cvoid, (Any, Any, Int32, UInt, UInt, UInt32, Any, Float64, Float64, Float64, Any, Any),
×
216
        ci, nothing, const_flag, min_world, max_world, ipo_effects, nothing, 0.0, 0.0, 0.0, di, edges)
217
    code_cache(interp)[mi] = ci
×
218
    codegen = codegen_cache(interp)
×
219
    if codegen !== nothing
×
220
        codegen[ci] = src
×
221
    end
222
    engine_reject(interp, ci)
×
223
    return nothing
×
224
end
225

226
function finish_nocycle(::AbstractInterpreter, frame::InferenceState, time_before::UInt64)
306,396✔
227
    opt_cache = IdDict{MethodInstance,CodeInstance}()
306,396✔
228
    finishinfer!(frame, frame.interp, frame.cycleid, opt_cache)
306,396✔
229
    opt = frame.result.src
306,396✔
230
    if opt isa OptimizationState # implies `may_optimize(caller.interp) === true`
306,396✔
231
        optimize(frame.interp, opt, frame.result)
134,303✔
232
    end
233
    empty!(opt_cache)
306,396✔
234
    validation_world = get_world_counter()
306,396✔
235
    finish!(frame.interp, frame, validation_world, time_before)
306,396✔
236
    if isdefined(frame.result, :ci)
306,396✔
237
        # After validation, under the world_counter_lock, set max_world to typemax(UInt) for all dependencies
238
        # (recursively). From that point onward the ordinary backedge mechanism is responsible for maintaining
239
        # validity.
240
        ccall(:jl_promote_ci_to_current, Cvoid, (Any, UInt), frame.result.ci, validation_world)
127,772✔
241
    end
242
    if frame.cycleid != 0
306,396✔
243
        frames = frame.callstack::Vector{AbsIntState}
304,653✔
244
        @assert frames[end] === frame
304,653✔
245
        pop!(frames)
304,653✔
246
    end
247
    return nothing
306,396✔
248
end
249

250
function finish_cycle(::AbstractInterpreter, frames::Vector{AbsIntState}, cycleid::Int, time_before::UInt64)
213✔
251
    cycle_valid_worlds = WorldRange()
213✔
252
    cycle_valid_effects = EFFECTS_TOTAL
213✔
253
    for frameid = cycleid:length(frames)
228✔
254
        caller = frames[frameid]::InferenceState
794✔
255
        @assert caller.cycleid == cycleid
794✔
256
        # converge the world age range and effects for this cycle here:
257
        # all frames in the cycle should have the same bits of `valid_worlds` and `effects`
258
        # that are simply the intersection of each partial computation, without having
259
        # dependencies on each other (unlike rt and exct)
260
        cycle_valid_worlds = intersect(cycle_valid_worlds, caller.world.valid_worlds)
794✔
261
        cycle_valid_effects = merge_effects(cycle_valid_effects, caller.ipo_effects)
1,136✔
262
    end
1,375✔
263
    opt_cache = IdDict{MethodInstance,CodeInstance}()
213✔
264
    for frameid = cycleid:length(frames)
228✔
265
        caller = frames[frameid]::InferenceState
794✔
266
        adjust_cycle_frame!(caller, cycle_valid_worlds, cycle_valid_effects)
794✔
267
        finishinfer!(caller, caller.interp, cycleid, opt_cache)
794✔
268
        time_now = _time_ns()
794✔
269
        caller.time_self_ns += (time_now - time_before)
794✔
270
        time_before = time_now
794✔
271
    end
1,375✔
272
    time_caches = 0.0 # the total and adjusted time of every entry in the cycle are the same
213✔
273
    time_paused = UInt64(0)
213✔
274
    for frameid = cycleid:length(frames)
228✔
275
        caller = frames[frameid]::InferenceState
794✔
276
        opt = caller.result.src
794✔
277
        if opt isa OptimizationState # implies `may_optimize(caller.interp) === true`
794✔
278
            optimize(caller.interp, opt, caller.result)
51✔
279
            time_now = _time_ns()
51✔
280
            caller.time_self_ns += (time_now - time_before)
51✔
281
            time_before = time_now
51✔
282
        end
283
        time_caches += caller.time_caches
794✔
284
        time_paused += caller.time_paused
794✔
285
        caller.time_paused = UInt64(0)
794✔
286
        caller.time_caches = 0.0
794✔
287
    end
1,375✔
288
    empty!(opt_cache)
213✔
289
    cycletop = frames[cycleid]::InferenceState
213✔
290
    time_start = cycletop.time_start
213✔
291
    validation_world = get_world_counter()
213✔
292
    cis = CodeInstance[]
213✔
293
    for frameid = cycleid:length(frames)
228✔
294
        caller = frames[frameid]::InferenceState
794✔
295
        caller.time_start = time_start
794✔
296
        caller.time_caches = time_caches
794✔
297
        caller.time_paused = time_paused
794✔
298
        finish!(caller.interp, caller, validation_world, time_before)
794✔
299
        if isdefined(caller.result, :ci)
794✔
300
            push!(cis, caller.result.ci)
794✔
301
        end
302
    end
1,375✔
303
    if cycletop.parentid != 0
213✔
304
        parent = frames[cycletop.parentid]
213✔
305
        parent.time_caches += time_caches
213✔
306
        parent.time_paused += time_paused
213✔
307
    end
308
    # After validation, under the world_counter_lock, set max_world to typemax(UInt) for all dependencies
309
    # (recursively). From that point onward the ordinary backedge mechanism is responsible for maintaining
310
    # validity.
311
    ccall(:jl_promote_cis_to_current, Cvoid, (Ptr{CodeInstance}, Csize_t, UInt), cis, length(cis), validation_world)
213✔
312
    resize!(frames, cycleid - 1)
213✔
313
    return nothing
213✔
314
end
315

316
function adjust_cycle_frame!(sv::InferenceState, cycle_valid_worlds::WorldRange, cycle_valid_effects::Effects)
317
    update_valid_age!(sv, cycle_valid_worlds)
794✔
318
    sv.ipo_effects = cycle_valid_effects
794✔
319
    # traverse the callees of this cycle that are tracked within `sv.cycle_backedges`
320
    # and adjust their statements so that they are consistent with the new `cycle_valid_effects`
321
    new_flags = flags_for_effects(cycle_valid_effects)
1,588✔
322
    for (callee, pc) in sv.cycle_backedges
794✔
323
        old_currpc = callee.currpc
1,798✔
324
        callee.currpc = pc
1,798✔
325
        set_curr_ssaflag!(callee, new_flags, IR_FLAGS_EFFECTS)
1,798✔
326
        callee.currpc = old_currpc
1,798✔
327
    end
1,798✔
328
    return nothing
794✔
329
end
330

331
function get_debuginfo(src)
332
    isa(src, CodeInfo) && return src.debuginfo
126,499✔
333
    isa(src, OptimizationState) && return src.src.debuginfo
732✔
334
    return nothing
732✔
335
end
336

337
function is_result_constabi_eligible(result::InferenceResult)
338
    result_type = result.result
476,761✔
339
    return isa(result_type, Const) && is_foldable_nothrow(result.ipo_effects) && is_inlineable_constant(result_type.val)
685,737✔
340
end
341

342
function compute_inlining_cost(interp::AbstractInterpreter, result::InferenceResult)
343
    src = result.src
111,515✔
344
    isa(src, OptimizationState) || return MAX_INLINE_COST
111,515✔
345
    compute_inlining_cost(interp, result, src.optresult)
223,030✔
346
end
347

348
function compute_inlining_cost(interp::AbstractInterpreter, result::InferenceResult, optresult#=::OptimizationResult=#)
10,788✔
349
    return inline_cost_model(interp, result, optresult.inline_flag, optresult.ir)
144,953✔
350
end
351

352
function inline_cost_model(interp::AbstractInterpreter, result::InferenceResult,
135,177✔
353
        inline_flag::UInt8, ir::IRCode)
354

355
    inline_flag === SRC_FLAG_DECLARED_NOINLINE && return MAX_INLINE_COST
135,177✔
356

357
    mi = result.linfo
133,370✔
358
    (; def, specTypes) = mi
133,370✔
359
    if !isa(def, Method)
133,370✔
360
        return MAX_INLINE_COST
×
361
    end
362

363
    declared_inline = inline_flag === SRC_FLAG_DECLARED_INLINE
133,370✔
364

365
    rt = result.result
133,370✔
366
    @assert !(rt isa LimitedAccuracy)
133,370✔
367
    rt = widenslotwrapper(rt)
133,370✔
368

369
    sig = unwrap_unionall(specTypes)
133,370✔
370
    if !(isa(sig, DataType) && sig.name === Tuple.name)
133,370✔
371
        return MAX_INLINE_COST
×
372
    end
373
    if !declared_inline && rt === Bottom
133,370✔
374
        return MAX_INLINE_COST
113✔
375
    end
376

377
    if declared_inline && isdispatchtuple(specTypes)
133,257✔
378
        # obey @inline declaration if a dispatch barrier would not help
379
        return MIN_INLINE_COST
20,761✔
380
    else
381
        # compute the cost (size) of inlining this code
382
        params = OptimizationParams(interp)
112,496✔
383
        cost_threshold = default = params.inline_cost_threshold
112,496✔
384
        if ⊑(optimizer_lattice(interp), rt, Tuple) && !isconcretetype(widenconst(rt))
112,496✔
385
            cost_threshold += params.inline_tupleret_bonus
28✔
386
        end
387
        # if the method is declared as `@inline`, increase the cost threshold 20x
388
        if declared_inline
112,496✔
389
            cost_threshold += 19*default
241✔
390
        end
391
        # a few functions get special treatment
392
        if def.module === _topmod(def.module)
112,496✔
393
            name = def.name
81,800✔
394
            if name === :iterate || name === :unsafe_convert || name === :cconvert
152,222✔
395
                cost_threshold += 4*default
11,863✔
396
            end
397
        end
398
        return inline_cost_model(ir, params, cost_threshold)
112,496✔
399
    end
400
end
401

402
function transform_result_for_local_cache(interp::AbstractInterpreter, result::InferenceResult, edges::SimpleVector)
403
    if is_result_constabi_eligible(result)
269,644✔
404
        return nothing
×
405
    end
406
    src = result.src
176,755✔
407
    if isa(src, OptimizationState)
176,755✔
408
        # Compute and store any information required to determine the inlineability of the callee.
409
        opt = src
111,515✔
410
        opt.src.inlining_cost = compute_inlining_cost(interp, result)
223,030✔
411
    end
412
    return src
176,755✔
413
end
414

415
function transform_result_for_cache(interp::AbstractInterpreter, result::InferenceResult, edges::SimpleVector)
69,083✔
416
    inlining_cost = nothing
69,083✔
417
    src = result.src
69,083✔
418
    if isa(src, OptimizationState)
69,083✔
419
        opt = src
22,650✔
420
        inlining_cost = compute_inlining_cost(interp, result, opt.optresult)
45,300✔
421
        discard_optimized_result(interp, opt, inlining_cost) && return nothing
22,650✔
422
        src = ir_to_codeinf!(opt)
38,828✔
423
    end
424
    if isa(src, CodeInfo)
65,847✔
425
        src.edges = edges
65,847✔
426
        if inlining_cost !== nothing
65,847✔
427
            src.inlining_cost = inlining_cost
19,414✔
428
        elseif may_optimize(interp)
46,433✔
429
            src.inlining_cost = compute_inlining_cost(interp, result)
×
430
        end
431
    end
432
    return src
65,847✔
433
end
434

435
function discard_optimized_result(interp::AbstractInterpreter, _#=::OptimizationState=#, inlining_cost#=::InlineCostType=#)
436
    may_discard_trees(interp) || return false
22,648✔
437
    return inlining_cost == MAX_INLINE_COST
22,650✔
438
end
439

440
function maybe_compress_codeinfo(interp::AbstractInterpreter, mi::MethodInstance, ci::CodeInfo)
441
    def = mi.def
65,142✔
442
    isa(def, Method) || return ci # don't compress toplevel code
65,142✔
443
    can_discard_trees = may_discard_trees(interp)
65,142✔
444
    cache_the_tree = !can_discard_trees || is_inlineable(ci)
65,142✔
445
    cache_the_tree || return nothing
111,575✔
446
    may_compress(interp) && return ccall(:jl_compress_ir, String, (Any, Any), def, ci)
18,709✔
447
    return ci
×
448
end
449

450
function cycle_fix_limited(@nospecialize(typ), sv::InferenceState, cycleid::Int)
1,368,305✔
451
    if typ isa LimitedAccuracy
1,368,305✔
452
        frames = sv.callstack::Vector{AbsIntState}
×
453
        causes = typ.causes
×
454
        for frameid = cycleid:length(frames)
×
455
            caller = frames[frameid]::InferenceState
×
456
            caller in causes || continue
×
457
            causes === typ.causes && (causes = copy(causes))
×
458
            pop!(causes, caller)
×
459
            if isempty(causes)
×
460
                return typ.typ
×
461
            end
462
        end
×
463
        @assert sv.parentid != 0
×
464
        if causes !== typ.causes
×
465
            return LimitedAccuracy(typ.typ, causes)
×
466
        end
467
    end
468
    return typ
1,368,305✔
469
end
470

471
function adjust_effects(ipo_effects::Effects, def::Method)
472
    # override the analyzed effects using manually annotated effect settings
473
    override = decode_effects_override(def.purity)
114,738✔
474
    if is_effect_overridden(override, :consistent)
114,738✔
475
        ipo_effects = Effects(ipo_effects; consistent=ALWAYS_TRUE)
1,264✔
476
    end
477
    if is_effect_overridden(override, :effect_free)
114,738✔
478
        ipo_effects = Effects(ipo_effects; effect_free=ALWAYS_TRUE)
3,730✔
479
    end
480
    if is_effect_overridden(override, :nothrow)
114,738✔
481
        ipo_effects = Effects(ipo_effects; nothrow=true)
2,230✔
482
    end
483
    if is_effect_overridden(override, :terminates_globally)
114,738✔
484
        ipo_effects = Effects(ipo_effects; terminates=true)
1,288✔
485
    end
486
    if is_effect_overridden(override, :notaskstate)
114,738✔
487
        ipo_effects = Effects(ipo_effects; notaskstate=true)
1,270✔
488
    end
489
    if is_effect_overridden(override, :inaccessiblememonly)
114,738✔
490
        ipo_effects = Effects(ipo_effects; inaccessiblememonly=ALWAYS_TRUE)
1,264✔
491
    end
492
    if is_effect_overridden(override, :noub)
114,738✔
493
        ipo_effects = Effects(ipo_effects; noub=ALWAYS_TRUE)
1,324✔
494
    elseif is_effect_overridden(override, :noub_if_noinbounds) && ipo_effects.noub !== ALWAYS_TRUE
113,414✔
495
        ipo_effects = Effects(ipo_effects; noub=NOUB_IF_NOINBOUNDS)
9✔
496
    end
497
    if is_effect_overridden(override, :consistent_overlay)
114,738✔
498
        ipo_effects = Effects(ipo_effects; nonoverlayed=CONSISTENT_OVERLAY)
×
499
    end
500
    if is_effect_overridden(override, :nortcall)
114,738✔
501
        ipo_effects = Effects(ipo_effects; nortcall=true)
1,264✔
502
    end
503
    return ipo_effects
114,738✔
504
end
505

506
function adjust_effects(sv::InferenceState)
110,059✔
507
    ipo_effects = sv.ipo_effects
110,059✔
508

509
    # refine :consistent-cy effect using the return type information
510
    # TODO this adjustment tries to compromise imprecise :consistent-cy information,
511
    # that is currently modeled in a flow-insensitive way: ideally we want to model it
512
    # with a proper dataflow analysis instead
513
    rt = sv.bestguess
110,059✔
514
    if rt === Bottom
110,059✔
515
        # always throwing an error counts or never returning both count as consistent
516
        ipo_effects = Effects(ipo_effects; consistent=ALWAYS_TRUE)
105✔
517
    end
518
    if sv.exc_bestguess === Bottom
110,059✔
519
        # if the exception type of this frame is known to be `Bottom`,
520
        # this frame is known to be safe
521
        ipo_effects = Effects(ipo_effects; nothrow=true)
94,320✔
522
    end
523
    if is_inaccessiblemem_or_argmemonly(ipo_effects) && all(1:narguments(sv, #=include_va=#true)) do i::Int
127,329✔
524
            return is_mutation_free_argtype(sv.slottypes[i])
30,197✔
525
        end
526
        ipo_effects = Effects(ipo_effects; inaccessiblememonly=ALWAYS_TRUE)
3,060✔
527
    end
528
    if is_consistent_if_notreturned(ipo_effects) && is_identity_free_argtype(rt)
110,059✔
529
        # in a case when the :consistent-cy here is only tainted by mutable allocations
530
        # (indicated by `CONSISTENT_IF_NOTRETURNED`), we may be able to refine it if the return
531
        # type guarantees that the allocations are never returned
532
        consistent = ipo_effects.consistent & ~CONSISTENT_IF_NOTRETURNED
×
533
        ipo_effects = Effects(ipo_effects; consistent)
×
534
    end
535
    if is_consistent_if_inaccessiblememonly(ipo_effects)
110,059✔
536
        if is_inaccessiblememonly(ipo_effects)
2,732✔
537
            consistent = ipo_effects.consistent & ~CONSISTENT_IF_INACCESSIBLEMEMONLY
×
538
            ipo_effects = Effects(ipo_effects; consistent)
×
539
        elseif is_inaccessiblemem_or_argmemonly(ipo_effects)
2,732✔
540
        else # `:inaccessiblememonly` is already tainted, there will be no chance to refine this
541
            ipo_effects = Effects(ipo_effects; consistent=ALWAYS_FALSE)
×
542
        end
543
    end
544
    if is_effect_free_if_inaccessiblememonly(ipo_effects)
110,059✔
545
        if is_inaccessiblememonly(ipo_effects)
×
546
            effect_free = ipo_effects.effect_free & ~EFFECT_FREE_IF_INACCESSIBLEMEMONLY
×
547
            ipo_effects = Effects(ipo_effects; effect_free)
×
548
        elseif is_inaccessiblemem_or_argmemonly(ipo_effects)
×
549
        else # `:inaccessiblememonly` is already tainted, there will be no chance to refine this
550
            ipo_effects = Effects(ipo_effects; effect_free=ALWAYS_FALSE)
×
551
        end
552
    end
553

554
    # override the analyzed effects using manually annotated effect settings
555
    def = sv.linfo.def
110,059✔
556
    if isa(def, Method)
110,059✔
557
        ipo_effects = adjust_effects(ipo_effects, def)
219,370✔
558
    end
559

560
    return ipo_effects
110,059✔
561
end
562

563
function refine_exception_type(@nospecialize(exc_bestguess), ipo_effects::Effects)
564
    ipo_effects.nothrow && return Bottom
583,396✔
565
    return exc_bestguess
249,915✔
566
end
567

568
const empty_edges = Core.svec()
569

570
# inference completed on `me`
571
# update the MethodInstance
572
function finishinfer!(me::InferenceState, interp::AbstractInterpreter, cycleid::Int,
307,153✔
573
                      opt_cache::IdDict{MethodInstance, CodeInstance})
574
    # prepare to run optimization passes on fulltree
575
    @assert isempty(me.ip)
307,153✔
576
    # inspect whether our inference had a limited result accuracy,
577
    # else it may be suitable to cache
578
    bestguess = me.bestguess = cycle_fix_limited(me.bestguess, me, cycleid)
307,153✔
579
    exc_bestguess = me.exc_bestguess = cycle_fix_limited(me.exc_bestguess, me, cycleid)
307,153✔
580
    limited_ret = bestguess isa LimitedAccuracy || exc_bestguess isa LimitedAccuracy
614,236✔
581
    limited_src = false
307,153✔
582
    if limited_ret
307,153✔
583
        @assert me.parentid != 0
70✔
584
    else
585
        gt = me.ssavaluetypes
307,083✔
586
        for j = 1:length(gt)
417,142✔
587
            gt[j] = gtj = cycle_fix_limited(gt[j], me, cycleid)
4,514,031✔
588
            if gtj isa LimitedAccuracy
4,514,031✔
589
                @assert me.parentid != 0
28✔
590
                limited_src = true
28✔
591
                break
28✔
592
            end
593
        end
4,514,003✔
594
    end
595
    result = me.result
307,153✔
596
    result.valid_worlds = me.world.valid_worlds
307,153✔
597
    result.result = bestguess
307,153✔
598
    ipo_effects = result.ipo_effects = me.ipo_effects = adjust_effects(me)
307,153✔
599
    result.exc_result = me.exc_bestguess = refine_exception_type(me.exc_bestguess, ipo_effects)
395,924✔
600
    me.src.rettype = widenconst(ignorelimited(bestguess))
614,236✔
601
    me.src.ssaflags = me.ssaflags
307,153✔
602
    me.src.min_world = first(me.world.valid_worlds)
307,153✔
603
    me.src.max_world = last(me.world.valid_worlds)
307,153✔
604
    istoplevel = !(me.linfo.def isa Method)
307,153✔
605
    istoplevel || compute_edges!(me) # don't add backedges to toplevel method instance
613,604✔
606

607
    if limited_ret || limited_src
614,236✔
608
        # A parent may be cached still, but not this intermediate work:
609
        # we can throw everything else away now. Caching anything can confuse later
610
        # heuristics to consider it worth trying to pursue compiling this further and
611
        # finding infinite work as a result. Avoiding caching helps to ensure there is only
612
        # a finite amount of work that can be discovered later (although potentially still a
613
        # large multiplier on it).
614
        result.src = nothing
98✔
615
        result.tombstone = true
98✔
616
        me.cache_mode = CACHE_MODE_NULL
98✔
617
        set_inlineable!(me.src, false)
98✔
618
    else
619
        # annotate fulltree with type information,
620
        # either because we are the outermost code, or we might use this later
621
        type_annotate!(interp, me)
307,055✔
622
        mayopt = may_optimize(interp)
307,055✔
623
        doopt = mayopt &&
373,165✔
624
                # disable optimization if we don't use this later (because it is not cached)
625
                me.cache_mode != CACHE_MODE_NULL &&
626
                # disable optimization if we've already obtained very accurate result
627
                !result_is_constabi(interp, result)
628
        if doopt
307,055✔
629
            result.src = OptimizationState(me, interp, opt_cache)
134,331✔
630
        else
631
            result.src = me.src # for reflection etc.
172,724✔
632
        end
633
    end
634

635
    maybe_validate_code(me.linfo, me.src, "inferred")
307,153✔
636

637
    # finish populating inference results into the CodeInstance if possible, and maybe cache that globally for use elsewhere
638
    if isdefined(result, :ci)
307,153✔
639
        result_type = result.result
128,564✔
640
        result_type isa LimitedAccuracy && (result_type = result_type.typ)
128,564✔
641
        @assert !(result_type === nothing)
128,564✔
642
        if isa(result_type, Const)
128,564✔
643
            rettype_const = result_type.val
24,927✔
644
            const_flags = is_result_constabi_eligible(result) ? 0x3 : 0x2
48,041✔
645
        elseif isa(result_type, PartialOpaque)
103,637✔
646
            rettype_const = result_type
×
647
            const_flags = 0x2
×
648
        elseif isconstType(result_type)
103,652✔
649
            rettype_const = result_type.parameters[1]
×
650
            const_flags = 0x2
×
651
        elseif isa(result_type, PartialStruct)
103,637✔
652
            rettype_const = (_getundefs(result_type), result_type.fields)
4,612✔
653
            const_flags = 0x2
4,612✔
654
        elseif isa(result_type, InterConditional)
99,025✔
655
            rettype_const = result_type
740✔
656
            const_flags = 0x2
740✔
657
        elseif isa(result_type, InterMustAlias)
98,285✔
658
            rettype_const = result_type
66✔
659
            const_flags = 0x2
66✔
660
        else
661
            rettype_const = nothing
98,219✔
662
            const_flags = 0x0
98,219✔
663
        end
664

665
        di = nothing
128,564✔
666
        edges = empty_edges # `edges` will be updated within `finish!`
128,564✔
667
        ci = result.ci
128,564✔
668
        min_world, max_world = first(result.valid_worlds), last(result.valid_worlds)
128,564✔
669
        ccall(:jl_fill_codeinst, Cvoid, (Any, Any, Any, Any, Int32, UInt, UInt, UInt32, Any, Any, Any),
128,564✔
670
            ci, widenconst(result_type), widenconst(result.exc_result), rettype_const, const_flags,
671
            min_world, max_world,
672
            encode_effects(result.ipo_effects), result.analysis_results, di, edges)
673
        if is_cached(me) # CACHE_MODE_GLOBAL
128,564✔
674
            already_cached = is_already_cached(me.interp, result, ci)
128,493✔
675
            if already_cached
128,493✔
676
                me.cache_mode = CACHE_MODE_VOLATILE
21✔
677
            else
678
                opt_cache[result.linfo] = ci
128,472✔
679
            end
680
        end
681
    end
682
    nothing
307,153✔
683
end
684

685
function is_already_cached(interp::AbstractInterpreter, result::InferenceResult, ::CodeInstance)
271,473✔
686
    # check if the existing linfo metadata is also sufficient to describe the current inference result
687
    # to decide if it is worth caching this right now
688
    mi = result.linfo
271,473✔
689
    cache = WorldView(code_cache(interp), result.valid_worlds)
271,473✔
690
    if haskey(cache, mi)
271,473✔
691
        # n.b.: accurate edge representation might cause the CodeInstance for this to be constructed later
692
        @assert isdefined(cache[mi], :inferred)
5,854✔
693
        return true
5,854✔
694
    end
695
    return false
265,619✔
696
end
697

698
# Iterate a series of back-edges that need registering, based on the provided forward edge list.
699
# Back-edges are returned as (invokesig, item), where the item is a Binding, MethodInstance, or
700
# MethodTable.
701
struct ForwardToBackedgeIterator
702
    forward_edges::SimpleVector
49,936✔
703
end
704

705
function Base.iterate(it::ForwardToBackedgeIterator, i::Int = 1)
706
    edges = it.forward_edges
1,027,614✔
707
    i > length(edges) && return nothing
631,995✔
708
    while i ≤ length(edges)
582,456✔
709
        item = edges[i]
582,441✔
710
        if item isa Int
582,441✔
711
            i += 2
382✔
712
            continue # ignore the query information if present but process the contents
382✔
713
        elseif isa(item, Method)
582,059✔
714
            # ignore `Method`-edges (from e.g. failed `abstract_call_method`)
715
            i += 1
×
716
            continue
×
717
        elseif isa(item, Core.Binding)
582,059✔
718
            return ((nothing, item), i + 1)
5,828✔
719
        end
720
        if isa(item, CodeInstance)
576,231✔
721
            item = get_ci_mi(item)
1,151,530✔
722
            return ((nothing, item), i + 1)
575,765✔
723
        elseif isa(item, MethodInstance) # regular dispatch
466✔
724
            return ((nothing, item), i + 1)
18✔
725
        else
726
            invokesig = item
448✔
727
            callee = edges[i+1]
448✔
728
            isa(callee, Method) && (i += 2; continue) # ignore `Method`-edges (from e.g. failed `abstract_call_method`)
448✔
729
            if isa(callee, MethodTable)
448✔
730
                # abstract dispatch (legacy style edges)
731
                return ((invokesig, callee), i + 2)
60✔
732
            else
733
                # `invoke` edge
734
                callee = isa(callee, CodeInstance) ? get_ci_mi(callee) : callee::MethodInstance
776✔
735
                return ((invokesig, callee), i + 2)
388✔
736
            end
737
        end
738
    end
382✔
739
    return nothing
15✔
740
end
741

742
# record the backedges
743

744
function maybe_add_binding_backedge!(b::Core.Binding, edge::Union{Method, CodeInstance})
745
    meth = isa(edge, Method) ? edge : get_ci_mi(edge).def
5,708✔
746
    ccall(:jl_maybe_add_binding_backedge, Cint, (Any, Any, Any), b, edge, meth)
2,854✔
747
    return nothing
2,854✔
748
end
749

750
function store_backedges(caller::CodeInstance, edges::SimpleVector)
49,936✔
751
    isa(get_ci_mi(caller).def, Method) || return # don't add backedges to toplevel method instance
49,936✔
752

753
    backedges = ForwardToBackedgeIterator(edges)
49,936✔
754
    for (i, (invokesig, item)) in enumerate(backedges)
52,811✔
755
        # check for any duplicate edges we've already registered
756
        duplicate_found = false
149,332✔
757
        for (i′, (invokesig′, item′)) in enumerate(backedges)
152,345✔
758
            i == i′ && break
432,727✔
759
            if item′ === item && invokesig′ == invokesig
283,395✔
760
                duplicate_found = true
×
761
                break
×
762
            end
763
        end
283,395✔
764

765
        if !duplicate_found
149,332✔
766
            if item isa Core.Binding
149,332✔
767
                maybe_add_binding_backedge!(item, caller)
5,708✔
768
            elseif item isa MethodTable
146,478✔
769
                ccall(:jl_method_table_add_backedge, Cvoid, (Any, Any), invokesig, caller)
21✔
770
            else
771
                item::MethodInstance
146,457✔
772
                ccall(:jl_method_instance_add_backedge, Cvoid, (Any, Any, Any), item, invokesig, caller)
146,457✔
773
            end
774
        end
775
    end
251,691✔
776
    nothing
49,936✔
777
end
778

779
function compute_edges!(sv::InferenceState)
110,059✔
780
    edges = sv.edges
110,059✔
781
    for i in 1:length(sv.stmt_info)
220,118✔
782
        add_edges!(edges, sv.stmt_info[i])
1,148,187✔
783
    end
2,186,315✔
784
    user_edges = sv.src.edges
110,059✔
785
    if user_edges !== nothing && user_edges !== empty_edges
110,059✔
786
        append!(edges, user_edges)
27✔
787
    end
788
    nothing
110,059✔
789
end
790

791
function record_slot_assign!(sv::InferenceState)
110,059✔
792
    # look at all assignments to slots
793
    # and union the set of types stored there
794
    # to compute a lower bound on the storage required
795
    body = sv.src.code::Vector{Any}
110,059✔
796
    slottypes = sv.slottypes::Vector{Any}
110,059✔
797
    ssavaluetypes = sv.ssavaluetypes
110,059✔
798
    for i = 1:length(body)
220,118✔
799
        expr = body[i]
1,148,187✔
800
        # find all reachable assignments to locals
801
        if was_reached(sv, i) && isexpr(expr, :(=))
1,148,187✔
802
            lhs = expr.args[1]
59,025✔
803
            if isa(lhs, SlotNumber)
59,025✔
804
                typ = ssavaluetypes[i]
59,025✔
805
                @assert typ !== NOT_FOUND "active slot in unreached region"
59,025✔
806
                vt = widenconst(typ)
59,025✔
807
                if vt !== Bottom
59,025✔
808
                    id = slot_id(lhs)
59,004✔
809
                    otherTy = slottypes[id]
59,004✔
810
                    if otherTy === Bottom
59,004✔
811
                        slottypes[id] = vt
51,722✔
812
                    elseif otherTy === Any
7,282✔
813
                        slottypes[id] = Any
8✔
814
                    else
815
                        slottypes[id] = tmerge(otherTy, vt)
7,274✔
816
                    end
817
                end
818
            end
819
        end
820
    end
2,186,315✔
821
    sv.src.slottypes = slottypes
110,059✔
822
    return nothing
110,059✔
823
end
824

825
# find the dominating assignment to the slot `id` in the block containing statement `idx`,
826
# returns `nothing` otherwise
827
function find_dominating_assignment(id::Int, idx::Int, sv::InferenceState)
×
828
    block = block_for_inst(sv.cfg, idx)
×
829
    for pc in reverse(sv.cfg.blocks[block].stmts) # N.B. reverse since the last assignment is dominating this block
×
830
        pc < idx || continue # N.B. needs pc ≠ idx as `id` can be assigned at `idx`
×
831
        stmt = sv.src.code[pc]
×
832
        isexpr(stmt, :(=)) || continue
×
833
        lhs = stmt.args[1]
×
834
        isa(lhs, SlotNumber) || continue
×
835
        slot_id(lhs) == id || continue
×
836
        return pc
×
837
    end
×
838
    return nothing
×
839
end
840

841
# annotate types of all symbols in AST, preparing for optimization
842
function type_annotate!(::AbstractInterpreter, sv::InferenceState)
307,022✔
843
    # widen `Conditional`s from `slottypes`
844
    slottypes = sv.slottypes
307,022✔
845
    for i = 1:length(slottypes)
417,081✔
846
        slottypes[i] = widenconditional(slottypes[i])
1,194,424✔
847
    end
2,082,516✔
848

849
    # compute the required type for each slot
850
    # to hold all of the items assigned into it
851
    record_slot_assign!(sv)
307,022✔
852

853
    # annotate variables load types
854
    src = sv.src
307,022✔
855
    stmts = src.code
307,022✔
856
    nstmt = length(stmts)
307,022✔
857
    ssavaluetypes = sv.ssavaluetypes
307,022✔
858
    nslots = length(src.slotflags)
307,022✔
859

860
    # widen slot wrappers (`Conditional` and `MustAlias`) and remove `NOT_FOUND` from `ssavaluetypes`
861
    # and mark any unreachable statements by wrapping them in Const(...), to distinguish them from
862
    # must-throw statements which also have type Bottom
863
    for i = 1:nstmt
417,081✔
864
        expr = stmts[i]
4,512,835✔
865
        if was_reached(sv, i)
4,512,835✔
866
            ssavaluetypes[i] = widenslotwrapper(ssavaluetypes[i]) # 3
3,933,028✔
867
        else # i.e. any runtime execution will never reach this statement
868
            push!(sv.unreachable, i)
579,807✔
869
            if is_meta_expr(expr) # keep any lexically scoped expressions
823,836✔
870
                ssavaluetypes[i] = Any # 3
×
871
            else
872
                ssavaluetypes[i] = Bottom # 3
579,807✔
873
                # annotate that this statement actually is dead
874
                stmts[i] = Const(expr)
579,807✔
875
            end
876
        end
877
    end
8,718,648✔
878

879
    # widen slot wrappers (`Conditional` and `MustAlias`) in `bb_vartables`
880
    for varstate in sv.bb_vartables
307,022✔
881
        if varstate !== nothing
872,630✔
882
            for slot in 1:nslots
903,993✔
883
                vt = varstate[slot]
9,083,009✔
884
                widened_type = widenslotwrapper(ignorelimited(vt.typ))
9,357,368✔
885
                varstate[slot] = VarState(widened_type, vt.undef)
9,083,009✔
886
            end
17,450,847✔
887
        end
888
    end
872,630✔
889

890
    return nothing
307,022✔
891
end
892

893
function merge_call_chain!(::AbstractInterpreter, parent::InferenceState, child::InferenceState)
3,205✔
894
    # add backedge of parent <- child
895
    # then add all backedges of parent <- parent.parent
896
    frames = parent.callstack::Vector{AbsIntState}
3,205✔
897
    @assert child.callstack === frames
3,205✔
898
    ancestorid = child.cycleid
3,205✔
899
    while true
3,786✔
900
        add_cycle_backedge!(parent, child)
3,786✔
901
        parent.cycleid === ancestorid && break
3,786✔
902
        child = parent
581✔
903
        parent = cycle_parent(child)::InferenceState
1,162✔
904
    end
581✔
905
    # ensure that walking the callstack has the same cycleid (DAG)
906
    for frameid = reverse(ancestorid:length(frames))
5,349✔
907
        frame = frames[frameid]::InferenceState
4,008✔
908
        frame.cycleid == ancestorid && break
4,008✔
909
        @assert frame.cycleid > ancestorid
803✔
910
        frame.cycleid = ancestorid
803✔
911
    end
803✔
912
end
913

914
function add_cycle_backedge!(caller::InferenceState, frame::InferenceState)
66✔
915
    update_valid_age!(caller, frame.world.valid_worlds)
66✔
916
    backedge = (caller, caller.currpc)
66✔
917
    contains_is(frame.cycle_backedges, backedge) || push!(frame.cycle_backedges, backedge)
102✔
918
    return frame
66✔
919
end
920

921
function is_same_frame(interp::AbstractInterpreter, mi::MethodInstance, frame::InferenceState)
45,093✔
922
    return mi === frame_instance(frame) && cache_owner(interp) === cache_owner(frame.interp)
2,566,617✔
923
end
924

925
function poison_callstack!(infstate::InferenceState, topmost::InferenceState)
926
    push!(infstate.pclimitations, topmost)
100✔
927
    nothing
×
928
end
929

930
# Walk through `mi`'s upstream call chain, starting at `parent`. If a parent
931
# frame matching `mi` is encountered, then there is a cycle in the call graph
932
# (i.e. `mi` is a descendant callee of itself). Upon encountering this cycle,
933
# we "resolve" it by merging the call chain, which entails updating each intermediary
934
# frame's `cycleid` field and adding the appropriate backedges. Finally,
935
# we return `mi`'s pre-existing frame. If no cycles are found, `nothing` is
936
# returned instead.
937
function resolve_call_cycle!(interp::AbstractInterpreter, mi::MethodInstance, parent::AbsIntState)
130,435✔
938
    # TODO (#48913) implement a proper recursion handling for irinterp:
939
    # This works most of the time currently just because the irinterp code doesn't get used much with
940
    # `@assume_effects`, so it never sees a cycle normally, but that may not be a sustainable solution.
941
    parent isa InferenceState || return false
130,435✔
942
    frames = parent.callstack::Vector{AbsIntState}
130,435✔
943
    uncached = false
130,435✔
944
    for frameid = reverse(1:length(frames))
260,039✔
945
        frame = frames[frameid]
2,566,617✔
946
        isa(frame, InferenceState) || break
2,566,617✔
947
        uncached |= !is_cached(frame) # ensure we never add a (globally) uncached frame to a cycle
2,566,617✔
948
        if is_same_frame(interp, mi, frame)
2,566,617✔
949
            if uncached
3,232✔
950
                # our attempt to speculate into a constant call lead to an undesired self-cycle
951
                # that cannot be converged: if necessary, poison our call-stack (up to the discovered duplicate frame)
952
                # with the limited flag and abort (set return type to Any) now
953
                poison_callstack!(parent, frame)
18✔
954
                return true
18✔
955
            end
956
            merge_call_chain!(interp, parent, frame)
3,214✔
957
            return frame
3,214✔
958
        end
959
    end
4,999,567✔
960
    return false
127,203✔
961
end
962

963
ipo_effects(code::CodeInstance) = decode_effects(code.ipo_purity_bits)
789,321✔
964

965
# return cached result of regular inference
966
function return_cached_result(interp::AbstractInterpreter, method::Method, codeinst::CodeInstance, caller::AbsIntState, edgecycle::Bool, edgelimited::Bool)
789,321✔
967
    rt = cached_return_type(codeinst)
862,906✔
968
    exct = codeinst.exctype
789,321✔
969
    effects = ipo_effects(codeinst)
789,321✔
970
    update_valid_age!(caller, WorldRange(min_world(codeinst), max_world(codeinst)))
789,321✔
971
    caller.time_caches += reinterpret(Float16, codeinst.time_infer_total)
789,321✔
972
    caller.time_caches += reinterpret(Float16, codeinst.time_infer_cache_saved)
789,321✔
973
    return Future(MethodCallResult(interp, caller, method, rt, exct, effects, codeinst, edgecycle, edgelimited))
789,321✔
974
end
975

976
function MethodCallResult(::AbstractInterpreter, sv::AbsIntState, method::Method,
977
                          @nospecialize(rt), @nospecialize(exct), effects::Effects,
978
                          edge::Union{Nothing,CodeInstance}, edgecycle::Bool, edgelimited::Bool,
979
                          volatile_inf_result::Union{Nothing,VolatileInferenceResult}=nothing)
980
    if edge === nothing
2,627,756✔
981
        edgecycle = edgelimited = true
4,706✔
982
    end
983

984
    # we look for the termination effect override here as well, since the :terminates effect
985
    # may have been tainted due to recursion at this point even if it's overridden
986
    if is_effect_overridden(sv, :terminates_globally)
2,100,022✔
987
        # this frame is known to terminate
988
        effects = Effects(effects, terminates=true)
30,500✔
989
    elseif is_effect_overridden(method, :terminates_globally)
1,034,761✔
990
        # this edge is known to terminate
991
        effects = Effects(effects; terminates=true)
26,056✔
992
    elseif edgecycle
1,008,705✔
993
        # Some sort of recursion was detected.
994
        if edge !== nothing && !edgelimited && !is_edge_recursed(edge, sv)
74,241✔
995
            # no `MethodInstance` cycles -- don't taint :terminate
996
        else
997
            # we cannot guarantee that the call will terminate
998
            effects = Effects(effects; terminates=false)
4,999✔
999
        end
1000
    end
1001

1002
    return MethodCallResult(rt, exct, effects, edge, edgecycle, edgelimited, volatile_inf_result)
1,336,495✔
1003
end
1004

1005
# allocate a dummy `edge::CodeInstance` to be added by `add_edges!`, reusing an existing_edge if possible
1006
# TODO: fill this in fully correctly (currently IPO info such as effects and return types are lost)
1007
function codeinst_as_edge(interp::AbstractInterpreter, sv::InferenceState, @nospecialize existing_edge)
176,640✔
1008
    mi = sv.linfo
176,710✔
1009
    min_world, max_world = first(sv.world.valid_worlds), last(sv.world.valid_worlds)
176,710✔
1010
    if max_world >= get_world_counter()
176,710✔
1011
        max_world = typemax(UInt)
176,710✔
1012
    end
1013
    edges = Core.svec(sv.edges...)
176,710✔
1014
    if existing_edge isa CodeInstance
176,710✔
1015
        # return an existing_edge, if the existing edge has more restrictions already (more edges and narrower worlds)
1016
        if existing_edge.min_world >= min_world &&
176,640✔
1017
           existing_edge.max_world <= max_world &&
1018
           existing_edge.edges == edges
1019
            return existing_edge
125,468✔
1020
        end
1021
    end
1022
    ci = CodeInstance(mi, cache_owner(interp), Any, Any, nothing, nothing, zero(Int32),
51,242✔
1023
        min_world, max_world, zero(UInt32), nothing, nothing, edges)
1024
    if max_world == typemax(UInt)
51,242✔
1025
        # if we can record all of the backedges in the global reverse-cache,
1026
        # we can now widen our applicability in the global cache too
1027
        # TODO: this should probably come after we decide this edge is even useful
1028
        store_backedges(ci, edges)
51,242✔
1029
    end
1030
    return ci
51,242✔
1031
end
1032

1033
# compute (and cache) an inferred AST and return the current best estimate of the result type
1034
function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize(atype), sparams::SimpleVector, caller::AbsIntState, edgecycle::Bool, edgelimited::Bool)
921,372✔
1035
    mi = specialize_method(method, atype, sparams)
921,372✔
1036
    cache_mode = CACHE_MODE_GLOBAL # cache edge targets globally by default
921,372✔
1037
    force_inline = is_stmt_inline(get_curr_ssaflag(caller))
921,372✔
1038
    edge_ci = nothing
921,372✔
1039
    # check cache with SOURCE_MODE_NOT_REQUIRED source_mode
1040
    let codeinst = get(code_cache(interp), mi, nothing)
1,711,161✔
1041
        if codeinst isa CodeInstance # return existing rettype if the code is already inferred
921,372✔
1042
            inferred = @atomic :monotonic codeinst.inferred
789,789✔
1043
            if inferred === nothing && force_inline
789,789✔
1044
                # we already inferred this edge before and decided to discard the inferred code,
1045
                # nevertheless we re-infer it here again in order to propagate the re-inferred
1046
                # source to the inliner as a volatile result
1047
                cache_mode = CACHE_MODE_VOLATILE
95✔
1048
                edge_ci = codeinst
95✔
1049
            else
1050
                @assert codeinst.def === mi "MethodInstance for cached edge does not match"
789,694✔
1051
                return return_cached_result(interp, method, codeinst, caller, edgecycle, edgelimited)
789,694✔
1052
            end
1053
        end
1054
    end
1055
    if !InferenceParams(interp).force_enable_inference && ccall(:jl_get_module_infer, Cint, (Any,), method.module) == 0
131,678✔
1056
        add_remark!(interp, caller, "[typeinf_edge] Inference is disabled for the target module")
×
1057
        return Future(MethodCallResult(interp, caller, method, Any, Any, Effects(), nothing, edgecycle, edgelimited))
×
1058
    end
1059
    if !is_cached(caller) && frame_parent(caller) === nothing
131,911✔
1060
        # this caller exists to return to the user
1061
        # (if we asked resolve_call_cycle!, it might instead detect that there is a cycle that it can't merge)
1062
        frame = false
1,243✔
1063
    else
1064
        frame = resolve_call_cycle!(interp, mi, caller)
130,435✔
1065
    end
1066
    if frame === false
131,678✔
1067
        # completely new, but check again after reserving in the engine
1068
        if cache_mode == CACHE_MODE_GLOBAL
128,446✔
1069
            reserve_start = _time_ns() # subtract engine_reserve (thread-synchronization) time from callers to avoid double-counting
128,351✔
1070
            ci_from_engine = engine_reserve(interp, mi)
128,351✔
1071
            caller.time_paused += (_time_ns() - reserve_start)
128,351✔
1072
            edge_ci = ci_from_engine
128,351✔
1073
            codeinst = get(code_cache(interp), mi, nothing)
128,351✔
1074
            if codeinst isa CodeInstance # return existing rettype if the code is already inferred
128,351✔
1075
                engine_reject(interp, ci_from_engine)
×
1076
                ci_from_engine = nothing
×
1077
                inferred = @atomic :monotonic codeinst.inferred
×
1078
                if inferred === nothing && force_inline
×
1079
                    cache_mode = CACHE_MODE_VOLATILE
×
1080
                    edge_ci = codeinst
×
1081
                else
1082
                    @assert codeinst.def === mi "MethodInstance for cached edge does not match"
×
1083
                    return return_cached_result(interp, method, codeinst, caller, edgecycle, edgelimited)
×
1084
                end
1085
            end
1086
        else
1087
            ci_from_engine = nothing
95✔
1088
        end
1089
        result = InferenceResult(mi, typeinf_lattice(interp))
256,892✔
1090
        if ci_from_engine !== nothing
128,446✔
1091
            result.ci = ci_from_engine
128,351✔
1092
        end
1093
        frame = InferenceState(result, cache_mode, interp) # always use the cache for edge targets
256,883✔
1094
        if frame === nothing
128,446✔
1095
            add_remark!(interp, caller, "[typeinf_edge] Failed to retrieve source")
9✔
1096
            # can't get the source for this, so we know nothing
1097
            if ci_from_engine !== nothing
9✔
1098
                engine_reject(interp, ci_from_engine)
9✔
1099
            end
1100
            return Future(MethodCallResult(interp, caller, method, Any, Any, Effects(), nothing, edgecycle, edgelimited))
9✔
1101
        end
1102
        assign_parentchild!(frame, caller)
256,874✔
1103
        # the actual inference task for this edge is going to be scheduled within `typeinf_local` via the callstack queue
1104
        # while splitting off the rest of the work for this caller into a separate workq thunk
1105
        let mresult = Future{MethodCallResult}()
128,437✔
1106
            push!(caller.tasks, function get_infer_result(interp, caller)
401,136✔
1107
                update_valid_age!(caller, frame.world.valid_worlds)
272,699✔
1108
                local isinferred = is_inferred(frame)
545,398✔
1109
                local edge = isinferred ? edge_ci : nothing
272,699✔
1110
                local effects = isinferred ? frame.result.ipo_effects : # effects are adjusted already within `finish` for ipo_effects
272,699✔
1111
                    adjust_effects(effects_for_cycle(frame.ipo_effects), method)
1112
                local bestguess = frame.bestguess
272,699✔
1113
                local exc_bestguess = refine_exception_type(frame.exc_bestguess, effects)
431,189✔
1114
                # propagate newly inferred source to the inliner, allowing efficient inlining w/o deserialization:
1115
                # note that this result is cached globally exclusively, so we can use this local result destructively
1116
                local volatile_inf_result = if isinferred && edge_ci isa CodeInstance
272,699✔
1117
                    result.ci_as_edge = edge_ci # set the edge for the inliner usage
271,234✔
1118
                    VolatileInferenceResult(result)
271,234✔
1119
                end
1120
                mresult[] = MethodCallResult(interp, caller, method, bestguess, exc_bestguess, effects,
272,699✔
1121
                    edge, edgecycle, edgelimited, volatile_inf_result)
1122
                return true
272,699✔
1123
            end)
1124
            return mresult
128,437✔
1125
        end
1126
    elseif frame === true
3,232✔
1127
        # unresolvable cycle
1128
        add_remark!(interp, caller, "[typeinf_edge] Unresolvable cycle")
18✔
1129
        return Future(MethodCallResult(interp, caller, method, Any, Any, Effects(), nothing, edgecycle, edgelimited))
18✔
1130
    end
1131
    # return the current knowledge about this cycle
1132
    frame = frame::InferenceState
3,214✔
1133
    update_valid_age!(caller, frame.world.valid_worlds)
3,214✔
1134
    effects = adjust_effects(effects_for_cycle(frame.ipo_effects), method)
5,900✔
1135
    bestguess = frame.bestguess
3,214✔
1136
    exc_bestguess = refine_exception_type(frame.exc_bestguess, effects)
5,850✔
1137
    return Future(MethodCallResult(interp, caller, method, bestguess, exc_bestguess, effects, nothing, edgecycle, edgelimited))
3,214✔
1138
end
1139

1140
# The `:terminates` effect bit must be conservatively tainted unless recursion cycle has
1141
# been fully resolved. As for other effects, there's no need to taint them at this moment
1142
# because they will be tainted as we try to resolve the cycle.
1143
effects_for_cycle(effects::Effects) = Effects(effects; terminates=false)
4,679✔
1144

1145
function cached_return_type(code::CodeInstance)
1146
    rettype = code.rettype
789,321✔
1147
    isdefined(code, :rettype_const) || return rettype
1,481,077✔
1148
    rettype_const = code.rettype_const
97,565✔
1149
    # the second subtyping/egal conditions are necessary to distinguish usual cases
1150
    # from rare cases when `Const` wrapped those extended lattice type objects
1151
    if isa(rettype_const, Tuple{Vector{Union{Nothing,Bool}}, Vector{Any}}) && !(Tuple{Vector{Union{Nothing,Bool}}, Vector{Any}} <: rettype)
97,565✔
1152
        undefs, fields = rettype_const
23,980✔
1153
        return PartialStruct(fallback_lattice, rettype, undefs, fields)
23,980✔
1154
    elseif isa(rettype_const, PartialOpaque) && rettype <: Core.OpaqueClosure
73,585✔
1155
        return rettype_const
×
1156
    elseif isa(rettype_const, InterConditional) && rettype !== InterConditional
73,585✔
1157
        return rettype_const
14,102✔
1158
    elseif isa(rettype_const, InterMustAlias) && rettype !== InterMustAlias
59,483✔
1159
        return rettype_const
6✔
1160
    else
1161
        return Const(rettype_const)
59,477✔
1162
    end
1163
end
1164

1165
#### entry points for inferring a MethodInstance given a type signature ####
1166

1167
"""
1168
    codeinfo_for_const(interp::AbstractInterpreter, mi::MethodInstance, worlds::WorldRange, edges::SimpleVector, @nospecialize(val))
1169

1170
Return a fake CodeInfo that just contains `return \$val`. This function is used in various reflection APIs when asking
1171
for the code of a function that inference has found to just return a constant. For such functions, no code is actually
1172
stored - the constant is used directly. However, because this is an ABI implementation detail, it is nice to maintain
1173
consistency and just synthesize a CodeInfo when the reflection APIs ask for them - this function does that.
1174
"""
1175
function codeinfo_for_const(::AbstractInterpreter, mi::MethodInstance, worlds::WorldRange, edges::SimpleVector, @nospecialize(val))
×
1176
    method = mi.def::Method
×
1177
    tree = ccall(:jl_new_code_info_uninit, Ref{CodeInfo}, ())
×
1178
    tree.code = Any[ ReturnNode(quoted(val)) ]
×
1179
    nargs = Int(method.nargs)
×
1180
    tree.slotnames = ccall(:jl_uncompress_argnames, Vector{Symbol}, (Any,), method.slot_syms)
×
1181
    tree.slotflags = fill(0x00, nargs)
×
1182
    tree.ssavaluetypes = 1
×
1183
    tree.debuginfo = DebugInfo(mi)
×
1184
    tree.ssaflags = [IR_FLAG_NULL]
×
1185
    tree.rettype = Core.Typeof(val)
×
1186
    tree.min_world = first(worlds)
×
1187
    tree.max_world = last(worlds)
×
1188
    tree.edges = edges
×
1189
    set_inlineable!(tree, true)
×
1190
    tree.parent = mi
×
1191
    return tree
×
1192
end
1193

1194
result_is_constabi(interp::AbstractInterpreter, result::InferenceResult) =
215,880✔
1195
    may_discard_trees(interp) && is_result_constabi_eligible(result)
1196

1197
# compute an inferred AST and return type
1198
typeinf_code(interp::AbstractInterpreter, match::MethodMatch, run_optimizer::Bool) =
2,698✔
1199
    typeinf_code(interp, specialize_method(match), run_optimizer)
1200
typeinf_code(interp::AbstractInterpreter, method::Method, @nospecialize(atype), sparams::SimpleVector,
×
1201
             run_optimizer::Bool) =
1202
    typeinf_code(interp, specialize_method(method, atype, sparams), run_optimizer)
1203
function typeinf_code(interp::AbstractInterpreter, mi::MethodInstance, run_optimizer::Bool)
12✔
1204
    frame = typeinf_frame(interp, mi, run_optimizer)
3,063✔
1205
    frame === nothing && return nothing
3,060✔
1206
    return frame.src
3,054✔
1207
end
1208

1209
"""
1210
    typeinf_ircode(interp::AbstractInterpreter, match::MethodMatch,
1211
                   optimize_until::Union{Int,String,Nothing}) -> (ir::Union{IRCode,Nothing}, returntype::Type)
1212
    typeinf_ircode(interp::AbstractInterpreter,
1213
                   method::Method, atype, sparams::SimpleVector,
1214
                   optimize_until::Union{Int,String,Nothing}) -> (ir::Union{IRCode,Nothing}, returntype::Type)
1215
    typeinf_ircode(interp::AbstractInterpreter, mi::MethodInstance,
1216
                   optimize_until::Union{Int,String,Nothing}) -> (ir::Union{IRCode,Nothing}, returntype::Type)
1217

1218
Infer a `method` and return an `IRCode` with inferred `returntype` on success.
1219
"""
1220
typeinf_ircode(interp::AbstractInterpreter, match::MethodMatch,
×
1221
               optimize_until::Union{Int,String,Nothing}) =
255✔
1222
    typeinf_ircode(interp, specialize_method(match), optimize_until)
1223
typeinf_ircode(interp::AbstractInterpreter, method::Method, @nospecialize(atype), sparams::SimpleVector,
×
1224
               optimize_until::Union{Int,String,Nothing}) =
1225
    typeinf_ircode(interp, specialize_method(method, atype, sparams), optimize_until)
1226
function typeinf_ircode(interp::AbstractInterpreter, mi::MethodInstance,
255✔
1227
                        optimize_until::Union{Int,String,Nothing})
1228
    frame = typeinf_frame(interp, mi, false)
255✔
1229
    if frame === nothing
255✔
1230
        return nothing, Any
×
1231
    end
1232
    (; result) = frame
255✔
1233
    opt = OptimizationState(frame, interp)
255✔
1234
    ir = run_passes_ipo_safe(opt.src, opt, optimize_until)
255✔
1235
    rt = widenconst(ignorelimited(result.result))
498✔
1236
    return ir, rt
249✔
1237
end
1238

1239
# compute an inferred frame
1240
typeinf_frame(interp::AbstractInterpreter, match::MethodMatch, run_optimizer::Bool) =
8,053✔
1241
    typeinf_frame(interp, specialize_method(match), run_optimizer)
1242
typeinf_frame(interp::AbstractInterpreter, method::Method, @nospecialize(atype), sparams::SimpleVector,
×
1243
              run_optimizer::Bool) =
1244
    typeinf_frame(interp, specialize_method(method, atype, sparams), run_optimizer)
1245
function typeinf_frame(interp::AbstractInterpreter, mi::MethodInstance, run_optimizer::Bool)
11,643✔
1246
    result = InferenceResult(mi, typeinf_lattice(interp))
23,286✔
1247
    frame = InferenceState(result, #=cache_mode=#:no, interp)
23,280✔
1248
    frame === nothing && return nothing
11,643✔
1249
    typeinf(interp, frame)
11,637✔
1250
    is_inferred(frame) || return nothing
11,637✔
1251
    if run_optimizer
11,637✔
1252
        if result_is_constabi(interp, frame.result)
14,639✔
1253
            rt = frame.result.result::Const
3✔
1254
            src = codeinfo_for_const(interp, frame.linfo, frame.world.valid_worlds, Core.svec(frame.edges...), rt.val)
3✔
1255
        else
1256
            opt = OptimizationState(frame, interp)
10,800✔
1257
            optimize(interp, opt, frame.result)
10,800✔
1258
            src = ir_to_codeinf!(opt, frame, Core.svec(opt.inlining.edges...))
10,794✔
1259
        end
1260
        result.src = frame.src = src
10,797✔
1261
    end
1262
    return frame
11,631✔
1263
end
1264

1265
# N.B.: These need to be aligned with the C side headers
1266
"""
1267
    SOURCE_MODE_NOT_REQUIRED
1268

1269
Indicates to inference that the source is not required and the only fields of
1270
the resulting `CodeInstance` that the caller is interested in are return or
1271
exception types and IPO effects. Inference is still free to create source for
1272
it or add it to the JIT even, but is not required or expected to do so.
1273
"""
1274
const SOURCE_MODE_NOT_REQUIRED = 0x0
1275

1276
"""
1277
    SOURCE_MODE_ABI
1278

1279
Indicates to inference that it should return a CodeInstance that can
1280
be `->invoke`'d (because it has already been compiled).
1281
"""
1282
const SOURCE_MODE_ABI = 0x1
1283

1284
"""
1285
    SOURCE_MODE_GET_SOURCE
1286

1287
Indicates to inference that it should return a CodeInstance after it has
1288
prepared interp to be able to provide source code for it.
1289
"""
1290
const SOURCE_MODE_GET_SOURCE = 0xf
1291

1292
"""
1293
    ci_has_abi(interp::AbstractInterpreter, code::CodeInstance)
1294

1295
Determine whether this CodeInstance is something that could be invoked if
1296
interp gave it to the runtime system (either because it already has an ->invoke
1297
ptr, or because interp has source that could be compiled).
1298
"""
1299
function ci_has_abi(interp::AbstractInterpreter, code::CodeInstance)
1300
    (@atomic :acquire code.invoke) !== C_NULL && return true
×
1301
    return ci_has_source(interp, code)
×
1302
end
1303

1304
"""
1305
    ci_has_source(interp::AbstractInterpreter, code::CodeInstance)
1306

1307
Determine whether this CodeInstance is something that could be compiled from
1308
source that interp has.
1309
"""
1310
function ci_has_source(interp::AbstractInterpreter, code::CodeInstance)
×
1311
    codegen = codegen_cache(interp)
×
1312
    codegen === nothing && return false
×
1313
    use_const_api(code) && return true
×
1314
    haskey(codegen, code) && return true
×
1315
    inf = @atomic :monotonic code.inferred
×
1316
    if isa(inf, String)
×
1317
        inf = _uncompressed_ir(code, inf)
×
1318
    end
1319
    if code.owner === nothing
×
1320
        if isa(inf, CodeInfo)
×
1321
            codegen[code] = inf
×
1322
            return true
×
1323
        end
1324
    elseif inf !== nothing
×
1325
        return true
×
1326
    end
1327
    return false
×
1328
end
1329

1330
function ci_has_invoke(code::CodeInstance)
1331
    return (@atomic :monotonic code.invoke) !== C_NULL
30✔
1332
end
1333

1334
function ci_meets_requirement(interp::AbstractInterpreter, code::CodeInstance, source_mode::UInt8)
1335
    source_mode == SOURCE_MODE_NOT_REQUIRED && return true
285✔
1336
    source_mode == SOURCE_MODE_ABI && return ci_has_abi(interp, code)
246✔
1337
    source_mode == SOURCE_MODE_GET_SOURCE && return ci_has_source(interp, code)
246✔
1338
    return false
×
1339
end
1340

1341
# compute (and cache) an inferred AST and return type
1342
function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance, source_mode::UInt8)
402✔
1343
    start_time = ccall(:jl_typeinf_timing_begin, UInt64, ())
402✔
1344
    let code = get(code_cache(interp), mi, nothing)
591✔
1345
        if code isa CodeInstance
402✔
1346
            # see if this code already exists in the cache
1347
            if ci_meets_requirement(interp, code, source_mode)
339✔
1348
                ccall(:jl_typeinf_timing_end, Cvoid, (UInt64,), start_time)
93✔
1349
                return code
93✔
1350
            end
1351
        end
1352
    end
1353
    def = mi.def
309✔
1354
    ci = engine_reserve(interp, mi)
309✔
1355
    # check cache again if it is still new after reserving in the engine
1356
    let code = get(code_cache(interp), mi, nothing)
405✔
1357
        if code isa CodeInstance
309✔
1358
            # see if this code already exists in the cache
1359
            if ci_meets_requirement(interp, code, source_mode)
192✔
1360
                engine_reject(interp, ci)
×
1361
                ccall(:jl_typeinf_timing_end, Cvoid, (UInt64,), start_time)
×
1362
                return code
×
1363
            end
1364
        end
1365
    end
1366
    if !InferenceParams(interp).force_enable_inference
309✔
1367
        if isa(def, Method) && ccall(:jl_get_module_infer, Cint, (Any,), def.module) == 0
222✔
1368
            src = retrieve_code_info(mi, get_inference_world(interp))
×
1369
            if src isa CodeInfo
×
1370
                finish!(interp, mi, ci, src)
×
1371
            else
1372
                engine_reject(interp, ci)
×
1373
            end
1374
            ccall(:jl_typeinf_timing_end, Cvoid, (UInt64,), start_time)
×
1375
            return ci
×
1376
        end
1377
    end
1378
    result = InferenceResult(mi, typeinf_lattice(interp))
618✔
1379
    result.ci = ci
309✔
1380
    frame = InferenceState(result, #=cache_mode=#:global, interp)
618✔
1381
    if frame === nothing
309✔
1382
        engine_reject(interp, ci)
×
1383
        ccall(:jl_typeinf_timing_end, Cvoid, (UInt64,), start_time)
×
1384
        return nothing
×
1385
    end
1386
    typeinf(interp, frame)
309✔
1387
    ccall(:jl_typeinf_timing_end, Cvoid, (UInt64,), start_time)
309✔
1388

1389
    ci = result.ci # reload from result in case it changed
309✔
1390
    codegen = codegen_cache(interp)
309✔
1391
    @assert frame.cache_mode != CACHE_MODE_NULL
309✔
1392
    @assert is_result_constabi_eligible(result) || codegen === nothing || haskey(codegen, ci)
468✔
1393
    @assert is_result_constabi_eligible(result) == use_const_api(ci)
309✔
1394
    @assert isdefined(ci, :inferred) "interpreter did not fulfill our expectations"
309✔
1395
    return ci
309✔
1396
end
1397

1398
# compute (and cache) an inferred AST and return the inferred return type
1399
function typeinf_type(interp::AbstractInterpreter, method::Method, @nospecialize(atype), sparams::SimpleVector)
×
1400
    if contains_is(unwrap_unionall(atype).parameters, Union{})
×
1401
        return Union{} # don't ask: it does weird and unnecessary things, if it occurs during bootstrap
×
1402
    end
1403
    return typeinf_type(interp, specialize_method(method, atype, sparams))
×
1404
end
1405
typeinf_type(interp::AbstractInterpreter, match::MethodMatch) =
663,187✔
1406
    typeinf_type(interp, specialize_method(match))
1407
function typeinf_type(interp::AbstractInterpreter, mi::MethodInstance)
44✔
1408
    ci = typeinf_ext(interp, mi, SOURCE_MODE_NOT_REQUIRED)
663,184✔
1409
    ci isa CodeInstance || return nothing
663,193✔
1410
    return ci.rettype
663,175✔
1411
end
1412

1413
# Resolve a call, as described by `argtype` to a single matching
1414
# Method and return a compilable MethodInstance for the call, if
1415
# it will be runtime-dispatched to exactly that MethodInstance
1416
function compileable_specialization_for_call(interp::AbstractInterpreter, @nospecialize(argtype))
×
1417
    mt = ccall(:jl_method_table_for, Any, (Any,), argtype)
×
1418
    if mt === nothing
×
1419
        # this would require scanning all method tables, so give up instead
1420
        return nothing
×
1421
    end
1422

1423
    matches = findall(argtype, method_table(interp); limit = 1)
×
1424
    matches === nothing && return nothing
×
1425
    length(matches.matches) == 0 && return nothing
×
1426
    match = only(matches.matches)
×
1427

1428
    compileable_atype = get_compileable_sig(match.method, match.spec_types, match.sparams)
×
1429
    compileable_atype === nothing && return nothing
×
1430
    if match.spec_types !== compileable_atype
×
1431
        sp_ = ccall(:jl_type_intersection_with_env, Any, (Any, Any), compileable_atype, match.method.sig)::SimpleVector
×
1432
        sparams = sp_[2]::SimpleVector
×
1433
        mi = specialize_method(match.method, compileable_atype, sparams)
×
1434
    else
1435
        mi = specialize_method(match.method, compileable_atype, match.sparams)
×
1436
    end
1437

1438
    return mi
×
1439
end
1440

1441
const QueueItems = Union{CodeInstance,MethodInstance,SimpleVector}
1442

1443
struct CompilationQueue
1444
    tocompile::Vector{QueueItems}
1445
    inspected::IdSet{QueueItems}
1446
    interp::Union{AbstractInterpreter,Nothing}
1447

1448
    CompilationQueue(;
×
1449
        interp::Union{AbstractInterpreter,Nothing}
1450
    ) = new(QueueItems[], IdSet{QueueItems}(), interp)
138✔
1451

1452
    CompilationQueue(queue::CompilationQueue;
×
1453
        interp::Union{AbstractInterpreter,Nothing}
1454
    ) = new(empty!(queue.tocompile), empty!(queue.inspected), interp)
54✔
1455
end
1456

1457
Base.push!(queue::CompilationQueue, item) = push!(queue.tocompile, item)
225✔
1458
Base.append!(queue::CompilationQueue, items) = append!(queue.tocompile, items)
27✔
1459
Base.pop!(queue::CompilationQueue) = pop!(queue.tocompile)
510✔
1460
Base.empty!(queue::CompilationQueue) = (empty!(queue.tocompile); empty!(queue.inspected))
×
1461
markinspected!(queue::CompilationQueue, item) = push!(queue.inspected, item)
252✔
1462
isinspected(queue::CompilationQueue, item) = item in queue.inspected
483✔
1463
Base.isempty(queue::CompilationQueue) = isempty(queue.tocompile)
579✔
1464

1465
# collect a list of all code that is needed along with CodeInstance to codegen it fully
1466
function collectinvokes!(workqueue::CompilationQueue, ci::CodeInfo, sptypes::Vector{VarState};
288✔
1467
                         invokelatest_queue::Union{CompilationQueue,Nothing} = nothing)
1468
    src = ci.code
42✔
1469
    for i = 1:length(src)
42✔
1470
        stmt = src[i]
1,551✔
1471
        isexpr(stmt, :(=)) && (stmt = stmt.args[2])
1,551✔
1472
        if isexpr(stmt, :invoke) || isexpr(stmt, :invoke_modify)
3,051✔
1473
            edge = stmt.args[1]
51✔
1474
            edge isa CodeInstance && isdefined(edge, :inferred) && push!(workqueue, edge)
51✔
1475
        end
1476

1477
        invokelatest_queue === nothing && continue
1,551✔
1478
        if isexpr(stmt, :call)
1,428✔
1479
            farg = stmt.args[1]
690✔
1480
            !applicable(argextype, farg, ci, sptypes) && continue # TODO: Why is this failing during bootstrap
690✔
1481
            ftyp = widenconst(argextype(farg, ci, sptypes))
1,380✔
1482

1483
            if ftyp === typeof(Core.finalizer) && length(stmt.args) == 3
690✔
1484
                finalizer = argextype(stmt.args[2], ci, sptypes)
×
1485
                obj = argextype(stmt.args[3], ci, sptypes)
×
1486
                atype = argtypes_to_type(Any[finalizer, obj])
×
1487
            else
1488
                # No dynamic dispatch to resolve / enqueue
1489
                continue
690✔
1490
            end
1491
        elseif isexpr(stmt, :cfunction) && length(stmt.args) == 5
738✔
1492
            (_, f, _, at, _) = stmt.args
×
1493
            linfo = ci.parent
×
1494

1495
            linfo isa MethodInstance || continue
×
1496
            at isa SimpleVector || continue
×
1497

1498
            ft = argextype(f, ci, sptypes)
×
1499
            argtypes = Any[ft]
×
1500
            for i = 1:length(at)
×
1501
                push!(argtypes, sp_type_rewrap(at[i], linfo, #= isreturn =# false))
×
1502
            end
×
1503
            atype = argtypes_to_type(argtypes)
×
1504
        else
1505
            # TODO: handle other StmtInfo like OpaqueClosure?
1506
            continue
738✔
1507
        end
1508
        let workqueue = invokelatest_queue
×
1509
            # make a best-effort attempt to enqueue the relevant code for the dynamic invokelatest call
1510
            mi = compileable_specialization_for_call(workqueue.interp, atype)
×
1511
            mi === nothing && continue
×
1512

1513
            push!(workqueue, mi)
×
1514
        end
1515
    end
1,659✔
1516
end
1517

1518
function add_codeinsts_to_jit!(interp::AbstractInterpreter, ci, source_mode::UInt8)
15✔
1519
    source_mode == SOURCE_MODE_ABI || return ci
15✔
1520
    ci isa CodeInstance && !ci_has_invoke(ci) || return ci
15✔
1521
    codegen = codegen_cache(interp)
15✔
1522
    codegen === nothing && return ci
15✔
1523
    workqueue = CompilationQueue(; interp)
15✔
1524
    push!(workqueue, ci)
15✔
1525
    while !isempty(workqueue)
30✔
1526
        # ci_has_real_invoke(ci) && return ci # optimization: cease looping if ci happens to get compiled (not just jl_fptr_wait_for_compiled, but fully jl_is_compiled_codeinst)
1527
        callee = pop!(workqueue)
15✔
1528
        ci_has_invoke(callee) && continue
15✔
1529
        isinspected(workqueue, callee) && continue
15✔
1530
        src = get(codegen, callee, nothing)
24✔
1531
        if !isa(src, CodeInfo)
15✔
1532
            src = @atomic :monotonic callee.inferred
×
1533
            if isa(src, String)
×
1534
                src = _uncompressed_ir(callee, src)
×
1535
            end
1536
            if !isa(src, CodeInfo)
×
1537
                newcallee = typeinf_ext(workqueue.interp, callee.def, source_mode) # always SOURCE_MODE_ABI
×
1538
                if newcallee isa CodeInstance
×
1539
                    callee === ci && (ci = newcallee) # ci stopped meeting the requirements after typeinf_ext last checked, try again with newcallee
×
1540
                    push!(workqueue, newcallee)
×
1541
                end
1542
                if newcallee !== callee
×
1543
                    markinspected!(workqueue, callee)
×
1544
                end
1545
                continue
×
1546
            end
1547
        end
1548
        markinspected!(workqueue, callee)
15✔
1549
        mi = get_ci_mi(callee)
30✔
1550
        sptypes = sptypes_from_meth_instance(mi)
15✔
1551
        collectinvokes!(workqueue, src, sptypes)
123✔
1552
        if iszero(ccall(:jl_mi_cache_has_ci, Cint, (Any, Any), mi, callee))
15✔
1553
            cached = ccall(:jl_get_ci_equiv, Any, (Any, UInt), callee, get_inference_world(workqueue.interp))::CodeInstance
×
1554
            if cached === callee
×
1555
                # make sure callee is gc-rooted and cached, as required by jl_add_codeinst_to_jit
1556
                code_cache(workqueue.interp)[mi] = callee
×
1557
            else
1558
                # use an existing CI from the cache, if there is available one that is compatible
1559
                callee === ci && (ci = cached)
×
1560
                callee = cached
×
1561
            end
1562
        end
1563
        ccall(:jl_add_codeinst_to_jit, Cvoid, (Any, Any), callee, src)
15✔
1564
    end
15✔
1565
    return ci
15✔
1566
end
1567

1568
function typeinf_ext_toplevel(interp::AbstractInterpreter, mi::MethodInstance, source_mode::UInt8)
9✔
1569
    ci = typeinf_ext(interp, mi, source_mode)
15✔
1570
    ci = add_codeinsts_to_jit!(interp, ci, source_mode)
15✔
1571
    return ci
15✔
1572
end
1573

1574
# This is a bridge for the C code calling `jl_typeinf_func()` on a single Method match
1575
function typeinf_ext_toplevel(mi::MethodInstance, world::UInt, source_mode::UInt8, trim_mode::UInt8)
×
1576
    inf_params = InferenceParams(; force_enable_inference = trim_mode != TRIM_NO)
×
1577
    interp = NativeInterpreter(world; inf_params)
×
1578
    return typeinf_ext_toplevel(interp, mi, source_mode)
×
1579
end
1580

1581
function compile!(codeinfos::Vector{Any}, workqueue::CompilationQueue;
108✔
1582
    invokelatest_queue::Union{CompilationQueue,Nothing} = nothing,
1583
)
1584
    interp = workqueue.interp
54✔
1585
    world = get_inference_world(interp)
54✔
1586
    while !isempty(workqueue)
549✔
1587
        item = pop!(workqueue)
495✔
1588
        # each item in this list is either a MethodInstance indicating something
1589
        # to compile, or an svec(rettype, sig) describing a C-callable alias to create.
1590
        if item isa MethodInstance
495✔
1591
            isinspected(workqueue, item) && continue
6✔
1592
            # if this method is generally visible to the current compilation world,
1593
            # and this is either the primary world, or not applicable in the primary world
1594
            # then we want to compile and emit this
1595
            if item.def.primary_world <= world
6✔
1596
                ci = typeinf_ext(interp, item, SOURCE_MODE_GET_SOURCE)
6✔
1597
                ci isa CodeInstance && push!(workqueue, ci)
6✔
1598
            end
1599
            markinspected!(workqueue, item)
6✔
1600
        elseif item isa SimpleVector
489✔
1601
            invokelatest_queue === nothing && continue
27✔
1602
            (rt::Type, sig::Type) = item
27✔
1603
            # make a best-effort attempt to enqueue the relevant code for the ccallable
1604
            mi = ccall(:jl_get_specialization1, Any,
27✔
1605
                        (Any, Csize_t, Cint),
1606
                        sig, world, #= mt_cache =# 0)
1607
            if mi !== nothing
27✔
1608
                mi = mi::MethodInstance
27✔
1609
                ci = typeinf_ext(interp, mi, SOURCE_MODE_GET_SOURCE)
27✔
1610
                ci isa CodeInstance && push!(invokelatest_queue, ci)
27✔
1611
            end
1612
            # additionally enqueue the ccallable entrypoint / adapter, which implicitly
1613
            # invokes the above ci
1614
            push!(codeinfos, item)
27✔
1615
        elseif item isa CodeInstance
462✔
1616
            callee = item
462✔
1617
            isinspected(workqueue, callee) && continue
462✔
1618
            mi = get_ci_mi(callee)
540✔
1619
            # now make sure everything has source code, if desired
1620
            if use_const_api(callee)
270✔
1621
                src = codeinfo_for_const(interp, mi, WorldRange(callee.min_world, callee.max_world), callee.edges, callee.rettype_const)
×
1622
            else
1623
                src = get(interp.codegen, callee, nothing)
270✔
1624
                if src === nothing
270✔
1625
                    newcallee = typeinf_ext(interp, mi, SOURCE_MODE_GET_SOURCE)
132✔
1626
                    if newcallee isa CodeInstance
132✔
1627
                        @assert use_const_api(newcallee) || haskey(interp.codegen, newcallee)
264✔
1628
                        push!(workqueue, newcallee)
132✔
1629
                    end
1630
                    if newcallee !== callee
132✔
1631
                        markinspected!(workqueue, callee)
93✔
1632
                    end
1633
                    continue
132✔
1634
                end
1635
            end
1636
            markinspected!(workqueue, callee)
138✔
1637
            if src isa CodeInfo
138✔
1638
                sptypes = sptypes_from_meth_instance(mi)
138✔
1639
                collectinvokes!(workqueue, src, sptypes; invokelatest_queue)
138✔
1640
                # try to reuse an existing CodeInstance from before to avoid making duplicates in the cache
1641
                if iszero(ccall(:jl_mi_cache_has_ci, Cint, (Any, Any), mi, callee))
138✔
1642
                    cached = ccall(:jl_get_ci_equiv, Any, (Any, UInt), callee, world)::CodeInstance
96✔
1643
                    if cached === callee
96✔
1644
                        code_cache(interp)[mi] = callee
×
1645
                    else
1646
                        # Use an existing CI from the cache, if there is available one that is compatible
1647
                        callee = cached
96✔
1648
                    end
1649
                end
1650
                push!(codeinfos, callee)
138✔
1651
                push!(codeinfos, src)
138✔
1652
            end
1653
        else @assert false "unexpected item in queue" end
×
1654
    end
495✔
1655
    return codeinfos
54✔
1656
end
1657

1658
# This is a bridge for the C code calling `jl_typeinf_func()` on set of Method matches
1659
# The trim_mode can be any of:
1660
const TRIM_NO = 0x0
1661
const TRIM_SAFE = 0x1
1662
const TRIM_UNSAFE = 0x2
1663
const TRIM_UNSAFE_WARN = 0x3
1664
function typeinf_ext_toplevel(methods::Vector{Any}, worlds::Vector{UInt}, trim_mode::UInt8)
27✔
1665
    inf_params = InferenceParams(; force_enable_inference = trim_mode != TRIM_NO)
27✔
1666

1667
    # Create an "invokelatest" queue to enable eager compilation of speculative
1668
    # invokelatest calls such as from `Core.finalizer` and `ccallable`
1669
    invokelatest_queue = CompilationQueue(;
27✔
1670
        interp = NativeInterpreter(get_world_counter(); inf_params)
1671
    )
1672

1673
    codeinfos = []
27✔
1674
    workqueue = CompilationQueue(; interp = nothing)
27✔
1675
    for this_world in reverse!(sort!(worlds))
27✔
1676
        workqueue = CompilationQueue(workqueue;
27✔
1677
            interp = NativeInterpreter(this_world; inf_params)
1678
        )
1679

1680
        append!(workqueue, methods)
27✔
1681
        compile!(codeinfos, workqueue; invokelatest_queue)
27✔
1682
    end
27✔
1683

1684
    if invokelatest_queue !== nothing
27✔
1685
        # This queue is intentionally aliased, to handle e.g. a `finalizer` calling `Core.finalizer`
1686
        # (it will enqueue into itself and immediately drain)
1687
        compile!(codeinfos, invokelatest_queue; invokelatest_queue)
27✔
1688
    end
1689

1690
    if trim_mode != TRIM_NO && trim_mode != TRIM_UNSAFE
27✔
1691
        verify_typeinf_trim(codeinfos, trim_mode == TRIM_UNSAFE_WARN)
6✔
1692
    end
1693
    return codeinfos
27✔
1694
end
1695

1696
const _verify_trim_world_age = RefValue{UInt}(typemax(UInt))
1697
verify_typeinf_trim(codeinfos::Vector{Any}, onlywarn::Bool) = Core._call_in_world(_verify_trim_world_age[], verify_typeinf_trim, stdout, codeinfos, onlywarn)
6✔
1698

1699
function return_type(@nospecialize(f), t::DataType) # this method has a special tfunc
1,113✔
1700
    world = tls_world_age()
1,113✔
1701
    args = Any[_return_type, NativeInterpreter(world), Tuple{Core.Typeof(f), t.parameters...}]
3,251✔
1702
    return ccall(:jl_call_in_typeinf_world, Any, (Ptr{Any}, Cint), args, length(args))
1,113✔
1703
end
1704

1705
function return_type(@nospecialize(f), t::DataType, world::UInt)
×
1706
    return return_type(Tuple{Core.Typeof(f), t.parameters...}, world)
×
1707
end
1708

1709
function return_type(t::DataType)
×
1710
    world = tls_world_age()
×
1711
    return return_type(t, world)
×
1712
end
1713

1714
function return_type(t::DataType, world::UInt)
×
1715
    args = Any[_return_type, NativeInterpreter(world), t]
×
1716
    return ccall(:jl_call_in_typeinf_world, Any, (Ptr{Any}, Cint), args, length(args))
×
1717
end
1718

1719
function _return_type(interp::AbstractInterpreter, t::DataType)
17,932✔
1720
    rt = Union{}
17,932✔
1721
    f = singleton_type(t.parameters[1])
35,820✔
1722
    if isa(f, Builtin)
17,932✔
1723
        args = Any[t.parameters...]
6✔
1724
        popfirst!(args)
6✔
1725
        rt = builtin_tfunction(interp, f, args, nothing)
6✔
1726
        rt = widenconst(rt)
6✔
1727
    else
1728
        for match in _methods_by_ftype(t, -1, get_inference_world(interp))::Vector
17,926✔
1729
            ty = typeinf_type(interp, match::MethodMatch)
19,561✔
1730
            ty === nothing && return Any
19,558✔
1731
            rt = tmerge(rt, ty)
19,555✔
1732
            rt === Any && break
19,555✔
1733
        end
18,859✔
1734
    end
1735
    return rt
17,929✔
1736
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc