@@ -29,7 +29,7 @@ function is_improvable(@nospecialize(rtype))
29
29
end
30
30
31
31
function abstract_call_gf_by_type (interp:: AbstractInterpreter , @nospecialize (f),
32
- fargs:: Union{Nothing,Vector{Any}} , argtypes:: Vector{Any} , @nospecialize (atype),
32
+ (; fargs, argtypes) :: ArgInfo , @nospecialize (atype),
33
33
sv:: InferenceState , max_methods:: Int = InferenceParams (interp). MAX_METHODS)
34
34
if sv. params. unoptimize_throw_blocks && is_stmt_throw_block (get_curr_ssaflag (sv))
35
35
add_remark! (interp, sv, " Skipped call in throw block" )
@@ -85,7 +85,8 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
85
85
push! (edges, edge)
86
86
end
87
87
this_argtypes = isa (matches, MethodMatches) ? argtypes : matches. applicable_argtypes[i]
88
- const_result = abstract_call_method_with_const_args (interp, result, f, this_argtypes, match, sv, false )
88
+ arginfo = ArgInfo (fargs, this_argtypes)
89
+ const_result = abstract_call_method_with_const_args (interp, result, f, arginfo, match, sv, false )
89
90
if const_result != = nothing
90
91
const_rt, const_result = const_result
91
92
if const_rt != = rt && const_rt ⊑ rt
@@ -110,7 +111,8 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
110
111
# try constant propagation with argtypes for this match
111
112
# this is in preparation for inlining, or improving the return result
112
113
this_argtypes = isa (matches, MethodMatches) ? argtypes : matches. applicable_argtypes[i]
113
- const_result = abstract_call_method_with_const_args (interp, result, f, this_argtypes, match, sv, false )
114
+ arginfo = ArgInfo (fargs, this_argtypes)
115
+ const_result = abstract_call_method_with_const_args (interp, result, f, arginfo, match, sv, false )
114
116
if const_result != = nothing
115
117
const_this_rt, const_result = const_result
116
118
if const_this_rt != = this_rt && const_this_rt ⊑ this_rt
@@ -523,13 +525,13 @@ struct MethodCallResult
523
525
end
524
526
525
527
function abstract_call_method_with_const_args (interp:: AbstractInterpreter , result:: MethodCallResult ,
526
- @nospecialize (f), argtypes :: Vector{Any} , match:: MethodMatch ,
528
+ @nospecialize (f), arginfo :: ArgInfo , match:: MethodMatch ,
527
529
sv:: InferenceState , va_override:: Bool )
528
- mi = maybe_get_const_prop_profitable (interp, result, f, argtypes , match, sv)
530
+ mi = maybe_get_const_prop_profitable (interp, result, f, arginfo , match, sv)
529
531
mi === nothing && return nothing
530
532
# try constant prop'
531
533
inf_cache = get_inference_cache (interp)
532
- inf_result = cache_lookup (mi, argtypes, inf_cache)
534
+ inf_result = cache_lookup (mi, arginfo . argtypes, inf_cache)
533
535
if inf_result === nothing
534
536
# if there might be a cycle, check to make sure we don't end up
535
537
# calling ourselves here.
@@ -545,7 +547,7 @@ function abstract_call_method_with_const_args(interp::AbstractInterpreter, resul
545
547
return nothing
546
548
end
547
549
end
548
- inf_result = InferenceResult (mi, argtypes , va_override)
550
+ inf_result = InferenceResult (mi; arginfo , va_override)
549
551
if ! any (inf_result. overridden_by_const)
550
552
add_remark! (interp, sv, " [constprop] Could not handle constant info in matching_cache_argtypes" )
551
553
return nothing
565
567
# if there's a possibility we could get a better result (hopefully without doing too much work)
566
568
# returns `MethodInstance` with constant arguments, returns nothing otherwise
567
569
function maybe_get_const_prop_profitable (interp:: AbstractInterpreter , result:: MethodCallResult ,
568
- @nospecialize (f), argtypes :: Vector{Any} , match:: MethodMatch ,
570
+ @nospecialize (f), arginfo :: ArgInfo , match:: MethodMatch ,
569
571
sv:: InferenceState )
570
572
if ! InferenceParams (interp). ipo_constant_propagation
571
573
add_remark! (interp, sv, " [constprop] Disabled by parameter" )
@@ -580,14 +582,14 @@ function maybe_get_const_prop_profitable(interp::AbstractInterpreter, result::Me
580
582
force || const_prop_entry_heuristic (interp, result, sv) || return nothing
581
583
nargs:: Int = method. nargs
582
584
method. isva && (nargs -= 1 )
583
- length (argtypes) < nargs && return nothing
584
- if ! (const_prop_argument_heuristic (interp, argtypes ) || const_prop_rettype_heuristic (interp, result. rt))
585
+ length (arginfo . argtypes) < nargs && return nothing
586
+ if ! (const_prop_argument_heuristic (interp, arginfo ) || const_prop_rettype_heuristic (interp, result. rt))
585
587
add_remark! (interp, sv, " [constprop] Disabled by argument and rettype heuristics" )
586
588
return nothing
587
589
end
588
- allconst = is_allconst (argtypes )
590
+ allconst = is_allconst (arginfo )
589
591
if ! force
590
- if ! const_prop_function_heuristic (interp, f, argtypes , nargs, allconst)
592
+ if ! const_prop_function_heuristic (interp, f, arginfo , nargs, allconst)
591
593
add_remark! (interp, sv, " [constprop] Disabled by function heuristic" )
592
594
return nothing
593
595
end
@@ -599,7 +601,7 @@ function maybe_get_const_prop_profitable(interp::AbstractInterpreter, result::Me
599
601
return nothing
600
602
end
601
603
mi = mi:: MethodInstance
602
- if ! force && ! const_prop_methodinstance_heuristic (interp, match, mi, argtypes , sv)
604
+ if ! force && ! const_prop_methodinstance_heuristic (interp, match, mi, arginfo , sv)
603
605
add_remark! (interp, sv, " [constprop] Disabled by method instance heuristic" )
604
606
return nothing
605
607
end
@@ -617,8 +619,11 @@ function const_prop_entry_heuristic(interp::AbstractInterpreter, result::MethodC
617
619
end
618
620
619
621
# see if propagating constants may be worthwhile
620
- function const_prop_argument_heuristic (interp:: AbstractInterpreter , argtypes:: Vector{Any} )
622
+ function const_prop_argument_heuristic (interp:: AbstractInterpreter , (; fargs, argtypes) :: ArgInfo )
621
623
for a in argtypes
624
+ if isa (a, Conditional) && fargs != = nothing
625
+ return is_const_prop_profitable_conditional (a, fargs)
626
+ end
622
627
a = widenconditional (a)
623
628
if has_nontrivial_const_info (a) && is_const_prop_profitable_arg (a)
624
629
return true
@@ -642,13 +647,34 @@ function is_const_prop_profitable_arg(@nospecialize(arg))
642
647
return isa (val, Symbol) || isa (val, Type) || (! isa (val, String) && ! ismutable (val))
643
648
end
644
649
650
+ function is_const_prop_profitable_conditional (cnd:: Conditional , fargs:: Vector{Any} )
651
+ slotid = find_constrained_arg (cnd, fargs)
652
+ if slotid != = nothing
653
+ return true
654
+ end
655
+ return is_const_prop_profitable_arg (widenconditional (cnd))
656
+ end
657
+
658
+ function find_constrained_arg (cnd:: Conditional , fargs:: Vector{Any} )
659
+ slot = cnd. var
660
+ return findfirst (fargs) do @nospecialize (x)
661
+ x === slot
662
+ end
663
+ end
664
+
645
665
function const_prop_rettype_heuristic (interp:: AbstractInterpreter , @nospecialize (rettype))
646
666
return improvable_via_constant_propagation (rettype)
647
667
end
648
668
649
- function is_allconst (argtypes:: Vector{Any} )
669
+ function is_allconst ((; fargs, argtypes) :: ArgInfo )
650
670
for a in argtypes
671
+ if isa (a, Conditional) && fargs != = nothing
672
+ if is_const_prop_profitable_conditional (a, fargs)
673
+ continue
674
+ end
675
+ end
651
676
a = widenconditional (a)
677
+ # TODO unify these condition with `has_nontrivial_const_info`
652
678
if ! isa (a, Const) && ! isconstType (a) && ! isa (a, PartialStruct) && ! isa (a, PartialOpaque)
653
679
return false
654
680
end
@@ -663,7 +689,9 @@ function force_const_prop(interp::AbstractInterpreter, @nospecialize(f), method:
663
689
istopfunction (f, :setproperty! )
664
690
end
665
691
666
- function const_prop_function_heuristic (interp:: AbstractInterpreter , @nospecialize (f), argtypes:: Vector{Any} , nargs:: Int , allconst:: Bool )
692
+ function const_prop_function_heuristic (
693
+ interp:: AbstractInterpreter , @nospecialize (f), (; argtypes):: ArgInfo ,
694
+ nargs:: Int , allconst:: Bool )
667
695
if nargs > 1
668
696
if istopfunction (f, :getindex ) || istopfunction (f, :setindex! )
669
697
arrty = argtypes[2 ]
705
733
# result anyway.
706
734
function const_prop_methodinstance_heuristic (
707
735
interp:: AbstractInterpreter , match:: MethodMatch , mi:: MethodInstance ,
708
- argtypes:: Vector{Any} , sv:: InferenceState )
736
+ (; argtypes) :: ArgInfo , sv:: InferenceState )
709
737
method = match. method
710
738
if method. is_for_opaque_closure
711
739
# Not inlining an opaque closure can be very expensive, so be generous
@@ -832,7 +860,7 @@ function abstract_iteration(interp::AbstractInterpreter, @nospecialize(itft), @n
832
860
return Any[Vararg{Any}], nothing
833
861
end
834
862
@assert ! isvarargtype (itertype)
835
- call = abstract_call_known (interp, iteratef, nothing , Any[itft, itertype], sv)
863
+ call = abstract_call_known (interp, iteratef, ArgInfo ( nothing , Any[itft, itertype]) , sv)
836
864
stateordonet = call. rt
837
865
info = call. info
838
866
# Return Bottom if this is not an iterator.
@@ -866,7 +894,7 @@ function abstract_iteration(interp::AbstractInterpreter, @nospecialize(itft), @n
866
894
valtype = getfield_tfunc (stateordonet, Const (1 ))
867
895
push! (ret, valtype)
868
896
statetype = nstatetype
869
- call = abstract_call_known (interp, iteratef, nothing , Any[Const (iteratef), itertype, statetype], sv)
897
+ call = abstract_call_known (interp, iteratef, ArgInfo ( nothing , Any[Const (iteratef), itertype, statetype]) , sv)
870
898
stateordonet = call. rt
871
899
stateordonet_widened = widenconst (stateordonet)
872
900
push! (calls, call)
@@ -901,7 +929,7 @@ function abstract_iteration(interp::AbstractInterpreter, @nospecialize(itft), @n
901
929
end
902
930
valtype = tmerge (valtype, nounion. parameters[1 ])
903
931
statetype = tmerge (statetype, nounion. parameters[2 ])
904
- stateordonet = abstract_call_known (interp, iteratef, nothing , Any[Const (iteratef), itertype, statetype], sv). rt
932
+ stateordonet = abstract_call_known (interp, iteratef, ArgInfo ( nothing , Any[Const (iteratef), itertype, statetype]) , sv). rt
905
933
stateordonet_widened = widenconst (stateordonet)
906
934
end
907
935
if valtype != = Union{}
@@ -990,7 +1018,7 @@ function abstract_apply(interp::AbstractInterpreter, argtypes::Vector{Any}, sv::
990
1018
break
991
1019
end
992
1020
end
993
- call = abstract_call (interp, nothing , ct, sv, max_methods)
1021
+ call = abstract_call (interp, ArgInfo ( nothing , ct) , sv, max_methods)
994
1022
push! (retinfos, ApplyCallInfo (call. info, arginfo))
995
1023
res = tmerge (res, call. rt)
996
1024
if bail_out_apply (interp, res, sv)
@@ -1054,8 +1082,8 @@ function argtype_tail(argtypes::Vector{Any}, i::Int)
1054
1082
return argtypes[i: n]
1055
1083
end
1056
1084
1057
- function abstract_call_builtin (interp:: AbstractInterpreter , f:: Builtin , fargs:: Union{Nothing,Vector{Any}} ,
1058
- argtypes :: Vector{Any} , sv:: InferenceState , max_methods:: Int )
1085
+ function abstract_call_builtin (interp:: AbstractInterpreter , f:: Builtin , (; fargs, argtypes) :: ArgInfo ,
1086
+ sv:: InferenceState , max_methods:: Int )
1059
1087
@nospecialize f
1060
1088
la = length (argtypes)
1061
1089
if f === ifelse && fargs isa Vector{Any} && la == 4
@@ -1188,7 +1216,7 @@ function abstract_call_unionall(argtypes::Vector{Any})
1188
1216
return Any
1189
1217
end
1190
1218
1191
- function abstract_invoke (interp:: AbstractInterpreter , argtypes:: Vector{Any} , sv:: InferenceState )
1219
+ function abstract_invoke (interp:: AbstractInterpreter , (; fargs, argtypes) :: ArgInfo , sv:: InferenceState )
1192
1220
ft′ = argtype_by_index (argtypes, 2 )
1193
1221
ft = widenconst (ft′)
1194
1222
ft === Bottom && return CallMeta (Bottom, false )
@@ -1216,14 +1244,17 @@ function abstract_invoke(interp::AbstractInterpreter, argtypes::Vector{Any}, sv:
1216
1244
# since some checks within `abstract_call_method_with_const_args` seem a bit costly
1217
1245
const_prop_entry_heuristic (interp, result, sv) || return CallMeta (rt, InvokeCallInfo (match, nothing ))
1218
1246
argtypes′ = argtypes[4 : end ]
1219
- const_prop_argument_heuristic (interp, argtypes′) || const_prop_rettype_heuristic (interp, rt) || return CallMeta (rt, InvokeCallInfo (match, nothing ))
1220
1247
pushfirst! (argtypes′, ft)
1248
+ fargs′ = fargs[4 : end ]
1249
+ pushfirst! (fargs′, fargs[1 ])
1250
+ arginfo = ArgInfo (fargs′, argtypes′)
1251
+ const_prop_argument_heuristic (interp, arginfo) || const_prop_rettype_heuristic (interp, rt) || return CallMeta (rt, InvokeCallInfo (match, nothing ))
1221
1252
# # typeintersect might have narrowed signature, but the accuracy gain doesn't seem worth the cost involved with the lattice comparisons
1222
1253
# for i in 1:length(argtypes′)
1223
1254
# t, a = ti.parameters[i], argtypes′[i]
1224
1255
# argtypes′[i] = t ⊑ a ? t : a
1225
1256
# end
1226
- const_result = abstract_call_method_with_const_args (interp, result, singleton_type (ft′), argtypes′ , match, sv, false )
1257
+ const_result = abstract_call_method_with_const_args (interp, result, singleton_type (ft′), arginfo , match, sv, false )
1227
1258
if const_result != = nothing
1228
1259
const_rt, const_result = const_result
1229
1260
if const_rt != = rt && const_rt ⊑ rt
@@ -1235,21 +1266,20 @@ end
1235
1266
1236
1267
# call where the function is known exactly
1237
1268
function abstract_call_known (interp:: AbstractInterpreter , @nospecialize (f),
1238
- fargs:: Union{Nothing,Vector{Any}} , argtypes:: Vector{Any} ,
1239
- sv:: InferenceState ,
1269
+ arginfo:: ArgInfo , sv:: InferenceState ,
1240
1270
max_methods:: Int = InferenceParams (interp). MAX_METHODS)
1241
-
1271
+ (; fargs, argtypes) = arginfo
1242
1272
la = length (argtypes)
1243
1273
1244
1274
if isa (f, Builtin)
1245
1275
if f === _apply_iterate
1246
1276
return abstract_apply (interp, argtypes, sv, max_methods)
1247
1277
elseif f === invoke
1248
- return abstract_invoke (interp, argtypes , sv)
1278
+ return abstract_invoke (interp, arginfo , sv)
1249
1279
elseif f === modifyfield!
1250
1280
return abstract_modifyfield! (interp, argtypes, sv)
1251
1281
end
1252
- return CallMeta (abstract_call_builtin (interp, f, fargs, argtypes , sv, max_methods), false )
1282
+ return CallMeta (abstract_call_builtin (interp, f, arginfo , sv, max_methods), false )
1253
1283
elseif f === Core. kwfunc
1254
1284
if la == 2
1255
1285
ft = widenconst (argtypes[2 ])
@@ -1282,12 +1312,12 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f),
1282
1312
# handle Conditional propagation through !Bool
1283
1313
aty = argtypes[2 ]
1284
1314
if isa (aty, Conditional)
1285
- call = abstract_call_gf_by_type (interp, f, fargs, Any[Const (f), Bool], Tuple{typeof (f), Bool}, sv) # make sure we've inferred `!(::Bool)`
1315
+ call = abstract_call_gf_by_type (interp, f, ArgInfo ( fargs, Any[Const (f), Bool]) , Tuple{typeof (f), Bool}, sv) # make sure we've inferred `!(::Bool)`
1286
1316
return CallMeta (Conditional (aty. var, aty. elsetype, aty. vtype), call. info)
1287
1317
end
1288
1318
elseif la == 3 && istopfunction (f, :!= = )
1289
1319
# mark !== as exactly a negated call to ===
1290
- rty = abstract_call_known (interp, (=== ), fargs, argtypes , sv). rt
1320
+ rty = abstract_call_known (interp, (=== ), arginfo , sv). rt
1291
1321
if isa (rty, Conditional)
1292
1322
return CallMeta (Conditional (rty. var, rty. elsetype, rty. vtype), false ) # swap if-else
1293
1323
elseif isa (rty, Const)
@@ -1303,7 +1333,7 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f),
1303
1333
fargs = nothing
1304
1334
end
1305
1335
argtypes = Any[typeof (< :), argtypes[3 ], argtypes[2 ]]
1306
- return CallMeta (abstract_call_known (interp, < :, fargs, argtypes, sv). rt, false )
1336
+ return CallMeta (abstract_call_known (interp, < :, ArgInfo ( fargs, argtypes) , sv). rt, false )
1307
1337
elseif la == 2 &&
1308
1338
(a2 = argtypes[2 ]; isa (a2, Const)) && (svecval = a2. val; isa (svecval, SimpleVector)) &&
1309
1339
istopfunction (f, :length )
@@ -1326,7 +1356,7 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f),
1326
1356
return CallMeta (val === false ? Type : val, MethodResultPure ())
1327
1357
end
1328
1358
atype = argtypes_to_type (argtypes)
1329
- return abstract_call_gf_by_type (interp, f, fargs, argtypes , atype, sv, max_methods)
1359
+ return abstract_call_gf_by_type (interp, f, arginfo , atype, sv, max_methods)
1330
1360
end
1331
1361
1332
1362
function abstract_call_opaque_closure (interp:: AbstractInterpreter , closure:: PartialOpaque , argtypes:: Vector{Any} , sv:: InferenceState )
@@ -1339,8 +1369,8 @@ function abstract_call_opaque_closure(interp::AbstractInterpreter, closure::Part
1339
1369
match = MethodMatch (sig, Core. svec (), closure. source, sig <: rewrap_unionall (sigT, tt))
1340
1370
info = OpaqueClosureCallInfo (match)
1341
1371
if ! result. edgecycle
1342
- const_result = abstract_call_method_with_const_args (interp, result, closure, argtypes,
1343
- match, sv, closure. isva)
1372
+ const_result = abstract_call_method_with_const_args (interp, result, closure,
1373
+ ArgInfo ( nothing , argtypes), match, sv, closure. isva)
1344
1374
if const_result != = nothing
1345
1375
const_rettype, const_result = const_result
1346
1376
if const_rettype ⊑ rt
@@ -1363,9 +1393,9 @@ function most_general_argtypes(closure::PartialOpaque)
1363
1393
end
1364
1394
1365
1395
# call where the function is any lattice element
1366
- function abstract_call (interp:: AbstractInterpreter , fargs :: Union{Nothing,Vector{Any}} , argtypes :: Vector{Any} ,
1396
+ function abstract_call (interp:: AbstractInterpreter , arginfo :: ArgInfo ,
1367
1397
sv:: InferenceState , max_methods:: Int = InferenceParams (interp). MAX_METHODS)
1368
- # print("call ", e.args[1], argtypes, "\n\n")
1398
+ argtypes = arginfo . argtypes
1369
1399
ft = argtypes[1 ]
1370
1400
f = singleton_type (ft)
1371
1401
if isa (ft, PartialOpaque)
@@ -1379,9 +1409,9 @@ function abstract_call(interp::AbstractInterpreter, fargs::Union{Nothing,Vector{
1379
1409
add_remark! (interp, sv, " Could not identify method table for call" )
1380
1410
return CallMeta (Any, false )
1381
1411
end
1382
- return abstract_call_gf_by_type (interp, nothing , fargs, argtypes , argtypes_to_type (argtypes), sv, max_methods)
1412
+ return abstract_call_gf_by_type (interp, nothing , arginfo , argtypes_to_type (argtypes), sv, max_methods)
1383
1413
end
1384
- return abstract_call_known (interp, f, fargs, argtypes , sv, max_methods)
1414
+ return abstract_call_known (interp, f, arginfo , sv, max_methods)
1385
1415
end
1386
1416
1387
1417
function sp_type_rewrap (@nospecialize (T), linfo:: MethodInstance , isreturn:: Bool )
@@ -1428,7 +1458,7 @@ function abstract_eval_cfunction(interp::AbstractInterpreter, e::Expr, vtypes::V
1428
1458
# this may be the wrong world for the call,
1429
1459
# but some of the result is likely to be valid anyways
1430
1460
# and that may help generate better codegen
1431
- abstract_call (interp, nothing , at, sv)
1461
+ abstract_call (interp, ArgInfo ( nothing , at) , sv)
1432
1462
nothing
1433
1463
end
1434
1464
@@ -1502,7 +1532,7 @@ function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e),
1502
1532
if argtypes === nothing
1503
1533
t = Bottom
1504
1534
else
1505
- callinfo = abstract_call (interp, ea, argtypes, sv)
1535
+ callinfo = abstract_call (interp, ArgInfo ( ea, argtypes) , sv)
1506
1536
sv. stmt_info[sv. currpc] = callinfo. info
1507
1537
t = callinfo. rt
1508
1538
end
0 commit comments