35
35
struct DelayedInliningSpec
36
36
match:: Union{MethodMatch, InferenceResult}
37
37
atypes:: Vector{Any}
38
- stmttype:: Any
39
38
end
40
39
41
40
struct InliningTodo
@@ -44,11 +43,11 @@ struct InliningTodo
44
43
spec:: Union{ResolvedInliningSpec, DelayedInliningSpec}
45
44
end
46
45
47
- InliningTodo (mi:: MethodInstance , match:: MethodMatch ,
48
- atypes :: Vector{Any} , @nospecialize (stmttype)) = InliningTodo (mi, DelayedInliningSpec (match, atypes, stmttype ))
46
+ InliningTodo (mi:: MethodInstance , match:: MethodMatch , atypes :: Vector{Any} ) =
47
+ InliningTodo (mi, DelayedInliningSpec (match, atypes))
49
48
50
- InliningTodo (result:: InferenceResult , atypes:: Vector{Any} , @nospecialize (stmttype) ) =
51
- InliningTodo (result. linfo, DelayedInliningSpec (result, atypes, stmttype ))
49
+ InliningTodo (result:: InferenceResult , atypes:: Vector{Any} ) =
50
+ InliningTodo (result. linfo, DelayedInliningSpec (result, atypes))
52
51
53
52
struct ConstantCase
54
53
val:: Any
@@ -677,7 +676,7 @@ function rewrite_apply_exprargs!(ir::IRCode, todo::Vector{Pair{Int, Any}}, idx::
677
676
handled = false
678
677
if isa (info, ConstCallInfo)
679
678
if ! is_stmt_noinline (flag) && maybe_handle_const_call! (
680
- ir, state1. id, new_stmt, info, new_sig,call . rt, istate, flag, false , todo)
679
+ ir, state1. id, new_stmt, info, new_sig, istate, flag, false , todo)
681
680
handled = true
682
681
else
683
682
info = info. call
@@ -687,8 +686,9 @@ function rewrite_apply_exprargs!(ir::IRCode, todo::Vector{Pair{Int, Any}}, idx::
687
686
info = isa (info, MethodMatchInfo) ?
688
687
MethodMatchInfo[info] : info. matches
689
688
# See if we can inline this call to `iterate`
690
- analyze_single_call! (ir, todo, state1. id, new_stmt,
691
- new_sig, call. rt, info, istate, flag)
689
+ analyze_single_call! (
690
+ ir, todo, state1. id, new_stmt,
691
+ new_sig, info, istate, flag)
692
692
end
693
693
if i != length (thisarginfo. each)
694
694
valT = getfield_tfunc (call. rt, Const (1 ))
@@ -708,11 +708,13 @@ function rewrite_apply_exprargs!(ir::IRCode, todo::Vector{Pair{Int, Any}}, idx::
708
708
return new_argexprs, new_atypes
709
709
end
710
710
711
- function rewrite_invoke_exprargs! (argexprs:: Vector{Any} )
711
+ function rewrite_invoke_exprargs! (expr:: Expr )
712
+ argexprs = expr. args
712
713
argexpr0 = argexprs[2 ]
713
714
argexprs = argexprs[4 : end ]
714
715
pushfirst! (argexprs, argexpr0)
715
- return argexprs
716
+ expr. args = argexprs
717
+ return expr
716
718
end
717
719
718
720
function compileable_specialization (et:: Union{EdgeTracker, Nothing} , match:: MethodMatch )
@@ -791,7 +793,7 @@ function validate_sparams(sparams::SimpleVector)
791
793
end
792
794
793
795
function analyze_method! (match:: MethodMatch , atypes:: Vector{Any} ,
794
- state:: InliningState , @nospecialize (stmttyp), flag:: UInt8 )
796
+ state:: InliningState , flag:: UInt8 )
795
797
method = match. method
796
798
methsig = method. sig
797
799
@@ -821,7 +823,7 @@ function analyze_method!(match::MethodMatch, atypes::Vector{Any},
821
823
return compileable_specialization (et, match)
822
824
end
823
825
824
- todo = InliningTodo (mi, match, atypes, stmttyp )
826
+ todo = InliningTodo (mi, match, atypes)
825
827
# If we don't have caches here, delay resolving this MethodInstance
826
828
# until the batch inlining step (or an external post-processing pass)
827
829
state. mi_cache === nothing && return todo
@@ -846,17 +848,13 @@ function handle_single_case!(ir::IRCode, stmt::Expr, idx::Int, @nospecialize(cas
846
848
if isa (case, ConstantCase)
847
849
ir[SSAValue (idx)] = case. val
848
850
elseif isa (case, MethodInstance)
849
- if isinvoke
850
- stmt. args = rewrite_invoke_exprargs! (stmt. args)
851
- end
851
+ isinvoke && rewrite_invoke_exprargs! (stmt)
852
852
stmt. head = :invoke
853
853
pushfirst! (stmt. args, case)
854
854
elseif case === nothing
855
855
# Do, well, nothing
856
856
else
857
- if isinvoke
858
- stmt. args = rewrite_invoke_exprargs! (stmt. args)
859
- end
857
+ isinvoke && rewrite_invoke_exprargs! (stmt)
860
858
push! (todo, idx=> (case:: InliningTodo ))
861
859
end
862
860
nothing
@@ -1005,7 +1003,6 @@ is_builtin(s::Signature) =
1005
1003
function inline_invoke! (ir:: IRCode , idx:: Int , sig:: Signature , (; match, result):: InvokeCallInfo ,
1006
1004
state:: InliningState , todo:: Vector{Pair{Int, Any}} , flag:: UInt8 )
1007
1005
stmt = ir. stmts[idx][:inst ]
1008
- calltype = ir. stmts[idx][:type ]
1009
1006
1010
1007
if ! match. fully_covers
1011
1008
# TODO : We could union split out the signature check and continue on
@@ -1018,7 +1015,7 @@ function inline_invoke!(ir::IRCode, idx::Int, sig::Signature, (; match, result):
1018
1015
pushfirst! (atypes, atype0)
1019
1016
1020
1017
if isa (result, InferenceResult) && ! is_stmt_noinline (flag)
1021
- (; mi) = item = InliningTodo (result, atypes, calltype )
1018
+ (; mi) = item = InliningTodo (result, atypes)
1022
1019
validate_sparams (mi. sparam_vals) || return nothing
1023
1020
if argtypes_to_type (atypes) <: mi.def.sig
1024
1021
state. mi_cache != = nothing && (item = resolve_todo (item, state, flag))
@@ -1027,7 +1024,7 @@ function inline_invoke!(ir::IRCode, idx::Int, sig::Signature, (; match, result):
1027
1024
end
1028
1025
end
1029
1026
1030
- result = analyze_method! (match, atypes, state, calltype, flag)
1027
+ result = analyze_method! (match, atypes, state, flag)
1031
1028
handle_single_case! (ir, stmt, idx, result, true , todo)
1032
1029
return nothing
1033
1030
end
@@ -1136,13 +1133,12 @@ function process_simple!(ir::IRCode, todo::Vector{Pair{Int, Any}}, idx::Int, sta
1136
1133
return sig
1137
1134
end
1138
1135
1139
- function analyze_single_call! (ir :: IRCode , todo :: Vector{Pair{Int, Any}} , idx :: Int , @nospecialize (stmt),
1140
- sig :: Signature , @nospecialize (calltype), infos :: Vector{MethodMatchInfo} ,
1141
- state:: InliningState , flag:: UInt8 )
1136
+ function analyze_single_call! (
1137
+ ir :: IRCode , todo :: Vector{Pair{Int, Any}} , idx :: Int , @nospecialize (stmt) ,
1138
+ sig :: Signature , infos :: Vector{MethodMatchInfo} , state:: InliningState , flag:: UInt8 )
1142
1139
cases = Pair{Any, Any}[]
1143
- signature_union = Union{}
1144
- only_method = nothing # keep track of whether there is one matching method
1145
- too_many = false
1140
+ local signature_union = Bottom
1141
+ local only_method = nothing # keep track of whether there is one matching method
1146
1142
local meth
1147
1143
local fully_covered = true
1148
1144
for i in 1 : length (infos)
@@ -1151,8 +1147,7 @@ function analyze_single_call!(ir::IRCode, todo::Vector{Pair{Int, Any}}, idx::Int
1151
1147
if meth. ambig
1152
1148
# Too many applicable methods
1153
1149
# Or there is a (partial?) ambiguity
1154
- too_many = true
1155
- break
1150
+ return
1156
1151
elseif length (meth) == 0
1157
1152
# No applicable methods; try next union split
1158
1153
continue
@@ -1172,7 +1167,7 @@ function analyze_single_call!(ir::IRCode, todo::Vector{Pair{Int, Any}}, idx::Int
1172
1167
fully_covered = false
1173
1168
continue
1174
1169
end
1175
- case = analyze_method! (match, sig. atypes, state, calltype, flag)
1170
+ case = analyze_method! (match, sig. atypes, state, flag)
1176
1171
if case === nothing
1177
1172
fully_covered = false
1178
1173
continue
@@ -1183,8 +1178,6 @@ function analyze_single_call!(ir::IRCode, todo::Vector{Pair{Int, Any}}, idx::Int
1183
1178
end
1184
1179
end
1185
1180
1186
- too_many && return
1187
-
1188
1181
signature_fully_covered = sig. atype <: signature_union
1189
1182
# If we're fully covered and there's only one applicable method,
1190
1183
# we inline, even if the signature is not a dispatch tuple
@@ -1199,7 +1192,7 @@ function analyze_single_call!(ir::IRCode, todo::Vector{Pair{Int, Any}}, idx::Int
1199
1192
match = meth[1 ]
1200
1193
end
1201
1194
fully_covered = true
1202
- case = analyze_method! (match, sig. atypes, state, calltype, flag)
1195
+ case = analyze_method! (match, sig. atypes, state, flag)
1203
1196
case === nothing && return
1204
1197
push! (cases, Pair {Any,Any} (match. spec_types, case))
1205
1198
end
@@ -1219,34 +1212,41 @@ function analyze_single_call!(ir::IRCode, todo::Vector{Pair{Int, Any}}, idx::Int
1219
1212
return nothing
1220
1213
end
1221
1214
1215
+ # try to create `InliningTodo`s using constant-prop'ed results
1216
+ # currently it works only when constant-prop' succeeded for all (union-split) signatures
1217
+ # TODO use any of constant-prop'ed results, and leave the other unhandled cases to later
1222
1218
function maybe_handle_const_call! (ir:: IRCode , idx:: Int , stmt:: Expr ,
1223
- info:: ConstCallInfo , sig:: Signature , @nospecialize (calltype),
1219
+ info:: ConstCallInfo , sig:: Signature ,
1224
1220
state:: InliningState , flag:: UInt8 ,
1225
1221
isinvoke:: Bool , todo:: Vector{Pair{Int, Any}} )
1226
- # when multiple matches are found, bail out and later inliner will union-split this signature
1227
- # TODO effectively use multiple constant analysis results here
1228
- length (info. results) == 1 || return false
1229
- result = info. results[1 ]
1230
- isa (result, InferenceResult) || return false
1231
-
1232
- (; mi) = item = InliningTodo (result, sig. atypes, calltype)
1233
- validate_sparams (mi. sparam_vals) || return true
1234
- mthd_sig = mi. def. sig
1235
- mistypes = mi. specTypes
1236
- state. mi_cache != = nothing && (item = resolve_todo (item, state, flag))
1237
- if sig. atype <: mthd_sig
1238
- handle_single_case! (ir, stmt, idx, item, isinvoke, todo)
1239
- return true
1240
- else
1241
- item === nothing && return true
1242
- # Union split out the error case
1243
- item = UnionSplit (false , sig. atype, Pair{Any, Any}[mistypes => item])
1244
- if isinvoke
1245
- stmt. args = rewrite_invoke_exprargs! (stmt. args)
1222
+ sigtype = sig. atype
1223
+ cases = Pair{Any, Any}[] # TODO avoid this allocation for single cases ?
1224
+ local fully_covered = true
1225
+ local signature_union = Bottom
1226
+ for result in info. results
1227
+ isa (result, InferenceResult) || return false
1228
+ (; mi) = item = InliningTodo (result, sig. atypes)
1229
+ if ! validate_sparams (mi. sparam_vals)
1230
+ fully_covered = false
1231
+ continue
1232
+ end
1233
+ state. mi_cache != = nothing && (item = resolve_todo (item, state, flag))
1234
+ if item === nothing
1235
+ fully_covered = false
1236
+ continue
1246
1237
end
1238
+ push! (cases, Pair {Any,Any} (mi. specTypes, item))
1239
+ signature_union = Union{signature_union, mi. def. sig}
1240
+ end
1241
+ fully_covered &= sigtype <: signature_union
1242
+ if fully_covered && length (cases) == 1
1243
+ handle_single_case! (ir, stmt, idx, cases[1 ][2 ], isinvoke, todo)
1244
+ elseif length (cases) > 0
1245
+ item = UnionSplit (fully_covered, sigtype, cases)
1246
+ isinvoke && rewrite_invoke_exprargs! (stmt)
1247
1247
push! (todo, idx=> item)
1248
- return true
1249
1248
end
1249
+ return true
1250
1250
end
1251
1251
1252
1252
function assemble_inline_todo! (ir:: IRCode , state:: InliningState )
@@ -1258,11 +1258,11 @@ function assemble_inline_todo!(ir::IRCode, state::InliningState)
1258
1258
sig === nothing && continue
1259
1259
1260
1260
stmt = ir. stmts[idx][:inst ]
1261
- calltype = ir. stmts[idx][:type ]
1262
1261
info = ir. stmts[idx][:info ]
1263
1262
1264
1263
# Check whether this call was @pure and evaluates to a constant
1265
1264
if info isa MethodResultPure
1265
+ calltype = ir. stmts[idx][:type ]
1266
1266
if calltype isa Const && is_inlineable_constant (calltype. val)
1267
1267
ir. stmts[idx][:inst ] = quoted (calltype. val)
1268
1268
continue
@@ -1278,20 +1278,19 @@ function assemble_inline_todo!(ir::IRCode, state::InliningState)
1278
1278
continue
1279
1279
end
1280
1280
1281
- # If inference arrived at this result by using constant propagation,
1282
- # it'll have performed a specialized analysis for just this case. Use its
1283
- # result.
1281
+ # if inference arrived here with constant-prop'ed result(s),
1282
+ # we can perform a specialized analysis for just this case
1284
1283
if isa (info, ConstCallInfo)
1285
1284
if ! is_stmt_noinline (flag) && maybe_handle_const_call! (
1286
- ir, idx, stmt, info, sig, calltype, state, flag, sig. f === Core. invoke, todo)
1285
+ ir, idx, stmt, info, sig, state, flag, sig. f === Core. invoke, todo)
1287
1286
continue
1288
1287
else
1289
1288
info = info. call
1290
1289
end
1291
1290
end
1292
1291
1293
1292
if isa (info, OpaqueClosureCallInfo)
1294
- result = analyze_method! (info. match, sig. atypes, state, calltype, flag)
1293
+ result = analyze_method! (info. match, sig. atypes, state, flag)
1295
1294
handle_single_case! (ir, stmt, idx, result, false , todo)
1296
1295
continue
1297
1296
end
@@ -1313,7 +1312,7 @@ function assemble_inline_todo!(ir::IRCode, state::InliningState)
1313
1312
continue
1314
1313
end
1315
1314
1316
- analyze_single_call! (ir, todo, idx, stmt, sig, calltype, infos, state, flag)
1315
+ analyze_single_call! (ir, todo, idx, stmt, sig, infos, state, flag)
1317
1316
end
1318
1317
todo
1319
1318
end
0 commit comments