@@ -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
@@ -835,7 +863,7 @@ function abstract_iteration(interp::AbstractInterpreter, @nospecialize(itft), @n
835
863
return Any[Vararg{Any}], nothing
836
864
end
837
865
@assert ! isvarargtype (itertype)
838
- call = abstract_call_known (interp, iteratef, nothing , Any[itft, itertype], sv)
866
+ call = abstract_call_known (interp, iteratef, ArgInfo ( nothing , Any[itft, itertype]) , sv)
839
867
stateordonet = call. rt
840
868
info = call. info
841
869
# Return Bottom if this is not an iterator.
@@ -869,7 +897,7 @@ function abstract_iteration(interp::AbstractInterpreter, @nospecialize(itft), @n
869
897
valtype = getfield_tfunc (stateordonet, Const (1 ))
870
898
push! (ret, valtype)
871
899
statetype = nstatetype
872
- call = abstract_call_known (interp, iteratef, nothing , Any[Const (iteratef), itertype, statetype], sv)
900
+ call = abstract_call_known (interp, iteratef, ArgInfo ( nothing , Any[Const (iteratef), itertype, statetype]) , sv)
873
901
stateordonet = call. rt
874
902
stateordonet_widened = widenconst (stateordonet)
875
903
push! (calls, call)
@@ -904,7 +932,7 @@ function abstract_iteration(interp::AbstractInterpreter, @nospecialize(itft), @n
904
932
end
905
933
valtype = tmerge (valtype, nounion. parameters[1 ])
906
934
statetype = tmerge (statetype, nounion. parameters[2 ])
907
- stateordonet = abstract_call_known (interp, iteratef, nothing , Any[Const (iteratef), itertype, statetype], sv). rt
935
+ stateordonet = abstract_call_known (interp, iteratef, ArgInfo ( nothing , Any[Const (iteratef), itertype, statetype]) , sv). rt
908
936
stateordonet_widened = widenconst (stateordonet)
909
937
end
910
938
if valtype != = Union{}
@@ -993,7 +1021,7 @@ function abstract_apply(interp::AbstractInterpreter, argtypes::Vector{Any}, sv::
993
1021
break
994
1022
end
995
1023
end
996
- call = abstract_call (interp, nothing , ct, sv, max_methods)
1024
+ call = abstract_call (interp, ArgInfo ( nothing , ct) , sv, max_methods)
997
1025
push! (retinfos, ApplyCallInfo (call. info, arginfo))
998
1026
res = tmerge (res, call. rt)
999
1027
if bail_out_apply (interp, res, sv)
@@ -1057,8 +1085,8 @@ function argtype_tail(argtypes::Vector{Any}, i::Int)
1057
1085
return argtypes[i: n]
1058
1086
end
1059
1087
1060
- function abstract_call_builtin (interp:: AbstractInterpreter , f:: Builtin , fargs:: Union{Nothing,Vector{Any}} ,
1061
- argtypes :: Vector{Any} , sv:: InferenceState , max_methods:: Int )
1088
+ function abstract_call_builtin (interp:: AbstractInterpreter , f:: Builtin , (; fargs, argtypes) :: ArgInfo ,
1089
+ sv:: InferenceState , max_methods:: Int )
1062
1090
@nospecialize f
1063
1091
la = length (argtypes)
1064
1092
if f === ifelse && fargs isa Vector{Any} && la == 4
@@ -1190,7 +1218,7 @@ function abstract_call_unionall(argtypes::Vector{Any})
1190
1218
return Any
1191
1219
end
1192
1220
1193
- function abstract_invoke (interp:: AbstractInterpreter , argtypes:: Vector{Any} , sv:: InferenceState )
1221
+ function abstract_invoke (interp:: AbstractInterpreter , (; fargs, argtypes) :: ArgInfo , sv:: InferenceState )
1194
1222
ft′ = argtype_by_index (argtypes, 2 )
1195
1223
ft = widenconst (ft′)
1196
1224
ft === Bottom && return CallMeta (Bottom, false )
@@ -1218,14 +1246,17 @@ function abstract_invoke(interp::AbstractInterpreter, argtypes::Vector{Any}, sv:
1218
1246
# since some checks within `abstract_call_method_with_const_args` seem a bit costly
1219
1247
const_prop_entry_heuristic (interp, result, sv) || return CallMeta (rt, InvokeCallInfo (match, nothing ))
1220
1248
argtypes′ = argtypes[4 : end ]
1221
- const_prop_argument_heuristic (interp, argtypes′) || const_prop_rettype_heuristic (interp, rt) || return CallMeta (rt, InvokeCallInfo (match, nothing ))
1222
1249
pushfirst! (argtypes′, ft)
1250
+ fargs′ = fargs[4 : end ]
1251
+ pushfirst! (fargs′, fargs[1 ])
1252
+ arginfo = ArgInfo (fargs′, argtypes′)
1253
+ const_prop_argument_heuristic (interp, arginfo) || const_prop_rettype_heuristic (interp, rt) || return CallMeta (rt, InvokeCallInfo (match, nothing ))
1223
1254
# # typeintersect might have narrowed signature, but the accuracy gain doesn't seem worth the cost involved with the lattice comparisons
1224
1255
# for i in 1:length(argtypes′)
1225
1256
# t, a = ti.parameters[i], argtypes′[i]
1226
1257
# argtypes′[i] = t ⊑ a ? t : a
1227
1258
# end
1228
- const_result = abstract_call_method_with_const_args (interp, result, singleton_type (ft′), argtypes′ , match, sv, false )
1259
+ const_result = abstract_call_method_with_const_args (interp, result, singleton_type (ft′), arginfo , match, sv, false )
1229
1260
if const_result != = nothing
1230
1261
const_rt, const_result = const_result
1231
1262
if const_rt != = rt && const_rt ⊑ rt
@@ -1237,21 +1268,20 @@ end
1237
1268
1238
1269
# call where the function is known exactly
1239
1270
function abstract_call_known (interp:: AbstractInterpreter , @nospecialize (f),
1240
- fargs:: Union{Nothing,Vector{Any}} , argtypes:: Vector{Any} ,
1241
- sv:: InferenceState ,
1271
+ arginfo:: ArgInfo , sv:: InferenceState ,
1242
1272
max_methods:: Int = InferenceParams (interp). MAX_METHODS)
1243
-
1273
+ (; fargs, argtypes) = arginfo
1244
1274
la = length (argtypes)
1245
1275
1246
1276
if isa (f, Builtin)
1247
1277
if f === _apply_iterate
1248
1278
return abstract_apply (interp, argtypes, sv, max_methods)
1249
1279
elseif f === invoke
1250
- return abstract_invoke (interp, argtypes , sv)
1280
+ return abstract_invoke (interp, arginfo , sv)
1251
1281
elseif f === modifyfield!
1252
1282
return abstract_modifyfield! (interp, argtypes, sv)
1253
1283
end
1254
- return CallMeta (abstract_call_builtin (interp, f, fargs, argtypes , sv, max_methods), false )
1284
+ return CallMeta (abstract_call_builtin (interp, f, arginfo , sv, max_methods), false )
1255
1285
elseif f === Core. kwfunc
1256
1286
if la == 2
1257
1287
ft = widenconst (argtypes[2 ])
@@ -1284,12 +1314,12 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f),
1284
1314
# handle Conditional propagation through !Bool
1285
1315
aty = argtypes[2 ]
1286
1316
if isa (aty, Conditional)
1287
- call = abstract_call_gf_by_type (interp, f, fargs, Any[Const (f), Bool], Tuple{typeof (f), Bool}, sv) # make sure we've inferred `!(::Bool)`
1317
+ 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)`
1288
1318
return CallMeta (Conditional (aty. var, aty. elsetype, aty. vtype), call. info)
1289
1319
end
1290
1320
elseif la == 3 && istopfunction (f, :!= = )
1291
1321
# mark !== as exactly a negated call to ===
1292
- rty = abstract_call_known (interp, (=== ), fargs, argtypes , sv). rt
1322
+ rty = abstract_call_known (interp, (=== ), arginfo , sv). rt
1293
1323
if isa (rty, Conditional)
1294
1324
return CallMeta (Conditional (rty. var, rty. elsetype, rty. vtype), false ) # swap if-else
1295
1325
elseif isa (rty, Const)
@@ -1305,7 +1335,7 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f),
1305
1335
fargs = nothing
1306
1336
end
1307
1337
argtypes = Any[typeof (< :), argtypes[3 ], argtypes[2 ]]
1308
- return CallMeta (abstract_call_known (interp, < :, fargs, argtypes, sv). rt, false )
1338
+ return CallMeta (abstract_call_known (interp, < :, ArgInfo ( fargs, argtypes) , sv). rt, false )
1309
1339
elseif la == 2 &&
1310
1340
(a2 = argtypes[2 ]; isa (a2, Const)) && (svecval = a2. val; isa (svecval, SimpleVector)) &&
1311
1341
istopfunction (f, :length )
@@ -1328,7 +1358,7 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f),
1328
1358
return CallMeta (val === false ? Type : val, MethodResultPure ())
1329
1359
end
1330
1360
atype = argtypes_to_type (argtypes)
1331
- return abstract_call_gf_by_type (interp, f, fargs, argtypes , atype, sv, max_methods)
1361
+ return abstract_call_gf_by_type (interp, f, arginfo , atype, sv, max_methods)
1332
1362
end
1333
1363
1334
1364
function abstract_call_opaque_closure (interp:: AbstractInterpreter , closure:: PartialOpaque , argtypes:: Vector{Any} , sv:: InferenceState )
@@ -1341,8 +1371,8 @@ function abstract_call_opaque_closure(interp::AbstractInterpreter, closure::Part
1341
1371
match = MethodMatch (sig, Core. svec (), closure. source, sig <: rewrap_unionall (sigT, tt))
1342
1372
info = OpaqueClosureCallInfo (match)
1343
1373
if ! result. edgecycle
1344
- const_result = abstract_call_method_with_const_args (interp, result, closure, argtypes,
1345
- match, sv, closure. isva)
1374
+ const_result = abstract_call_method_with_const_args (interp, result, closure,
1375
+ ArgInfo ( nothing , argtypes), match, sv, closure. isva)
1346
1376
if const_result != = nothing
1347
1377
const_rettype, const_result = const_result
1348
1378
if const_rettype ⊑ rt
@@ -1365,9 +1395,9 @@ function most_general_argtypes(closure::PartialOpaque)
1365
1395
end
1366
1396
1367
1397
# call where the function is any lattice element
1368
- function abstract_call (interp:: AbstractInterpreter , fargs :: Union{Nothing,Vector{Any}} , argtypes :: Vector{Any} ,
1398
+ function abstract_call (interp:: AbstractInterpreter , arginfo :: ArgInfo ,
1369
1399
sv:: InferenceState , max_methods:: Int = InferenceParams (interp). MAX_METHODS)
1370
- # print("call ", e.args[1], argtypes, "\n\n")
1400
+ argtypes = arginfo . argtypes
1371
1401
ft = argtypes[1 ]
1372
1402
f = singleton_type (ft)
1373
1403
if isa (ft, PartialOpaque)
@@ -1381,9 +1411,9 @@ function abstract_call(interp::AbstractInterpreter, fargs::Union{Nothing,Vector{
1381
1411
add_remark! (interp, sv, " Could not identify method table for call" )
1382
1412
return CallMeta (Any, false )
1383
1413
end
1384
- return abstract_call_gf_by_type (interp, nothing , fargs, argtypes , argtypes_to_type (argtypes), sv, max_methods)
1414
+ return abstract_call_gf_by_type (interp, nothing , arginfo , argtypes_to_type (argtypes), sv, max_methods)
1385
1415
end
1386
- return abstract_call_known (interp, f, fargs, argtypes , sv, max_methods)
1416
+ return abstract_call_known (interp, f, arginfo , sv, max_methods)
1387
1417
end
1388
1418
1389
1419
function sp_type_rewrap (@nospecialize (T), linfo:: MethodInstance , isreturn:: Bool )
@@ -1433,7 +1463,7 @@ function abstract_eval_cfunction(interp::AbstractInterpreter, e::Expr, vtypes::V
1433
1463
# this may be the wrong world for the call,
1434
1464
# but some of the result is likely to be valid anyways
1435
1465
# and that may help generate better codegen
1436
- abstract_call (interp, nothing , at, sv)
1466
+ abstract_call (interp, ArgInfo ( nothing , at) , sv)
1437
1467
nothing
1438
1468
end
1439
1469
@@ -1507,7 +1537,7 @@ function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e),
1507
1537
if argtypes === nothing
1508
1538
t = Bottom
1509
1539
else
1510
- callinfo = abstract_call (interp, ea, argtypes, sv)
1540
+ callinfo = abstract_call (interp, ArgInfo ( ea, argtypes) , sv)
1511
1541
sv. stmt_info[sv. currpc] = callinfo. info
1512
1542
t = callinfo. rt
1513
1543
end
0 commit comments