@@ -1202,9 +1202,10 @@ function process_simple!(ir::IRCode, todo::Vector{Pair{Int, Any}}, idx::Int, sta
1202
1202
return sig
1203
1203
end
1204
1204
1205
+ # TODO inline non-`isdispatchtuple`, union-split callsites
1205
1206
function analyze_single_call! (
1206
1207
ir:: IRCode , todo:: Vector{Pair{Int, Any}} , idx:: Int , @nospecialize (stmt),
1207
- sig :: Signature , infos:: Vector{MethodMatchInfo} , state:: InliningState )
1208
+ (; atypes, atype) :: Signature , infos:: Vector{MethodMatchInfo} , state:: InliningState )
1208
1209
cases = InliningCase[]
1209
1210
local signature_union = Bottom
1210
1211
local only_method = nothing # keep track of whether there is one matching method
@@ -1236,7 +1237,7 @@ function analyze_single_call!(
1236
1237
fully_covered = false
1237
1238
continue
1238
1239
end
1239
- item = analyze_method! (match, sig . atypes, state)
1240
+ item = analyze_method! (match, atypes, state)
1240
1241
if item === nothing
1241
1242
fully_covered = false
1242
1243
continue
@@ -1247,25 +1248,25 @@ function analyze_single_call!(
1247
1248
end
1248
1249
end
1249
1250
1250
- signature_fully_covered = sig. atype <: signature_union
1251
- # If we're fully covered and there's only one applicable method,
1252
- # we inline, even if the signature is not a dispatch tuple
1253
- if signature_fully_covered && length (cases) == 0 && only_method isa Method
1254
- if length (infos) > 1
1255
- (metharg, methsp) = ccall (:jl_type_intersection_with_env , Any, (Any, Any),
1256
- sig. atype, only_method. sig):: SimpleVector
1257
- match = MethodMatch (metharg, methsp, only_method, true )
1258
- else
1259
- meth = meth:: MethodLookupResult
1260
- @assert length (meth) == 1
1261
- match = meth[1 ]
1251
+ # if the signature is fully covered and there is only one applicable method,
1252
+ # we can try to inline it even if the signature is not a dispatch tuple
1253
+ if atype <: signature_union
1254
+ if length (cases) == 0 && only_method isa Method
1255
+ if length (infos) > 1
1256
+ (metharg, methsp) = ccall (:jl_type_intersection_with_env , Any, (Any, Any),
1257
+ atype, only_method. sig):: SimpleVector
1258
+ match = MethodMatch (metharg, methsp, only_method, true )
1259
+ else
1260
+ meth = meth:: MethodLookupResult
1261
+ @assert length (meth) == 1
1262
+ match = meth[1 ]
1263
+ end
1264
+ item = analyze_method! (match, atypes, state)
1265
+ item === nothing && return
1266
+ push! (cases, InliningCase (match. spec_types, item))
1267
+ fully_covered = true
1262
1268
end
1263
- fully_covered = true
1264
- item = analyze_method! (match, sig. atypes, state)
1265
- item === nothing && return
1266
- push! (cases, InliningCase (match. spec_types, item))
1267
- end
1268
- if ! signature_fully_covered
1269
+ else
1269
1270
fully_covered = false
1270
1271
end
1271
1272
@@ -1274,36 +1275,81 @@ function analyze_single_call!(
1274
1275
# onto the todo list
1275
1276
if fully_covered && length (cases) == 1
1276
1277
handle_single_case! (ir, stmt, idx, cases[1 ]. item, false , todo)
1277
- return
1278
+ elseif length (cases) > 0
1279
+ push! (todo, idx=> UnionSplit (fully_covered, atype, cases))
1278
1280
end
1279
- length (cases) == 0 && return
1280
- push! (todo, idx=> UnionSplit (fully_covered, sig. atype, cases))
1281
1281
return nothing
1282
1282
end
1283
1283
1284
+ # try to create `InliningCase`s using constant-prop'ed results
1285
+ # currently it works only when constant-prop' succeeded for all (union-split) signatures
1286
+ # TODO use any of constant-prop'ed results, and leave the other unhandled cases to later
1287
+ # TODO this function contains a lot of duplications with `analyze_single_call!`, factor them out
1284
1288
function maybe_handle_const_call! (
1285
- ir:: IRCode , idx:: Int , stmt:: Expr , info :: ConstCallInfo , sig :: Signature ,
1289
+ ir:: IRCode , idx:: Int , stmt:: Expr , (; results) :: ConstCallInfo , (; atypes, atype) :: Signature ,
1286
1290
state:: InliningState , isinvoke:: Bool , todo:: Vector{Pair{Int, Any}} )
1287
- # when multiple matches are found, bail out and later inliner will union-split this signature
1288
- # TODO effectively use multiple constant analysis results here
1289
- length (info. results) == 1 || return false
1290
- result = info. results[1 ]
1291
- isa (result, InferenceResult) || return false
1292
-
1293
- (; mi) = item = InliningTodo (result, sig. atypes)
1294
- validate_sparams (mi. sparam_vals) || return true
1295
- state. mi_cache != = nothing && (item = resolve_todo (item, state))
1296
- if sig. atype <: mi.def.sig
1297
- handle_single_case! (ir, stmt, idx, item, isinvoke, todo)
1298
- return true
1291
+ cases = InliningCase[] # TODO avoid this allocation for single cases ?
1292
+ local fully_covered = true
1293
+ local signature_union = Bottom
1294
+ for result in results
1295
+ isa (result, InferenceResult) || return false
1296
+ (; mi) = item = InliningTodo (result, atypes)
1297
+ spec_types = mi. specTypes
1298
+ signature_union = Union{signature_union, spec_types}
1299
+ if ! isdispatchtuple (spec_types)
1300
+ fully_covered = false
1301
+ continue
1302
+ end
1303
+ if ! validate_sparams (mi. sparam_vals)
1304
+ fully_covered = false
1305
+ continue
1306
+ end
1307
+ state. mi_cache != = nothing && (item = resolve_todo (item, state))
1308
+ if item === nothing
1309
+ fully_covered = false
1310
+ continue
1311
+ end
1312
+ push! (cases, InliningCase (spec_types, item))
1313
+ end
1314
+
1315
+ # if the signature is fully covered and there is only one applicable method,
1316
+ # we can try to inline it even if the signature is not a dispatch tuple
1317
+ if atype <: signature_union
1318
+ if length (cases) == 0 && length (results) == 1
1319
+ (; mi) = item = InliningTodo (results[1 ]:: InferenceResult , atypes)
1320
+ state. mi_cache != = nothing && (item = resolve_todo (item, state))
1321
+ validate_sparams (mi. sparam_vals) || return true
1322
+ item === nothing && return true
1323
+ push! (cases, InliningCase (mi. specTypes, item))
1324
+ fully_covered = true
1325
+ end
1299
1326
else
1300
- item === nothing && return true
1301
- # Union split out the error case
1302
- item = UnionSplit (false , sig. atype, InliningCase[InliningCase (mi. specTypes, item)])
1327
+ fully_covered = false
1328
+ end
1329
+
1330
+ # If we only have one case and that case is fully covered, we may either
1331
+ # be able to do the inlining now (for constant cases), or push it directly
1332
+ # onto the todo list
1333
+ if fully_covered && length (cases) == 1
1334
+ handle_single_case! (ir, stmt, idx, cases[1 ]. item, isinvoke, todo)
1335
+ elseif length (cases) > 0
1303
1336
isinvoke && rewrite_invoke_exprargs! (stmt)
1304
- push! (todo, idx=> item)
1305
- return true
1337
+ push! (todo, idx=> UnionSplit (fully_covered, atype, cases))
1306
1338
end
1339
+ return true
1340
+ end
1341
+
1342
+ function handle_const_opaque_closure_call! (
1343
+ ir:: IRCode , idx:: Int , stmt:: Expr , (; results):: ConstCallInfo ,
1344
+ (; atypes):: Signature , state:: InliningState , todo:: Vector{Pair{Int, Any}} )
1345
+ @assert length (results) == 1
1346
+ result = results[1 ]:: InferenceResult
1347
+ item = InliningTodo (result, atypes)
1348
+ isdispatchtuple (item. mi. specTypes) || return
1349
+ validate_sparams (item. mi. sparam_vals) || return
1350
+ state. mi_cache != = nothing && (item = resolve_todo (item, state))
1351
+ handle_single_case! (ir, stmt, idx, item, false , todo)
1352
+ return nothing
1307
1353
end
1308
1354
1309
1355
function assemble_inline_todo! (ir:: IRCode , state:: InliningState )
@@ -1336,18 +1382,22 @@ function assemble_inline_todo!(ir::IRCode, state::InliningState)
1336
1382
# if inference arrived here with constant-prop'ed result(s),
1337
1383
# we can perform a specialized analysis for just this case
1338
1384
if isa (info, ConstCallInfo)
1339
- if maybe_handle_const_call! (
1340
- ir, idx, stmt, info, sig,
1341
- state, sig. f === Core. invoke, todo)
1385
+ if isa (info. call, OpaqueClosureCallInfo)
1386
+ handle_const_opaque_closure_call! (
1387
+ ir, idx, stmt, info,
1388
+ sig, state, todo)
1342
1389
continue
1343
1390
else
1344
- info = info. call
1391
+ maybe_handle_const_call! (
1392
+ ir, idx, stmt, info, sig,
1393
+ state, sig. f === Core. invoke, todo) && continue
1345
1394
end
1395
+ info = info. call
1346
1396
end
1347
1397
1348
1398
if isa (info, OpaqueClosureCallInfo)
1349
- result = analyze_method! (info. match, sig. atypes, state)
1350
- handle_single_case! (ir, stmt, idx, result , false , todo)
1399
+ item = analyze_method! (info. match, sig. atypes, state)
1400
+ handle_single_case! (ir, stmt, idx, item , false , todo)
1351
1401
continue
1352
1402
end
1353
1403
0 commit comments