@@ -153,7 +153,7 @@ function finish!(interp::AbstractInterpreter, caller::InferenceState, validation
153
153
# when compiling the compiler to inject everything eagerly
154
154
# where codegen can start finding and using it right away
155
155
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)
157
157
ccall (:jl_add_codeinst_to_jit , Cvoid, (Any, Any), ci, uncompressed)
158
158
end
159
159
end
@@ -1099,39 +1099,62 @@ end
1099
1099
"""
1100
1100
SOURCE_MODE_NOT_REQUIRED
1101
1101
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.
1106
1106
"""
1107
1107
const SOURCE_MODE_NOT_REQUIRED = 0x0
1108
1108
1109
1109
"""
1110
1110
SOURCE_MODE_ABI
1111
1111
1112
1112
Indicates 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).
1118
1114
"""
1119
1115
const SOURCE_MODE_ABI = 0x1
1120
1116
1121
1117
"""
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
1123
1124
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.
1129
1125
"""
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 )
1131
1133
(@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
1132
1148
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
1135
1158
return true
1136
1159
end
1137
1160
return false
@@ -1141,9 +1164,10 @@ function ci_has_invoke(code::CodeInstance)
1141
1164
return (@atomic :monotonic code. invoke) != = C_NULL
1142
1165
end
1143
1166
1144
- function ci_meets_requirement (code:: CodeInstance , source_mode:: UInt8 )
1167
+ function ci_meets_requirement (interp :: AbstractInterpreter , code:: CodeInstance , source_mode:: UInt8 )
1145
1168
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)
1147
1171
return false
1148
1172
end
1149
1173
@@ -1153,7 +1177,7 @@ function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance, source_mod
1153
1177
let code = get (code_cache (interp), mi, nothing )
1154
1178
if code isa CodeInstance
1155
1179
# 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)
1157
1181
ccall (:jl_typeinf_timing_end , Cvoid, (UInt64,), start_time)
1158
1182
return code
1159
1183
end
@@ -1165,7 +1189,7 @@ function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance, source_mod
1165
1189
let code = get (code_cache (interp), mi, nothing )
1166
1190
if code isa CodeInstance
1167
1191
# 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)
1169
1193
engine_reject (interp, ci)
1170
1194
ccall (:jl_typeinf_timing_end , Cvoid, (UInt64,), start_time)
1171
1195
return code
@@ -1196,18 +1220,11 @@ function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance, source_mod
1196
1220
ccall (:jl_typeinf_timing_end , Cvoid, (UInt64,), start_time)
1197
1221
1198
1222
ci = result. ci # reload from result in case it changed
1223
+ codegen = codegen_cache (interp)
1199
1224
@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)
1204
1226
@assert is_result_constabi_eligible (result) == use_const_api (ci)
1205
1227
@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
1211
1228
return ci
1212
1229
end
1213
1230
@@ -1221,35 +1238,9 @@ end
1221
1238
typeinf_type (interp:: AbstractInterpreter , match:: MethodMatch ) =
1222
1239
typeinf_type (interp, specialize_method (match))
1223
1240
function 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
1253
1244
end
1254
1245
1255
1246
# 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
1286
1277
src = _uncompressed_ir (callee, src)
1287
1278
end
1288
1279
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
1290
1281
if newcallee isa CodeInstance
1291
1282
callee === ci && (ci = newcallee) # ci stopped meeting the requirements after typeinf_ext last checked, try again with newcallee
1292
1283
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)
1295
1287
end
1296
1288
continue
1297
1289
end
1298
1290
end
1299
1291
push! (inspected, callee)
1300
1292
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
1301
1305
ccall (:jl_add_codeinst_to_jit , Cvoid, (Any, Any), callee, src)
1302
1306
end
1303
1307
return ci
@@ -1341,7 +1345,7 @@ function typeinf_ext_toplevel(methods::Vector{Any}, worlds::Vector{UInt}, trim_m
1341
1345
# and this is either the primary world, or not applicable in the primary world
1342
1346
# then we want to compile and emit this
1343
1347
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 )
1345
1349
ci isa CodeInstance && push! (tocompile, ci)
1346
1350
end
1347
1351
elseif item isa SimpleVector && latest
@@ -1352,7 +1356,7 @@ function typeinf_ext_toplevel(methods::Vector{Any}, worlds::Vector{UInt}, trim_m
1352
1356
sig, this_world, #= mt_cache =# 0 )
1353
1357
if ptr != = C_NULL
1354
1358
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 )
1356
1360
ci isa CodeInstance && push! (tocompile, ci)
1357
1361
end
1358
1362
# additionally enqueue the ccallable entrypoint / adapter, which implicitly
@@ -1364,26 +1368,37 @@ function typeinf_ext_toplevel(methods::Vector{Any}, worlds::Vector{UInt}, trim_m
1364
1368
while ! isempty (tocompile)
1365
1369
callee = pop! (tocompile)
1366
1370
callee in inspected && continue
1367
- push! (inspected, callee)
1368
1371
# now make sure everything has source code, if desired
1369
1372
mi = get_ci_mi (callee)
1370
1373
def = mi. def
1371
1374
if use_const_api (callee)
1372
1375
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))
1377
1376
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
1380
1389
end
1390
+ push! (inspected, callee)
1381
1391
if src isa CodeInfo
1382
1392
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
1385
1394
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
1387
1402
end
1388
1403
push! (codeinfos, callee)
1389
1404
push! (codeinfos, src)
0 commit comments