104
104
function finish! (interp:: AbstractInterpreter , caller:: InferenceState , validation_world:: UInt , time_before:: UInt64 )
105
105
result = caller. result
106
106
# @assert last(result.valid_worlds) <= get_world_counter() || isempty(caller.edges)
107
- if isdefined (result, :ci )
107
+ if caller. cache_mode === CACHE_MODE_LOCAL
108
+ @assert ! isdefined (result, :ci )
109
+ result. src = transform_result_for_local_cache (interp, result)
110
+ elseif isdefined (result, :ci )
108
111
edges = result_edges (interp, caller)
109
112
ci = result. ci
110
113
# if we aren't cached, we don't need this edge
@@ -115,11 +118,16 @@ function finish!(interp::AbstractInterpreter, caller::InferenceState, validation
115
118
store_backedges (ci, edges)
116
119
end
117
120
inferred_result = nothing
118
- uncompressed = inferred_result
121
+ uncompressed = result . src
119
122
const_flag = is_result_constabi_eligible (result)
123
+ debuginfo = get_debuginfo (result. src)
120
124
discard_src = caller. cache_mode === CACHE_MODE_NULL || const_flag
121
125
if ! discard_src
122
126
inferred_result = transform_result_for_cache (interp, result, edges)
127
+ if inferred_result != = nothing
128
+ uncompressed = inferred_result
129
+ debuginfo = get_debuginfo (inferred_result)
130
+ end
123
131
# 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)?
124
132
if inferred_result isa CodeInfo
125
133
result. src = inferred_result
@@ -128,27 +136,28 @@ function finish!(interp::AbstractInterpreter, caller::InferenceState, validation
128
136
resize! (inferred_result. slottypes:: Vector{Any} , nslots)
129
137
resize! (inferred_result. slotnames, nslots)
130
138
end
131
- di = inferred_result. debuginfo
132
- uncompressed = inferred_result
133
139
inferred_result = maybe_compress_codeinfo (interp, result. linfo, inferred_result)
134
140
result. is_src_volatile = false
135
141
elseif ci. owner === nothing
136
142
# The global cache can only handle objects that codegen understands
137
143
inferred_result = nothing
138
144
end
139
145
end
140
- if ! @isdefined di
141
- di = DebugInfo (result. linfo)
146
+ if debuginfo === nothing
147
+ debuginfo = DebugInfo (result. linfo)
142
148
end
143
149
time_now = _time_ns ()
144
150
time_self_ns = caller. time_self_ns + (time_now - time_before)
145
151
time_total = (time_now - caller. time_start - caller. time_paused) * 1e-9
146
152
ccall (:jl_update_codeinst , Cvoid, (Any, Any, Int32, UInt, UInt, UInt32, Any, Float64, Float64, Float64, Any, Any),
147
153
ci, inferred_result, const_flag, first (result. valid_worlds), last (result. valid_worlds), encode_effects (result. ipo_effects),
148
- result. analysis_results, time_total, caller. time_caches, time_self_ns * 1e-9 , di , edges)
154
+ result. analysis_results, time_total, caller. time_caches, time_self_ns * 1e-9 , debuginfo , edges)
149
155
engine_reject (interp, ci)
150
156
codegen = codegen_cache (interp)
151
- if ! discard_src && codegen != = nothing && uncompressed isa CodeInfo
157
+ if ! discard_src && codegen != = nothing && (isa (uncompressed, CodeInfo) || isa (uncompressed, OptimizationState))
158
+ if isa (uncompressed, OptimizationState)
159
+ uncompressed = ir_to_codeinf! (uncompressed, edges)
160
+ end
152
161
# record that the caller could use this result to generate code when required, if desired, to avoid repeating n^2 work
153
162
codegen[ci] = uncompressed
154
163
if bootstrapping_compiler && inferred_result == nothing
@@ -299,36 +308,113 @@ function adjust_cycle_frame!(sv::InferenceState, cycle_valid_worlds::WorldRange,
299
308
return nothing
300
309
end
301
310
311
+ function get_debuginfo (src)
312
+ isa (src, CodeInfo) && return src. debuginfo
313
+ isa (src, OptimizationState) && return src. src. debuginfo
314
+ return nothing
315
+ end
316
+
302
317
function is_result_constabi_eligible (result:: InferenceResult )
303
318
result_type = result. result
304
319
return isa (result_type, Const) && is_foldable_nothrow (result. ipo_effects) && is_inlineable_constant (result_type. val)
305
320
end
306
321
307
- function transform_result_for_cache (:: AbstractInterpreter , result:: InferenceResult , edges:: SimpleVector )
322
+ function compute_inlining_cost (interp:: AbstractInterpreter , result:: InferenceResult )
323
+ src = result. src
324
+ isa (src, OptimizationState) || return MAX_INLINE_COST
325
+ compute_inlining_cost (interp, result, src. optresult)
326
+ end
327
+
328
+ function compute_inlining_cost (interp:: AbstractInterpreter , result:: InferenceResult , optresult#= ::OptimizationResult=# )
329
+ return inline_cost_model (interp, result, optresult. inline_flag, optresult. ir)
330
+ end
331
+
332
+ function inline_cost_model (interp:: AbstractInterpreter , result:: InferenceResult ,
333
+ inline_flag:: UInt8 , ir:: IRCode )
334
+
335
+ inline_flag === SRC_FLAG_DECLARED_NOINLINE && return MAX_INLINE_COST
336
+
337
+ mi = result. linfo
338
+ (; def, specTypes) = mi
339
+ if ! isa (def, Method)
340
+ return MAX_INLINE_COST
341
+ end
342
+
343
+ declared_inline = inline_flag === SRC_FLAG_DECLARED_INLINE
344
+
345
+ rt = result. result
346
+ @assert ! (rt isa LimitedAccuracy)
347
+ rt = widenslotwrapper (rt)
348
+
349
+ sig = unwrap_unionall (specTypes)
350
+ if ! (isa (sig, DataType) && sig. name === Tuple. name)
351
+ return MAX_INLINE_COST
352
+ end
353
+ if ! declared_inline && rt === Bottom
354
+ return MAX_INLINE_COST
355
+ end
356
+
357
+ if declared_inline && isdispatchtuple (specTypes)
358
+ # obey @inline declaration if a dispatch barrier would not help
359
+ return MIN_INLINE_COST
360
+ else
361
+ # compute the cost (size) of inlining this code
362
+ params = OptimizationParams (interp)
363
+ cost_threshold = default = params. inline_cost_threshold
364
+ if ⊑ (optimizer_lattice (interp), rt, Tuple) && ! isconcretetype (widenconst (rt))
365
+ cost_threshold += params. inline_tupleret_bonus
366
+ end
367
+ # if the method is declared as `@inline`, increase the cost threshold 20x
368
+ if declared_inline
369
+ cost_threshold += 19 * default
370
+ end
371
+ # a few functions get special treatment
372
+ if def. module === _topmod (def. module)
373
+ name = def. name
374
+ if name === :iterate || name === :unsafe_convert || name === :cconvert
375
+ cost_threshold += 4 * default
376
+ end
377
+ end
378
+ return inline_cost_model (ir, params, cost_threshold)
379
+ end
380
+ end
381
+
382
+ function transform_result_for_local_cache (interp:: AbstractInterpreter , result:: InferenceResult )
308
383
src = result. src
309
384
if isa (src, OptimizationState)
310
- src = ir_to_codeinf! (src)
385
+ # Compute and store any information required to determine the inlineability of the callee.
386
+ opt = src
387
+ opt. src. inlining_cost = compute_inlining_cost (interp, result)
388
+ end
389
+ return src
390
+ end
391
+
392
+ function transform_result_for_cache (interp:: AbstractInterpreter , result:: InferenceResult , edges:: SimpleVector )
393
+ inlining_cost = nothing
394
+ src = result. src
395
+ if isa (src, OptimizationState)
396
+ opt = src
397
+ inlining_cost = compute_inlining_cost (interp, result, opt. optresult)
398
+ discard_optimized_result (interp, opt, inlining_cost) && return nothing
399
+ src = ir_to_codeinf! (opt)
311
400
end
312
401
if isa (src, CodeInfo)
313
402
src. edges = edges
403
+ src. inlining_cost = inlining_cost != = nothing ? inlining_cost : compute_inlining_cost (interp, result)
314
404
end
315
405
return src
316
406
end
317
407
408
+ function discard_optimized_result (interp:: AbstractInterpreter , opt#= ::OptimizationState=# , inlining_cost#= ::InlineCostType=# )
409
+ may_discard_trees (interp) || return false
410
+ return inlining_cost == MAX_INLINE_COST
411
+ end
412
+
318
413
function maybe_compress_codeinfo (interp:: AbstractInterpreter , mi:: MethodInstance , ci:: CodeInfo )
319
414
def = mi. def
320
415
isa (def, Method) || return ci # don't compress toplevel code
321
- can_discard_trees = may_discard_trees (interp)
322
- cache_the_tree = ! can_discard_trees || is_inlineable (ci)
323
- if cache_the_tree
324
- if may_compress (interp)
325
- return ccall (:jl_compress_ir , String, (Any, Any), def, ci)
326
- else
327
- return ci
328
- end
329
- else
330
- return nothing
331
- end
416
+ may_compress (interp) && return ccall (:jl_compress_ir , String, (Any, Any), def, ci)
417
+ return ci
332
418
end
333
419
334
420
function cache_result! (interp:: AbstractInterpreter , result:: InferenceResult , ci:: CodeInstance )
@@ -1101,8 +1187,7 @@ function typeinf_frame(interp::AbstractInterpreter, mi::MethodInstance, run_opti
1101
1187
else
1102
1188
opt = OptimizationState (frame, interp)
1103
1189
optimize (interp, opt, frame. result)
1104
- src = ir_to_codeinf! (opt)
1105
- src. edges = Core. svec (opt. inlining. edges... )
1190
+ src = ir_to_codeinf! (opt, frame, Core. svec (opt. inlining. edges... ))
1106
1191
end
1107
1192
result. src = frame. src = src
1108
1193
end
0 commit comments