@@ -689,19 +689,16 @@ function rewrite_apply_exprargs!(
689
689
new_sig = with_atype (call_sig (ir, new_stmt):: Signature )
690
690
new_info = call. info
691
691
if isa (new_info, ConstCallInfo)
692
- maybe_handle_const_call ! (
692
+ handle_const_call ! (
693
693
ir, state1. id, new_stmt, new_info, flag,
694
- new_sig, istate, todo) && @goto analyzed
695
- new_info = new_info. call # cascade to the non-constant handling
696
- end
697
- if isa (new_info, MethodMatchInfo) || isa (new_info, UnionSplitInfo)
694
+ new_sig, istate, todo)
695
+ elseif isa (new_info, MethodMatchInfo) || isa (new_info, UnionSplitInfo)
698
696
new_infos = isa (new_info, MethodMatchInfo) ? MethodMatchInfo[new_info] : new_info. matches
699
697
# See if we can inline this call to `iterate`
700
698
analyze_single_call! (
701
699
ir, state1. id, new_stmt, new_infos, flag,
702
700
new_sig, istate, todo)
703
701
end
704
- @label analyzed
705
702
if i != length (thisarginfo. each)
706
703
valT = getfield_tfunc (call. rt, Const (1 ))
707
704
val_extracted = insert_node! (ir, idx, NewInstruction (
@@ -1136,139 +1133,150 @@ function process_simple!(ir::IRCode, idx::Int, state::InliningState, todo::Vecto
1136
1133
return stmt, sig
1137
1134
end
1138
1135
1139
- # TODO inline non-`isdispatchtuple`, union-split callsites
1136
+ # TODO inline non-`isdispatchtuple`, union-split callsites?
1140
1137
function analyze_single_call! (
1141
1138
ir:: IRCode , idx:: Int , stmt:: Expr , infos:: Vector{MethodMatchInfo} , flag:: UInt8 ,
1142
1139
sig:: Signature , state:: InliningState , todo:: Vector{Pair{Int, Any}} )
1143
1140
(; argtypes, atype) = sig
1144
1141
cases = InliningCase[]
1145
1142
local signature_union = Bottom
1146
1143
local only_method = nothing # keep track of whether there is one matching method
1147
- local meth
1144
+ local meth:: MethodLookupResult
1148
1145
local fully_covered = true
1149
1146
for i in 1 : length (infos)
1150
- info = infos[i]
1151
- meth = info. results
1147
+ meth = infos[i]. results
1152
1148
if meth. ambig
1153
1149
# Too many applicable methods
1154
1150
# Or there is a (partial?) ambiguity
1155
- return
1151
+ return nothing
1156
1152
elseif length (meth) == 0
1157
1153
# No applicable methods; try next union split
1158
1154
continue
1159
- elseif length (meth) == 1 && only_method != = false
1160
- if only_method === nothing
1161
- only_method = meth[1 ]. method
1162
- elseif only_method != = meth[1 ]. method
1155
+ else
1156
+ if length (meth) == 1 && only_method != = false
1157
+ if only_method === nothing
1158
+ only_method = meth[1 ]. method
1159
+ elseif only_method != = meth[1 ]. method
1160
+ only_method = false
1161
+ end
1162
+ else
1163
1163
only_method = false
1164
1164
end
1165
- else
1166
- only_method = false
1167
1165
end
1168
1166
for match in meth
1169
- spec_types = match. spec_types
1170
- signature_union = Union{signature_union, spec_types}
1171
- if ! isdispatchtuple (spec_types)
1172
- fully_covered = false
1173
- continue
1174
- end
1175
- item = analyze_method! (match, argtypes, flag, state)
1176
- if item === nothing
1177
- fully_covered = false
1178
- continue
1179
- elseif _any (case-> case. sig === spec_types, cases)
1180
- continue
1181
- end
1182
- push! (cases, InliningCase (spec_types, item))
1167
+ signature_union = Union{signature_union, match. spec_types}
1168
+ fully_covered &= handle_match! (match, argtypes, flag, state, cases)
1183
1169
end
1184
1170
end
1185
1171
1186
- # if the signature is fully or mostly covered and there is only one applicable method,
1172
+ # if the signature is fully covered and there is only one applicable method,
1187
1173
# we can try to inline it even if the signature is not a dispatch tuple
1188
1174
if length (cases) == 0 && only_method isa Method
1189
1175
if length (infos) > 1
1190
1176
(metharg, methsp) = ccall (:jl_type_intersection_with_env , Any, (Any, Any),
1191
1177
atype, only_method. sig):: SimpleVector
1192
1178
match = MethodMatch (metharg, methsp:: SimpleVector , only_method, true )
1193
1179
else
1194
- meth = meth:: MethodLookupResult
1195
1180
@assert length (meth) == 1
1196
1181
match = meth[1 ]
1197
1182
end
1198
1183
item = analyze_method! (match, argtypes, flag, state)
1199
- item === nothing && return
1184
+ item === nothing && return nothing
1200
1185
push! (cases, InliningCase (match. spec_types, item))
1201
1186
fully_covered = match. fully_covers
1202
1187
else
1203
1188
fully_covered &= atype <: signature_union
1204
1189
end
1205
1190
1206
- # If we only have one case and that case is fully covered, we may either
1207
- # be able to do the inlining now (for constant cases), or push it directly
1208
- # onto the todo list
1209
- if fully_covered && length (cases) == 1
1210
- handle_single_case! (ir, idx, stmt, cases[1 ]. item, todo)
1211
- elseif length (cases) > 0
1212
- push! (todo, idx=> UnionSplit (fully_covered, atype, cases))
1213
- end
1214
- return nothing
1191
+ handle_cases! (ir, idx, stmt, sig, cases, fully_covered, todo)
1215
1192
end
1216
1193
1217
- # try to create `InliningCase`s using constant-prop'ed results
1218
- # currently it works only when constant-prop' succeeded for all (union-split) signatures
1219
- # TODO use any of constant-prop'ed results, and leave the other unhandled cases to later
1220
- # TODO this function contains a lot of duplications with `analyze_single_call!`, factor them out
1221
- function maybe_handle_const_call! (
1222
- ir:: IRCode , idx:: Int , stmt:: Expr , info:: ConstCallInfo , flag:: UInt8 ,
1194
+ # similar to `analyze_single_call!`, but with constant results
1195
+ function handle_const_call! (
1196
+ ir:: IRCode , idx:: Int , stmt:: Expr , cinfo:: ConstCallInfo , flag:: UInt8 ,
1223
1197
sig:: Signature , state:: InliningState , todo:: Vector{Pair{Int, Any}} )
1224
1198
(; argtypes, atype) = sig
1225
- results = info. results
1226
- cases = InliningCase[] # TODO avoid this allocation for single cases ?
1199
+ (; call, results) = cinfo
1200
+ infos = isa (call, MethodMatchInfo) ? MethodMatchInfo[call] : call. matches
1201
+ cases = InliningCase[]
1227
1202
local fully_covered = true
1228
1203
local signature_union = Bottom
1229
- for result in results
1230
- isa (result, InferenceResult) || return false
1231
- (; mi) = item = InliningTodo (result, argtypes)
1232
- spec_types = mi. specTypes
1233
- signature_union = Union{signature_union, spec_types}
1234
- if ! isdispatchtuple (spec_types)
1235
- fully_covered = false
1236
- continue
1237
- end
1238
- if ! validate_sparams (mi. sparam_vals)
1239
- fully_covered = false
1204
+ local j = 0
1205
+ for i in 1 : length (infos)
1206
+ meth = infos[i]. results
1207
+ if meth. ambig
1208
+ # Too many applicable methods
1209
+ # Or there is a (partial?) ambiguity
1210
+ return nothing
1211
+ elseif length (meth) == 0
1212
+ # No applicable methods; try next union split
1240
1213
continue
1241
1214
end
1242
- state. mi_cache != = nothing && (item = resolve_todo (item, state, flag))
1243
- if item === nothing
1244
- fully_covered = false
1245
- continue
1215
+ for match in meth
1216
+ j += 1
1217
+ result = results[j]
1218
+ if result === nothing
1219
+ signature_union = Union{signature_union, match. spec_types}
1220
+ fully_covered &= handle_match! (match, argtypes, flag, state, cases)
1221
+ else
1222
+ signature_union = Union{signature_union, result. linfo. specTypes}
1223
+ fully_covered &= handle_const_result! (result, argtypes, flag, state, cases)
1224
+ end
1246
1225
end
1247
- push! (cases, InliningCase (spec_types, item))
1248
1226
end
1249
1227
1250
1228
# if the signature is fully covered and there is only one applicable method,
1251
1229
# we can try to inline it even if the signature is not a dispatch tuple
1252
1230
if length (cases) == 0 && length (results) == 1
1253
1231
(; mi) = item = InliningTodo (results[1 ]:: InferenceResult , argtypes)
1254
1232
state. mi_cache != = nothing && (item = resolve_todo (item, state, flag))
1255
- validate_sparams (mi. sparam_vals) || return true
1256
- item === nothing && return true
1233
+ validate_sparams (mi. sparam_vals) || return nothing
1234
+ item === nothing && return nothing
1257
1235
push! (cases, InliningCase (mi. specTypes, item))
1258
1236
fully_covered = atype <: mi.specTypes
1259
1237
else
1260
1238
fully_covered &= atype <: signature_union
1261
1239
end
1262
1240
1241
+ handle_cases! (ir, idx, stmt, sig, cases, fully_covered, todo)
1242
+ end
1243
+
1244
+ function handle_match! (
1245
+ match:: MethodMatch , argtypes:: Vector{Any} , flag:: UInt8 , state:: InliningState ,
1246
+ cases:: Vector{InliningCase} )
1247
+ spec_types = match. spec_types
1248
+ isdispatchtuple (spec_types) || return false
1249
+ item = analyze_method! (match, argtypes, flag, state)
1250
+ item === nothing && return false
1251
+ _any (case-> case. sig === spec_types, cases) && return true
1252
+ push! (cases, InliningCase (spec_types, item))
1253
+ return true
1254
+ end
1255
+
1256
+ function handle_const_result! (
1257
+ result:: InferenceResult , argtypes:: Vector{Any} , flag:: UInt8 , state:: InliningState ,
1258
+ cases:: Vector{InliningCase} )
1259
+ (; mi) = item = InliningTodo (result, argtypes)
1260
+ spec_types = mi. specTypes
1261
+ isdispatchtuple (spec_types) || return false
1262
+ validate_sparams (mi. sparam_vals) || return false
1263
+ state. mi_cache != = nothing && (item = resolve_todo (item, state, flag))
1264
+ item === nothing && return false
1265
+ push! (cases, InliningCase (spec_types, item))
1266
+ return true
1267
+ end
1268
+
1269
+ function handle_cases! (ir:: IRCode , idx:: Int , stmt:: Expr , sig:: Signature ,
1270
+ cases:: Vector{InliningCase} , fully_covered:: Bool , todo:: Vector{Pair{Int, Any}} )
1263
1271
# If we only have one case and that case is fully covered, we may either
1264
1272
# be able to do the inlining now (for constant cases), or push it directly
1265
1273
# onto the todo list
1266
1274
if fully_covered && length (cases) == 1
1267
1275
handle_single_case! (ir, idx, stmt, cases[1 ]. item, todo)
1268
1276
elseif length (cases) > 0
1269
- push! (todo, idx=> UnionSplit (fully_covered, atype, cases))
1277
+ push! (todo, idx=> UnionSplit (fully_covered, sig . atype, cases))
1270
1278
end
1271
- return true
1279
+ return nothing
1272
1280
end
1273
1281
1274
1282
function handle_const_opaque_closure_call! (
@@ -1302,7 +1310,8 @@ function assemble_inline_todo!(ir::IRCode, state::InliningState)
1302
1310
end
1303
1311
ir. stmts[idx][:flag ] |= IR_FLAG_EFFECT_FREE
1304
1312
info = info. info
1305
- elseif info === false
1313
+ end
1314
+ if info === false
1306
1315
# Inference determined this couldn't be analyzed. Don't question it.
1307
1316
continue
1308
1317
end
@@ -1333,10 +1342,10 @@ function assemble_inline_todo!(ir::IRCode, state::InliningState)
1333
1342
# if inference arrived here with constant-prop'ed result(s),
1334
1343
# we can perform a specialized analysis for just this case
1335
1344
if isa (info, ConstCallInfo)
1336
- maybe_handle_const_call ! (
1345
+ handle_const_call ! (
1337
1346
ir, idx, stmt, info, flag,
1338
- sig, state, todo) && continue
1339
- info = info . call # cascade to the non-constant handling
1347
+ sig, state, todo)
1348
+ continue
1340
1349
end
1341
1350
1342
1351
# Ok, now figure out what method to call
0 commit comments