@@ -1147,9 +1147,10 @@ function process_simple!(ir::IRCode, todo::Vector{Pair{Int, Any}}, idx::Int, sta
1147
1147
return sig
1148
1148
end
1149
1149
1150
+ # TODO inline non-`isdispatchtuple`, union-split callsites
1150
1151
function analyze_single_call! (
1151
1152
ir:: IRCode , todo:: Vector{Pair{Int, Any}} , idx:: Int , @nospecialize (stmt),
1152
- sig :: Signature , infos:: Vector{MethodMatchInfo} , state:: InliningState , flag:: UInt8 )
1153
+ (; atypes, atype) :: Signature , infos:: Vector{MethodMatchInfo} , state:: InliningState , flag:: UInt8 )
1153
1154
cases = InliningCase[]
1154
1155
local signature_union = Bottom
1155
1156
local only_method = nothing # keep track of whether there is one matching method
@@ -1181,7 +1182,7 @@ function analyze_single_call!(
1181
1182
fully_covered = false
1182
1183
continue
1183
1184
end
1184
- item = analyze_method! (match, sig . atypes, state, flag)
1185
+ item = analyze_method! (match, atypes, state, flag)
1185
1186
if item === nothing
1186
1187
fully_covered = false
1187
1188
continue
@@ -1192,25 +1193,25 @@ function analyze_single_call!(
1192
1193
end
1193
1194
end
1194
1195
1195
- signature_fully_covered = sig. atype <: signature_union
1196
- # If we're fully covered and there's only one applicable method,
1197
- # we inline, even if the signature is not a dispatch tuple
1198
- if signature_fully_covered && length (cases) == 0 && only_method isa Method
1199
- if length (infos) > 1
1200
- (metharg, methsp) = ccall (:jl_type_intersection_with_env , Any, (Any, Any),
1201
- sig. atype, only_method. sig):: SimpleVector
1202
- match = MethodMatch (metharg, methsp, only_method, true )
1203
- else
1204
- meth = meth:: MethodLookupResult
1205
- @assert length (meth) == 1
1206
- match = meth[1 ]
1196
+ # if the signature is fully covered and there is only one applicable method,
1197
+ # we can try to inline it even if the signature is not a dispatch tuple
1198
+ if atype <: signature_union
1199
+ if length (cases) == 0 && only_method isa Method
1200
+ if length (infos) > 1
1201
+ (metharg, methsp) = ccall (:jl_type_intersection_with_env , Any, (Any, Any),
1202
+ atype, only_method. sig):: SimpleVector
1203
+ match = MethodMatch (metharg, methsp, only_method, true )
1204
+ else
1205
+ meth = meth:: MethodLookupResult
1206
+ @assert length (meth) == 1
1207
+ match = meth[1 ]
1208
+ end
1209
+ item = analyze_method! (match, atypes, state, flag)
1210
+ item === nothing && return
1211
+ push! (cases, InliningCase (match. spec_types, item))
1212
+ fully_covered = true
1207
1213
end
1208
- fully_covered = true
1209
- item = analyze_method! (match, sig. atypes, state, flag)
1210
- item === nothing && return
1211
- push! (cases, InliningCase (match. spec_types, item))
1212
- end
1213
- if ! signature_fully_covered
1214
+ else
1214
1215
fully_covered = false
1215
1216
end
1216
1217
@@ -1219,36 +1220,81 @@ function analyze_single_call!(
1219
1220
# onto the todo list
1220
1221
if fully_covered && length (cases) == 1
1221
1222
handle_single_case! (ir, stmt, idx, cases[1 ]. item, false , todo)
1222
- return
1223
+ elseif length (cases) > 0
1224
+ push! (todo, idx=> UnionSplit (fully_covered, atype, cases))
1223
1225
end
1224
- length (cases) == 0 && return
1225
- push! (todo, idx=> UnionSplit (fully_covered, sig. atype, cases))
1226
1226
return nothing
1227
1227
end
1228
1228
1229
+ # try to create `InliningCase`s using constant-prop'ed results
1230
+ # currently it works only when constant-prop' succeeded for all (union-split) signatures
1231
+ # TODO use any of constant-prop'ed results, and leave the other unhandled cases to later
1232
+ # TODO this function contains a lot of duplications with `analyze_single_call!`, factor them out
1229
1233
function maybe_handle_const_call! (
1230
- ir:: IRCode , idx:: Int , stmt:: Expr , info :: ConstCallInfo , sig :: Signature ,
1234
+ ir:: IRCode , idx:: Int , stmt:: Expr , (; results) :: ConstCallInfo , (; atypes, atype) :: Signature ,
1231
1235
state:: InliningState , flag:: UInt8 , isinvoke:: Bool , todo:: Vector{Pair{Int, Any}} )
1232
- # when multiple matches are found, bail out and later inliner will union-split this signature
1233
- # TODO effectively use multiple constant analysis results here
1234
- length (info. results) == 1 || return false
1235
- result = info. results[1 ]
1236
- isa (result, InferenceResult) || return false
1237
-
1238
- (; mi) = item = InliningTodo (result, sig. atypes)
1239
- validate_sparams (mi. sparam_vals) || return true
1240
- state. mi_cache != = nothing && (item = resolve_todo (item, state, flag))
1241
- if sig. atype <: mi.def.sig
1242
- handle_single_case! (ir, stmt, idx, item, isinvoke, todo)
1243
- return true
1236
+ cases = InliningCase[] # TODO avoid this allocation for single cases ?
1237
+ local fully_covered = true
1238
+ local signature_union = Bottom
1239
+ for result in results
1240
+ isa (result, InferenceResult) || return false
1241
+ (; mi) = item = InliningTodo (result, atypes)
1242
+ spec_types = mi. specTypes
1243
+ signature_union = Union{signature_union, spec_types}
1244
+ if ! isdispatchtuple (spec_types)
1245
+ fully_covered = false
1246
+ continue
1247
+ end
1248
+ if ! validate_sparams (mi. sparam_vals)
1249
+ fully_covered = false
1250
+ continue
1251
+ end
1252
+ state. mi_cache != = nothing && (item = resolve_todo (item, state, flag))
1253
+ if item === nothing
1254
+ fully_covered = false
1255
+ continue
1256
+ end
1257
+ push! (cases, InliningCase (spec_types, item))
1258
+ end
1259
+
1260
+ # if the signature is fully covered and there is only one applicable method,
1261
+ # we can try to inline it even if the signature is not a dispatch tuple
1262
+ if atype <: signature_union
1263
+ if length (cases) == 0 && length (results) == 1
1264
+ (; mi) = item = InliningTodo (results[1 ]:: InferenceResult , atypes)
1265
+ state. mi_cache != = nothing && (item = resolve_todo (item, state, flag))
1266
+ validate_sparams (mi. sparam_vals) || return true
1267
+ item === nothing && return true
1268
+ push! (cases, InliningCase (mi. specTypes, item))
1269
+ fully_covered = true
1270
+ end
1244
1271
else
1245
- item === nothing && return true
1246
- # Union split out the error case
1247
- item = UnionSplit (false , sig. atype, InliningCase[InliningCase (mi. specTypes, item)])
1272
+ fully_covered = false
1273
+ end
1274
+
1275
+ # If we only have one case and that case is fully covered, we may either
1276
+ # be able to do the inlining now (for constant cases), or push it directly
1277
+ # onto the todo list
1278
+ if fully_covered && length (cases) == 1
1279
+ handle_single_case! (ir, stmt, idx, cases[1 ]. item, isinvoke, todo)
1280
+ elseif length (cases) > 0
1248
1281
isinvoke && rewrite_invoke_exprargs! (stmt)
1249
- push! (todo, idx=> item)
1250
- return true
1282
+ push! (todo, idx=> UnionSplit (fully_covered, atype, cases))
1251
1283
end
1284
+ return true
1285
+ end
1286
+
1287
+ function handle_const_opaque_closure_call! (
1288
+ ir:: IRCode , idx:: Int , stmt:: Expr , (; results):: ConstCallInfo ,
1289
+ (; atypes):: Signature , state:: InliningState , flag:: UInt8 , todo:: Vector{Pair{Int, Any}} )
1290
+ @assert length (results) == 1
1291
+ result = results[1 ]:: InferenceResult
1292
+ item = InliningTodo (result, atypes)
1293
+ isdispatchtuple (item. mi. specTypes) || return
1294
+ validate_sparams (item. mi. sparam_vals) || return
1295
+ state. mi_cache != = nothing && (item = resolve_todo (item, state, flag))
1296
+ handle_single_case! (ir, stmt, idx, item, false , todo)
1297
+ return nothing
1252
1298
end
1253
1299
1254
1300
function assemble_inline_todo! (ir:: IRCode , state:: InliningState )
@@ -1283,18 +1329,25 @@ function assemble_inline_todo!(ir::IRCode, state::InliningState)
1283
1329
# if inference arrived here with constant-prop'ed result(s),
1284
1330
# we can perform a specialized analysis for just this case
1285
1331
if isa (info, ConstCallInfo)
1286
- if ! is_stmt_noinline (flag) && maybe_handle_const_call! (
1287
- ir, idx, stmt, info, sig,
1288
- state, flag, sig. f === Core. invoke, todo)
1289
- continue
1332
+ if ! is_stmt_noinline (flag)
1333
+ if isa (info. call, OpaqueClosureCallInfo)
1334
+ handle_const_opaque_closure_call! (
1335
+ ir, idx, stmt, info,
1336
+ sig, state, flag, todo)
1337
+ continue
1338
+ else
1339
+ maybe_handle_const_call! (
1340
+ ir, idx, stmt, info, sig,
1341
+ state, flag, sig. f === Core. invoke, todo) && continue
1342
+ end
1290
1343
else
1291
1344
info = info. call
1292
1345
end
1293
1346
end
1294
1347
1295
1348
if isa (info, OpaqueClosureCallInfo)
1296
- result = analyze_method! (info. match, sig. atypes, state, flag)
1297
- handle_single_case! (ir, stmt, idx, result , false , todo)
1349
+ item = analyze_method! (info. match, sig. atypes, state, flag)
1350
+ handle_single_case! (ir, stmt, idx, item , false , todo)
1298
1351
continue
1299
1352
end
1300
1353
0 commit comments