@@ -156,7 +156,7 @@ function finish!(interp::AbstractInterpreter, caller::InferenceState, validation
156
156
# when compiling the compiler to inject everything eagerly
157
157
# where codegen can start finding and using it right away
158
158
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)
160
160
ccall (:jl_add_codeinst_to_jit , Cvoid, (Any, Any), ci, uncompressed)
161
161
end
162
162
end
@@ -1113,39 +1113,62 @@ end
1113
1113
"""
1114
1114
SOURCE_MODE_NOT_REQUIRED
1115
1115
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.
1120
1120
"""
1121
1121
const SOURCE_MODE_NOT_REQUIRED = 0x0
1122
1122
1123
1123
"""
1124
1124
SOURCE_MODE_ABI
1125
1125
1126
1126
Indicates 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).
1132
1128
"""
1133
1129
const SOURCE_MODE_ABI = 0x1
1134
1130
1135
1131
"""
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
1137
1138
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.
1143
1139
"""
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 )
1145
1147
(@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
1146
1162
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
1149
1172
return true
1150
1173
end
1151
1174
return false
@@ -1155,9 +1178,10 @@ function ci_has_invoke(code::CodeInstance)
1155
1178
return (@atomic :monotonic code. invoke) != = C_NULL
1156
1179
end
1157
1180
1158
- function ci_meets_requirement (code:: CodeInstance , source_mode:: UInt8 )
1181
+ function ci_meets_requirement (interp :: AbstractInterpreter , code:: CodeInstance , source_mode:: UInt8 )
1159
1182
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)
1161
1185
return false
1162
1186
end
1163
1187
@@ -1167,7 +1191,7 @@ function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance, source_mod
1167
1191
let code = get (code_cache (interp), mi, nothing )
1168
1192
if code isa CodeInstance
1169
1193
# 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)
1171
1195
ccall (:jl_typeinf_timing_end , Cvoid, (UInt64,), start_time)
1172
1196
return code
1173
1197
end
@@ -1179,7 +1203,7 @@ function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance, source_mod
1179
1203
let code = get (code_cache (interp), mi, nothing )
1180
1204
if code isa CodeInstance
1181
1205
# 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)
1183
1207
engine_reject (interp, ci)
1184
1208
ccall (:jl_typeinf_timing_end , Cvoid, (UInt64,), start_time)
1185
1209
return code
@@ -1210,18 +1234,11 @@ function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance, source_mod
1210
1234
ccall (:jl_typeinf_timing_end , Cvoid, (UInt64,), start_time)
1211
1235
1212
1236
ci = result. ci # reload from result in case it changed
1237
+ codegen = codegen_cache (interp)
1213
1238
@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)
1218
1240
@assert is_result_constabi_eligible (result) == use_const_api (ci)
1219
1241
@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
1225
1242
return ci
1226
1243
end
1227
1244
@@ -1235,35 +1252,9 @@ end
1235
1252
typeinf_type (interp:: AbstractInterpreter , match:: MethodMatch ) =
1236
1253
typeinf_type (interp, specialize_method (match))
1237
1254
function 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
1267
1258
end
1268
1259
1269
1260
# 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
1300
1291
src = _uncompressed_ir (callee, src)
1301
1292
end
1302
1293
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
1304
1295
if newcallee isa CodeInstance
1305
1296
callee === ci && (ci = newcallee) # ci stopped meeting the requirements after typeinf_ext last checked, try again with newcallee
1306
1297
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)
1309
1301
end
1310
1302
continue
1311
1303
end
1312
1304
end
1313
1305
push! (inspected, callee)
1314
1306
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
1315
1319
ccall (:jl_add_codeinst_to_jit , Cvoid, (Any, Any), callee, src)
1316
1320
end
1317
1321
return ci
@@ -1355,7 +1359,7 @@ function typeinf_ext_toplevel(methods::Vector{Any}, worlds::Vector{UInt}, trim_m
1355
1359
# and this is either the primary world, or not applicable in the primary world
1356
1360
# then we want to compile and emit this
1357
1361
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 )
1359
1363
ci isa CodeInstance && push! (tocompile, ci)
1360
1364
end
1361
1365
elseif item isa SimpleVector && latest
@@ -1366,7 +1370,7 @@ function typeinf_ext_toplevel(methods::Vector{Any}, worlds::Vector{UInt}, trim_m
1366
1370
sig, this_world, #= mt_cache =# 0 )
1367
1371
if ptr != = C_NULL
1368
1372
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 )
1370
1374
ci isa CodeInstance && push! (tocompile, ci)
1371
1375
end
1372
1376
# additionally enqueue the ccallable entrypoint / adapter, which implicitly
@@ -1377,26 +1381,37 @@ function typeinf_ext_toplevel(methods::Vector{Any}, worlds::Vector{UInt}, trim_m
1377
1381
while ! isempty (tocompile)
1378
1382
callee = pop! (tocompile)
1379
1383
callee in inspected && continue
1380
- push! (inspected, callee)
1381
1384
# now make sure everything has source code, if desired
1382
1385
mi = get_ci_mi (callee)
1383
1386
def = mi. def
1384
1387
if use_const_api (callee)
1385
1388
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))
1390
1389
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
1393
1402
end
1403
+ push! (inspected, callee)
1394
1404
if src isa CodeInfo
1395
1405
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
1398
1407
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
1400
1415
end
1401
1416
push! (codeinfos, callee)
1402
1417
push! (codeinfos, src)
0 commit comments