@@ -50,12 +50,13 @@ struct InliningCase
50
50
end
51
51
52
52
struct UnionSplit
53
- fully_covered:: Bool
53
+ handled_all_cases:: Bool # All possible dispatches are included in the cases
54
+ fully_covered:: Bool # All handled cases are fully covering
54
55
atype:: DataType
55
56
cases:: Vector{InliningCase}
56
57
bbs:: Vector{Int}
57
- UnionSplit (fully_covered:: Bool , atype:: DataType , cases:: Vector{InliningCase} ) =
58
- new (fully_covered, atype, cases, Int[])
58
+ UnionSplit (handled_all_cases :: Bool , fully_covered:: Bool , atype:: DataType , cases:: Vector{InliningCase} ) =
59
+ new (handled_all_cases, fully_covered, atype, cases, Int[])
59
60
end
60
61
61
62
struct InliningEdgeTracker
215
216
216
217
function cfg_inline_unionsplit! (ir:: IRCode , idx:: Int , union_split:: UnionSplit ,
217
218
state:: CFGInliningState , params:: OptimizationParams )
218
- (; fully_covered, #= atype,=# cases, bbs) = union_split
219
+ (; handled_all_cases, fully_covered, #= atype,=# cases, bbs) = union_split
219
220
inline_into_block! (state, block_for_inst (ir, idx))
220
221
from_bbs = Int[]
221
222
delete! (state. split_targets, length (state. new_cfg_blocks))
@@ -235,7 +236,7 @@ function cfg_inline_unionsplit!(ir::IRCode, idx::Int, union_split::UnionSplit,
235
236
end
236
237
end
237
238
push! (from_bbs, length (state. new_cfg_blocks))
238
- if ! (i == length (cases) && fully_covered)
239
+ if ! (i == length (cases) && (handled_all_cases && fully_covered) )
239
240
# This block will have the next condition or the final else case
240
241
push! (state. new_cfg_blocks, BasicBlock (StmtRange (idx, idx)))
241
242
push! (state. new_cfg_blocks[cond_bb]. succs, length (state. new_cfg_blocks))
@@ -244,7 +245,10 @@ function cfg_inline_unionsplit!(ir::IRCode, idx::Int, union_split::UnionSplit,
244
245
end
245
246
end
246
247
# The edge from the fallback block.
247
- fully_covered || push! (from_bbs, length (state. new_cfg_blocks))
248
+ # NOTE This edge is only required for `!handled_all_cases` and not `!fully_covered`,
249
+ # since in the latter case we inline `Core.throw_methoderror` into the fallback
250
+ # block, which is must-throw, making the subsequent code path unreachable.
251
+ ! handled_all_cases && push! (from_bbs, length (state. new_cfg_blocks))
248
252
# This block will be the block everyone returns to
249
253
push! (state. new_cfg_blocks, BasicBlock (StmtRange (idx, idx), from_bbs, orig_succs))
250
254
join_bb = length (state. new_cfg_blocks)
@@ -523,7 +527,7 @@ assuming their order stays the same post-discovery in `ml_matches`.
523
527
function ir_inline_unionsplit! (compact:: IncrementalCompact , idx:: Int , argexprs:: Vector{Any} ,
524
528
union_split:: UnionSplit , boundscheck:: Symbol ,
525
529
todo_bbs:: Vector{Tuple{Int,Int}} , interp:: AbstractInterpreter )
526
- (; fully_covered, atype, cases, bbs) = union_split
530
+ (; handled_all_cases, fully_covered, atype, cases, bbs) = union_split
527
531
stmt, typ, line = compact. result[idx][:stmt ], compact. result[idx][:type ], compact. result[idx][:line ]
528
532
join_bb = bbs[end ]
529
533
pn = PhiNode ()
@@ -538,7 +542,7 @@ function ir_inline_unionsplit!(compact::IncrementalCompact, idx::Int, argexprs::
538
542
cond = true
539
543
nparams = fieldcount (atype)
540
544
@assert nparams == fieldcount (mtype)
541
- if ! (i == ncases && fully_covered)
545
+ if ! (i == ncases && fully_covered && handled_all_cases )
542
546
for i = 1 : nparams
543
547
aft, mft = fieldtype (atype, i), fieldtype (mtype, i)
544
548
# If this is always true, we don't need to check for it
@@ -597,14 +601,18 @@ function ir_inline_unionsplit!(compact::IncrementalCompact, idx::Int, argexprs::
597
601
end
598
602
bb += 1
599
603
# We're now in the fall through block, decide what to do
600
- if ! fully_covered
604
+ if ! handled_all_cases
601
605
ssa = insert_node_here! (compact, NewInstruction (stmt, typ, line))
602
606
push! (pn. edges, bb)
603
607
push! (pn. values, ssa)
604
608
insert_node_here! (compact, NewInstruction (GotoNode (join_bb), Any, line))
605
609
finish_current_bb! (compact, 0 )
610
+ elseif ! fully_covered
611
+ insert_node_here! (compact, NewInstruction (Expr (:call , GlobalRef (Core, :throw_methoderror ), argexprs... ), Union{}, line))
612
+ insert_node_here! (compact, NewInstruction (ReturnNode (), Union{}, line))
613
+ finish_current_bb! (compact, 0 )
614
+ ncases == 0 && return insert_node_here! (compact, NewInstruction (nothing , Any, line))
606
615
end
607
-
608
616
# We're now in the join block.
609
617
return insert_node_here! (compact, NewInstruction (pn, typ, line))
610
618
end
@@ -1348,10 +1356,6 @@ function compute_inlining_cases(@nospecialize(info::CallInfo), flag::UInt32, sig
1348
1356
# Too many applicable methods
1349
1357
# Or there is a (partial?) ambiguity
1350
1358
return nothing
1351
- elseif length (meth) == 0
1352
- # No applicable methods; try next union split
1353
- handled_all_cases = false
1354
- continue
1355
1359
end
1356
1360
local split_fully_covered = false
1357
1361
for (j, match) in enumerate (meth)
@@ -1392,22 +1396,26 @@ function compute_inlining_cases(@nospecialize(info::CallInfo), flag::UInt32, sig
1392
1396
handled_all_cases &= handle_any_const_result! (cases,
1393
1397
result, match, argtypes, info, flag, state; allow_typevars= true )
1394
1398
end
1399
+ if ! fully_covered
1400
+ atype = argtypes_to_type (sig. argtypes)
1401
+ # We will emit an inline MethodError so we need a backedge to the MethodTable
1402
+ add_uncovered_edges! (state. edges, info, atype)
1403
+ end
1395
1404
elseif ! isempty (cases)
1396
1405
# if we've not seen all candidates, union split is valid only for dispatch tuples
1397
1406
filter! (case:: InliningCase -> isdispatchtuple (case. sig), cases)
1398
1407
end
1399
-
1400
- return cases, (handled_all_cases & fully_covered), joint_effects
1408
+ return cases, handled_all_cases, fully_covered, joint_effects
1401
1409
end
1402
1410
1403
1411
function handle_call! (todo:: Vector{Pair{Int,Any}} ,
1404
1412
ir:: IRCode , idx:: Int , stmt:: Expr , @nospecialize (info:: CallInfo ), flag:: UInt32 , sig:: Signature ,
1405
1413
state:: InliningState )
1406
1414
cases = compute_inlining_cases (info, flag, sig, state)
1407
1415
cases === nothing && return nothing
1408
- cases, all_covered , joint_effects = cases
1416
+ cases, handled_all_cases, fully_covered , joint_effects = cases
1409
1417
atype = argtypes_to_type (sig. argtypes)
1410
- handle_cases! (todo, ir, idx, stmt, atype, cases, all_covered , joint_effects)
1418
+ handle_cases! (todo, ir, idx, stmt, atype, cases, handled_all_cases, fully_covered , joint_effects)
1411
1419
end
1412
1420
1413
1421
function handle_match! (cases:: Vector{InliningCase} ,
@@ -1496,19 +1504,19 @@ function concrete_result_item(result::ConcreteResult, @nospecialize(info::CallIn
1496
1504
end
1497
1505
1498
1506
function handle_cases! (todo:: Vector{Pair{Int,Any}} , ir:: IRCode , idx:: Int , stmt:: Expr ,
1499
- @nospecialize (atype), cases:: Vector{InliningCase} , all_covered :: Bool ,
1507
+ @nospecialize (atype), cases:: Vector{InliningCase} , handled_all_cases :: Bool , fully_covered :: Bool ,
1500
1508
joint_effects:: Effects )
1501
1509
# If we only have one case and that case is fully covered, we may either
1502
1510
# be able to do the inlining now (for constant cases), or push it directly
1503
1511
# onto the todo list
1504
- if all_covered && length (cases) == 1
1512
+ if fully_covered && handled_all_cases && length (cases) == 1
1505
1513
handle_single_case! (todo, ir, idx, stmt, cases[1 ]. item)
1506
- elseif length (cases) > 0
1514
+ elseif length (cases) > 0 || handled_all_cases
1507
1515
isa (atype, DataType) || return nothing
1508
1516
for case in cases
1509
1517
isa (case. sig, DataType) || return nothing
1510
1518
end
1511
- push! (todo, idx=> UnionSplit (all_covered , atype, cases))
1519
+ push! (todo, idx=> UnionSplit (handled_all_cases, fully_covered , atype, cases))
1512
1520
else
1513
1521
add_flag! (ir[SSAValue (idx)], flags_for_effects (joint_effects))
1514
1522
end
0 commit comments