@@ -153,7 +153,7 @@ function finish!(interp::AbstractInterpreter, caller::InferenceState, validation
153153 # when compiling the compiler to inject everything eagerly
154154 # where codegen can start finding and using it right away
155155 mi = result. linfo
156- if mi. def isa Method && isa_compileable_sig (mi)
156+ if mi. def isa Method && isa_compileable_sig (mi) && is_cached (caller)
157157 ccall (:jl_add_codeinst_to_jit , Cvoid, (Any, Any), ci, uncompressed)
158158 end
159159 end
@@ -1099,39 +1099,62 @@ end
10991099"""
11001100 SOURCE_MODE_NOT_REQUIRED
11011101
1102- Indicates to inference that the source is not required and the only fields
1103- of the resulting `CodeInstance` that the caller is interested in are types
1104- and effects. Inference is still free to create a CodeInstance with source,
1105- but is not required to do so.
1102+ Indicates to inference that the source is not required and the only fields of
1103+ the resulting `CodeInstance` that the caller is interested in are return or
1104+ exception types and IPO effects. Inference is still free to create source for
1105+ it or add it to the JIT even, but is not required or expected to do so.
11061106"""
11071107const SOURCE_MODE_NOT_REQUIRED = 0x0
11081108
11091109"""
11101110 SOURCE_MODE_ABI
11111111
11121112Indicates to inference that it should return a CodeInstance that can
1113- either be `->invoke`'d (because it has already been compiled or because
1114- it has constabi) or one that can be made so by compiling its `->inferred`
1115- field.
1116-
1117- N.B.: The `->inferred` field is volatile and the compiler may delete it.
1113+ be `->invoke`'d (because it has already been compiled).
11181114"""
11191115const SOURCE_MODE_ABI = 0x1
11201116
11211117"""
1122- ci_has_abi(code::CodeInstance)
1118+ SOURCE_MODE_GET_SOURCE
1119+
1120+ Indicates to inference that it should return a CodeInstance after it has
1121+ prepared interp to be able to provide source code for it.
1122+ """
1123+ const SOURCE_MODE_GET_SOURCE = 0xf
11231124
1124- Determine whether this CodeInstance is something that could be invoked if we gave it
1125- to the runtime system (either because it already has an ->invoke ptr, or
1126- because it has source that could be compiled). Note that this information may
1127- be stale by the time the user see it, so the user will need to perform their
1128- own checks if they actually need the abi from it.
11291125"""
1130- function ci_has_abi (code:: CodeInstance )
1126+ ci_has_abi(interp::AbstractInterpreter, code::CodeInstance)
1127+
1128+ Determine whether this CodeInstance is something that could be invoked if
1129+ interp gave it to the runtime system (either because it already has an ->invoke
1130+ ptr, or because interp has source that could be compiled).
1131+ """
1132+ function ci_has_abi (interp:: AbstractInterpreter , code:: CodeInstance )
11311133 (@atomic :acquire code. invoke) != = C_NULL && return true
1134+ return ci_has_source (interp, code)
1135+ end
1136+
1137+ """
1138+ ci_has_source(interp::AbstractInterpreter, code::CodeInstance)
1139+
1140+ Determine whether this CodeInstance is something that could be compiled from
1141+ source that interp has.
1142+ """
1143+ function ci_has_source (interp:: AbstractInterpreter , code:: CodeInstance )
1144+ codegen = codegen_cache (interp)
1145+ codegen === nothing && return false
1146+ use_const_api (code) && return true
1147+ haskey (codegen, code) && return true
11321148 inf = @atomic :monotonic code. inferred
1133- if code. owner === nothing ? (isa (inf, CodeInfo) || isa (inf, String)) : inf != = nothing
1134- # interp.codegen[code] = maybe_uncompress(code, inf) # TODO : the correct way to ensure this information doesn't become stale would be to push it into the stable codegen cache
1149+ if isa (inf, String)
1150+ inf = _uncompressed_ir (code, inf)
1151+ end
1152+ if code. owner === nothing
1153+ if isa (inf, CodeInfo)
1154+ codegen[code] = inf
1155+ return true
1156+ end
1157+ elseif inf != = nothing
11351158 return true
11361159 end
11371160 return false
@@ -1141,9 +1164,10 @@ function ci_has_invoke(code::CodeInstance)
11411164 return (@atomic :monotonic code. invoke) != = C_NULL
11421165end
11431166
1144- function ci_meets_requirement (code:: CodeInstance , source_mode:: UInt8 )
1167+ function ci_meets_requirement (interp :: AbstractInterpreter , code:: CodeInstance , source_mode:: UInt8 )
11451168 source_mode == SOURCE_MODE_NOT_REQUIRED && return true
1146- source_mode == SOURCE_MODE_ABI && return ci_has_abi (code)
1169+ source_mode == SOURCE_MODE_ABI && return ci_has_abi (interp, code)
1170+ source_mode == SOURCE_MODE_GET_SOURCE && return ci_has_source (interp, code)
11471171 return false
11481172end
11491173
@@ -1153,7 +1177,7 @@ function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance, source_mod
11531177 let code = get (code_cache (interp), mi, nothing )
11541178 if code isa CodeInstance
11551179 # see if this code already exists in the cache
1156- if ci_meets_requirement (code, source_mode)
1180+ if ci_meets_requirement (interp, code, source_mode)
11571181 ccall (:jl_typeinf_timing_end , Cvoid, (UInt64,), start_time)
11581182 return code
11591183 end
@@ -1165,7 +1189,7 @@ function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance, source_mod
11651189 let code = get (code_cache (interp), mi, nothing )
11661190 if code isa CodeInstance
11671191 # see if this code already exists in the cache
1168- if ci_meets_requirement (code, source_mode)
1192+ if ci_meets_requirement (interp, code, source_mode)
11691193 engine_reject (interp, ci)
11701194 ccall (:jl_typeinf_timing_end , Cvoid, (UInt64,), start_time)
11711195 return code
@@ -1196,18 +1220,11 @@ function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance, source_mod
11961220 ccall (:jl_typeinf_timing_end , Cvoid, (UInt64,), start_time)
11971221
11981222 ci = result. ci # reload from result in case it changed
1223+ codegen = codegen_cache (interp)
11991224 @assert frame. cache_mode != CACHE_MODE_NULL
1200- @assert is_result_constabi_eligible (result) || begin
1201- codegen = codegen_cache (interp)
1202- codegen === nothing || haskey (codegen, ci)
1203- end
1225+ @assert is_result_constabi_eligible (result) || codegen === nothing || haskey (codegen, ci)
12041226 @assert is_result_constabi_eligible (result) == use_const_api (ci)
12051227 @assert isdefined (ci, :inferred ) " interpreter did not fulfill our expectations"
1206- if ! is_cached (frame) && source_mode == SOURCE_MODE_ABI
1207- # XXX : jl_type_infer somewhat ambiguously assumes this must be cached
1208- # XXX : this should be using the CI from the cache, if possible instead: haskey(cache, mi) && (ci = cache[mi])
1209- code_cache (interp)[mi] = ci
1210- end
12111228 return ci
12121229end
12131230
@@ -1221,35 +1238,9 @@ end
12211238typeinf_type (interp:: AbstractInterpreter , match:: MethodMatch ) =
12221239 typeinf_type (interp, specialize_method (match))
12231240function typeinf_type (interp:: AbstractInterpreter , mi:: MethodInstance )
1224- # n.b.: this could be replaced with @something(typeinf_ext(interp, mi, SOURCE_MODE_NOT_REQUIRED), return nothing).rettype
1225- start_time = ccall (:jl_typeinf_timing_begin , UInt64, ())
1226- let code = get (code_cache (interp), mi, nothing )
1227- if code isa CodeInstance
1228- # see if this rettype already exists in the cache
1229- ccall (:jl_typeinf_timing_end , Cvoid, (UInt64,), start_time)
1230- return code. rettype
1231- end
1232- end
1233- ci = engine_reserve (interp, mi)
1234- let code = get (code_cache (interp), mi, nothing )
1235- if code isa CodeInstance
1236- engine_reject (interp, ci)
1237- # see if this rettype already exists in the cache
1238- ccall (:jl_typeinf_timing_end , Cvoid, (UInt64,), start_time)
1239- return code. rettype
1240- end
1241- end
1242- result = InferenceResult (mi, typeinf_lattice (interp))
1243- result. ci = ci
1244- frame = InferenceState (result, #= cache_mode=# :global , interp)
1245- if frame === nothing
1246- engine_reject (interp, ci)
1247- return nothing
1248- end
1249- typeinf (interp, frame)
1250- ccall (:jl_typeinf_timing_end , Cvoid, (UInt64,), start_time)
1251- is_inferred (result) || return nothing
1252- return widenconst (ignorelimited (result. result))
1241+ ci = typeinf_ext (interp, mi, SOURCE_MODE_NOT_REQUIRED)
1242+ ci isa CodeInstance || return nothing
1243+ return ci. rettype
12531244end
12541245
12551246# collect a list of all code that is needed along with CodeInstance to codegen it fully
@@ -1286,18 +1277,31 @@ function add_codeinsts_to_jit!(interp::AbstractInterpreter, ci, source_mode::UIn
12861277 src = _uncompressed_ir (callee, src)
12871278 end
12881279 if ! isa (src, CodeInfo)
1289- newcallee = typeinf_ext (interp, callee. def, source_mode)
1280+ newcallee = typeinf_ext (interp, callee. def, source_mode) # always SOURCE_MODE_ABI
12901281 if newcallee isa CodeInstance
12911282 callee === ci && (ci = newcallee) # ci stopped meeting the requirements after typeinf_ext last checked, try again with newcallee
12921283 push! (tocompile, newcallee)
1293- # else
1294- # println("warning: could not get source code for ", callee.def)
1284+ end
1285+ if newcallee != = callee
1286+ push! (inspected, callee)
12951287 end
12961288 continue
12971289 end
12981290 end
12991291 push! (inspected, callee)
13001292 collectinvokes! (tocompile, src)
1293+ mi = get_ci_mi (callee)
1294+ if iszero (ccall (:jl_mi_cache_has_ci , Cint, (Any, Any), mi, callee))
1295+ cached = ccall (:jl_get_ci_equiv , Any, (Any, UInt), callee, get_inference_world (interp)):: CodeInstance
1296+ if cached === callee
1297+ # make sure callee is gc-rooted and cached, as required by jl_add_codeinst_to_jit
1298+ code_cache (interp)[mi] = callee
1299+ else
1300+ # use an existing CI from the cache, if there is available one that is compatible
1301+ callee === ci && (ci = cached)
1302+ callee = cached
1303+ end
1304+ end
13011305 ccall (:jl_add_codeinst_to_jit , Cvoid, (Any, Any), callee, src)
13021306 end
13031307 return ci
@@ -1341,7 +1345,7 @@ function typeinf_ext_toplevel(methods::Vector{Any}, worlds::Vector{UInt}, trim_m
13411345 # and this is either the primary world, or not applicable in the primary world
13421346 # then we want to compile and emit this
13431347 if item. def. primary_world <= this_world <= item. def. deleted_world
1344- ci = typeinf_ext (interp, item, SOURCE_MODE_NOT_REQUIRED )
1348+ ci = typeinf_ext (interp, item, SOURCE_MODE_GET_SOURCE )
13451349 ci isa CodeInstance && push! (tocompile, ci)
13461350 end
13471351 elseif item isa SimpleVector && latest
@@ -1352,7 +1356,7 @@ function typeinf_ext_toplevel(methods::Vector{Any}, worlds::Vector{UInt}, trim_m
13521356 sig, this_world, #= mt_cache =# 0 )
13531357 if ptr != = C_NULL
13541358 mi = unsafe_pointer_to_objref (ptr):: MethodInstance
1355- ci = typeinf_ext (interp, mi, SOURCE_MODE_NOT_REQUIRED )
1359+ ci = typeinf_ext (interp, mi, SOURCE_MODE_GET_SOURCE )
13561360 ci isa CodeInstance && push! (tocompile, ci)
13571361 end
13581362 # additionally enqueue the ccallable entrypoint / adapter, which implicitly
@@ -1364,26 +1368,37 @@ function typeinf_ext_toplevel(methods::Vector{Any}, worlds::Vector{UInt}, trim_m
13641368 while ! isempty (tocompile)
13651369 callee = pop! (tocompile)
13661370 callee in inspected && continue
1367- push! (inspected, callee)
13681371 # now make sure everything has source code, if desired
13691372 mi = get_ci_mi (callee)
13701373 def = mi. def
13711374 if use_const_api (callee)
13721375 src = codeinfo_for_const (interp, mi, callee. rettype_const)
1373- elseif haskey (interp. codegen, callee)
1374- src = interp. codegen[callee]
1375- elseif isa (def, Method) && ! InferenceParams (interp). force_enable_inference && ccall (:jl_get_module_infer , Cint, (Any,), def. module) == 0
1376- src = retrieve_code_info (mi, get_inference_world (interp))
13771376 else
1378- # TODO : typeinf_code could return something with different edges/ages/owner/abi (needing an update to callee), which we don't handle here
1379- src = typeinf_code (interp, mi, true )
1377+ src = get (interp. codegen, callee, nothing )
1378+ if src === nothing
1379+ newcallee = typeinf_ext (interp, mi, SOURCE_MODE_GET_SOURCE)
1380+ if newcallee isa CodeInstance
1381+ @assert use_const_api (newcallee) || haskey (interp. codegen, newcallee)
1382+ push! (tocompile, newcallee)
1383+ end
1384+ if newcallee != = callee
1385+ push! (inspected, callee)
1386+ end
1387+ continue
1388+ end
13801389 end
1390+ push! (inspected, callee)
13811391 if src isa CodeInfo
13821392 collectinvokes! (tocompile, src)
1383- # It is somewhat ambiguous if typeinf_ext might have callee in the caches,
1384- # but for the purpose of native compile, we always want them put there.
1393+ # try to reuse an existing CodeInstance from before to avoid making duplicates in the cache
13851394 if iszero (ccall (:jl_mi_cache_has_ci , Cint, (Any, Any), mi, callee))
1386- code_cache (interp)[mi] = callee
1395+ cached = ccall (:jl_get_ci_equiv , Any, (Any, UInt), callee, this_world):: CodeInstance
1396+ if cached === callee
1397+ code_cache (interp)[mi] = callee
1398+ else
1399+ # Use an existing CI from the cache, if there is available one that is compatible
1400+ callee = cached
1401+ end
13871402 end
13881403 push! (codeinfos, callee)
13891404 push! (codeinfos, src)
0 commit comments