@@ -156,7 +156,7 @@ function finish!(interp::AbstractInterpreter, caller::InferenceState, validation
156156 # when compiling the compiler to inject everything eagerly
157157 # where codegen can start finding and using it right away
158158 mi = result. linfo
159- if mi. def isa Method && isa_compileable_sig (mi)
159+ if mi. def isa Method && isa_compileable_sig (mi) && is_cached (caller)
160160 ccall (:jl_add_codeinst_to_jit , Cvoid, (Any, Any), ci, uncompressed)
161161 end
162162 end
@@ -1113,39 +1113,62 @@ end
11131113"""
11141114 SOURCE_MODE_NOT_REQUIRED
11151115
1116- Indicates to inference that the source is not required and the only fields
1117- of the resulting `CodeInstance` that the caller is interested in are types
1118- and effects. Inference is still free to create a CodeInstance with source,
1119- but is not required to do so.
1116+ Indicates to inference that the source is not required and the only fields of
1117+ the resulting `CodeInstance` that the caller is interested in are return or
1118+ exception types and IPO effects. Inference is still free to create source for
1119+ it or add it to the JIT even, but is not required or expected to do so.
11201120"""
11211121const SOURCE_MODE_NOT_REQUIRED = 0x0
11221122
11231123"""
11241124 SOURCE_MODE_ABI
11251125
11261126Indicates to inference that it should return a CodeInstance that can
1127- either be `->invoke`'d (because it has already been compiled or because
1128- it has constabi) or one that can be made so by compiling its `->inferred`
1129- field.
1130-
1131- N.B.: The `->inferred` field is volatile and the compiler may delete it.
1127+ be `->invoke`'d (because it has already been compiled).
11321128"""
11331129const SOURCE_MODE_ABI = 0x1
11341130
11351131"""
1136- ci_has_abi(code::CodeInstance)
1132+ SOURCE_MODE_GET_SOURCE
1133+
1134+ Indicates to inference that it should return a CodeInstance after it has
1135+ prepared interp to be able to provide source code for it.
1136+ """
1137+ const SOURCE_MODE_GET_SOURCE = 0xf
11371138
1138- Determine whether this CodeInstance is something that could be invoked if we gave it
1139- to the runtime system (either because it already has an ->invoke ptr, or
1140- because it has source that could be compiled). Note that this information may
1141- be stale by the time the user see it, so the user will need to perform their
1142- own checks if they actually need the abi from it.
11431139"""
1144- function ci_has_abi (code:: CodeInstance )
1140+ ci_has_abi(interp::AbstractInterpreter, code::CodeInstance)
1141+
1142+ Determine whether this CodeInstance is something that could be invoked if
1143+ interp gave it to the runtime system (either because it already has an ->invoke
1144+ ptr, or because interp has source that could be compiled).
1145+ """
1146+ function ci_has_abi (interp:: AbstractInterpreter , code:: CodeInstance )
11451147 (@atomic :acquire code. invoke) != = C_NULL && return true
1148+ return ci_has_source (interp, code)
1149+ end
1150+
1151+ """
1152+ ci_has_source(interp::AbstractInterpreter, code::CodeInstance)
1153+
1154+ Determine whether this CodeInstance is something that could be compiled from
1155+ source that interp has.
1156+ """
1157+ function ci_has_source (interp:: AbstractInterpreter , code:: CodeInstance )
1158+ codegen = codegen_cache (interp)
1159+ codegen === nothing && return false
1160+ use_const_api (code) && return true
1161+ haskey (codegen, code) && return true
11461162 inf = @atomic :monotonic code. inferred
1147- if code. owner === nothing ? (isa (inf, CodeInfo) || isa (inf, String)) : inf != = nothing
1148- # 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
1163+ if isa (inf, String)
1164+ inf = _uncompressed_ir (code, inf)
1165+ end
1166+ if code. owner === nothing
1167+ if isa (inf, CodeInfo)
1168+ codegen[code] = inf
1169+ return true
1170+ end
1171+ elseif inf != = nothing
11491172 return true
11501173 end
11511174 return false
@@ -1155,9 +1178,10 @@ function ci_has_invoke(code::CodeInstance)
11551178 return (@atomic :monotonic code. invoke) != = C_NULL
11561179end
11571180
1158- function ci_meets_requirement (code:: CodeInstance , source_mode:: UInt8 )
1181+ function ci_meets_requirement (interp :: AbstractInterpreter , code:: CodeInstance , source_mode:: UInt8 )
11591182 source_mode == SOURCE_MODE_NOT_REQUIRED && return true
1160- source_mode == SOURCE_MODE_ABI && return ci_has_abi (code)
1183+ source_mode == SOURCE_MODE_ABI && return ci_has_abi (interp, code)
1184+ source_mode == SOURCE_MODE_GET_SOURCE && return ci_has_source (interp, code)
11611185 return false
11621186end
11631187
@@ -1167,7 +1191,7 @@ function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance, source_mod
11671191 let code = get (code_cache (interp), mi, nothing )
11681192 if code isa CodeInstance
11691193 # see if this code already exists in the cache
1170- if ci_meets_requirement (code, source_mode)
1194+ if ci_meets_requirement (interp, code, source_mode)
11711195 ccall (:jl_typeinf_timing_end , Cvoid, (UInt64,), start_time)
11721196 return code
11731197 end
@@ -1179,7 +1203,7 @@ function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance, source_mod
11791203 let code = get (code_cache (interp), mi, nothing )
11801204 if code isa CodeInstance
11811205 # see if this code already exists in the cache
1182- if ci_meets_requirement (code, source_mode)
1206+ if ci_meets_requirement (interp, code, source_mode)
11831207 engine_reject (interp, ci)
11841208 ccall (:jl_typeinf_timing_end , Cvoid, (UInt64,), start_time)
11851209 return code
@@ -1210,18 +1234,11 @@ function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance, source_mod
12101234 ccall (:jl_typeinf_timing_end , Cvoid, (UInt64,), start_time)
12111235
12121236 ci = result. ci # reload from result in case it changed
1237+ codegen = codegen_cache (interp)
12131238 @assert frame. cache_mode != CACHE_MODE_NULL
1214- @assert is_result_constabi_eligible (result) || begin
1215- codegen = codegen_cache (interp)
1216- codegen === nothing || haskey (codegen, ci)
1217- end
1239+ @assert is_result_constabi_eligible (result) || codegen === nothing || haskey (codegen, ci)
12181240 @assert is_result_constabi_eligible (result) == use_const_api (ci)
12191241 @assert isdefined (ci, :inferred ) " interpreter did not fulfill our expectations"
1220- if ! is_cached (frame) && source_mode == SOURCE_MODE_ABI
1221- # XXX : jl_type_infer somewhat ambiguously assumes this must be cached
1222- # XXX : this should be using the CI from the cache, if possible instead: haskey(cache, mi) && (ci = cache[mi])
1223- code_cache (interp)[mi] = ci
1224- end
12251242 return ci
12261243end
12271244
@@ -1235,35 +1252,9 @@ end
12351252typeinf_type (interp:: AbstractInterpreter , match:: MethodMatch ) =
12361253 typeinf_type (interp, specialize_method (match))
12371254function typeinf_type (interp:: AbstractInterpreter , mi:: MethodInstance )
1238- # n.b.: this could be replaced with @something(typeinf_ext(interp, mi, SOURCE_MODE_NOT_REQUIRED), return nothing).rettype
1239- start_time = ccall (:jl_typeinf_timing_begin , UInt64, ())
1240- let code = get (code_cache (interp), mi, nothing )
1241- if code isa CodeInstance
1242- # see if this rettype already exists in the cache
1243- ccall (:jl_typeinf_timing_end , Cvoid, (UInt64,), start_time)
1244- return code. rettype
1245- end
1246- end
1247- ci = engine_reserve (interp, mi)
1248- let code = get (code_cache (interp), mi, nothing )
1249- if code isa CodeInstance
1250- engine_reject (interp, ci)
1251- # see if this rettype already exists in the cache
1252- ccall (:jl_typeinf_timing_end , Cvoid, (UInt64,), start_time)
1253- return code. rettype
1254- end
1255- end
1256- result = InferenceResult (mi, typeinf_lattice (interp))
1257- result. ci = ci
1258- frame = InferenceState (result, #= cache_mode=# :global , interp)
1259- if frame === nothing
1260- engine_reject (interp, ci)
1261- return nothing
1262- end
1263- typeinf (interp, frame)
1264- ccall (:jl_typeinf_timing_end , Cvoid, (UInt64,), start_time)
1265- is_inferred (result) || return nothing
1266- return widenconst (ignorelimited (result. result))
1255+ ci = typeinf_ext (interp, mi, SOURCE_MODE_NOT_REQUIRED)
1256+ ci isa CodeInstance || return nothing
1257+ return ci. rettype
12671258end
12681259
12691260# collect a list of all code that is needed along with CodeInstance to codegen it fully
@@ -1300,18 +1291,31 @@ function add_codeinsts_to_jit!(interp::AbstractInterpreter, ci, source_mode::UIn
13001291 src = _uncompressed_ir (callee, src)
13011292 end
13021293 if ! isa (src, CodeInfo)
1303- newcallee = typeinf_ext (interp, callee. def, source_mode)
1294+ newcallee = typeinf_ext (interp, callee. def, source_mode) # always SOURCE_MODE_ABI
13041295 if newcallee isa CodeInstance
13051296 callee === ci && (ci = newcallee) # ci stopped meeting the requirements after typeinf_ext last checked, try again with newcallee
13061297 push! (tocompile, newcallee)
1307- # else
1308- # println("warning: could not get source code for ", callee.def)
1298+ end
1299+ if newcallee != = callee
1300+ push! (inspected, callee)
13091301 end
13101302 continue
13111303 end
13121304 end
13131305 push! (inspected, callee)
13141306 collectinvokes! (tocompile, src)
1307+ mi = get_ci_mi (callee)
1308+ if iszero (ccall (:jl_mi_cache_has_ci , Cint, (Any, Any), mi, callee))
1309+ cached = ccall (:jl_get_ci_equiv , Any, (Any, UInt), callee, get_inference_world (interp)):: CodeInstance
1310+ if cached === callee
1311+ # make sure callee is gc-rooted and cached, as required by jl_add_codeinst_to_jit
1312+ code_cache (interp)[mi] = callee
1313+ else
1314+ # use an existing CI from the cache, if there is available one that is compatible
1315+ callee === ci && (ci = cached)
1316+ callee = cached
1317+ end
1318+ end
13151319 ccall (:jl_add_codeinst_to_jit , Cvoid, (Any, Any), callee, src)
13161320 end
13171321 return ci
@@ -1355,7 +1359,7 @@ function typeinf_ext_toplevel(methods::Vector{Any}, worlds::Vector{UInt}, trim_m
13551359 # and this is either the primary world, or not applicable in the primary world
13561360 # then we want to compile and emit this
13571361 if item. def. primary_world <= this_world <= item. def. deleted_world
1358- ci = typeinf_ext (interp, item, SOURCE_MODE_NOT_REQUIRED )
1362+ ci = typeinf_ext (interp, item, SOURCE_MODE_GET_SOURCE )
13591363 ci isa CodeInstance && push! (tocompile, ci)
13601364 end
13611365 elseif item isa SimpleVector && latest
@@ -1366,7 +1370,7 @@ function typeinf_ext_toplevel(methods::Vector{Any}, worlds::Vector{UInt}, trim_m
13661370 sig, this_world, #= mt_cache =# 0 )
13671371 if ptr != = C_NULL
13681372 mi = unsafe_pointer_to_objref (ptr):: MethodInstance
1369- ci = typeinf_ext (interp, mi, SOURCE_MODE_NOT_REQUIRED )
1373+ ci = typeinf_ext (interp, mi, SOURCE_MODE_GET_SOURCE )
13701374 ci isa CodeInstance && push! (tocompile, ci)
13711375 end
13721376 # additionally enqueue the ccallable entrypoint / adapter, which implicitly
@@ -1377,26 +1381,37 @@ function typeinf_ext_toplevel(methods::Vector{Any}, worlds::Vector{UInt}, trim_m
13771381 while ! isempty (tocompile)
13781382 callee = pop! (tocompile)
13791383 callee in inspected && continue
1380- push! (inspected, callee)
13811384 # now make sure everything has source code, if desired
13821385 mi = get_ci_mi (callee)
13831386 def = mi. def
13841387 if use_const_api (callee)
13851388 src = codeinfo_for_const (interp, mi, callee. rettype_const)
1386- elseif haskey (interp. codegen, callee)
1387- src = interp. codegen[callee]
1388- elseif isa (def, Method) && ! InferenceParams (interp). force_enable_inference && ccall (:jl_get_module_infer , Cint, (Any,), def. module) == 0
1389- src = retrieve_code_info (mi, get_inference_world (interp))
13901389 else
1391- # TODO : typeinf_code could return something with different edges/ages/owner/abi (needing an update to callee), which we don't handle here
1392- src = typeinf_code (interp, mi, true )
1390+ src = get (interp. codegen, callee, nothing )
1391+ if src === nothing
1392+ newcallee = typeinf_ext (interp, mi, SOURCE_MODE_GET_SOURCE)
1393+ if newcallee isa CodeInstance
1394+ @assert use_const_api (newcallee) || haskey (interp. codegen, newcallee)
1395+ push! (tocompile, newcallee)
1396+ end
1397+ if newcallee != = callee
1398+ push! (inspected, callee)
1399+ end
1400+ continue
1401+ end
13931402 end
1403+ push! (inspected, callee)
13941404 if src isa CodeInfo
13951405 collectinvokes! (tocompile, src)
1396- # It is somewhat ambiguous if typeinf_ext might have callee in the caches,
1397- # but for the purpose of native compile, we always want them put there.
1406+ # try to reuse an existing CodeInstance from before to avoid making duplicates in the cache
13981407 if iszero (ccall (:jl_mi_cache_has_ci , Cint, (Any, Any), mi, callee))
1399- code_cache (interp)[mi] = callee
1408+ cached = ccall (:jl_get_ci_equiv , Any, (Any, UInt), callee, this_world):: CodeInstance
1409+ if cached === callee
1410+ code_cache (interp)[mi] = callee
1411+ else
1412+ # Use an existing CI from the cache, if there is available one that is compatible
1413+ callee = cached
1414+ end
14001415 end
14011416 push! (codeinfos, callee)
14021417 push! (codeinfos, src)
0 commit comments