@@ -50,12 +50,13 @@ struct InliningCase
5050end
5151
5252struct 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
5455 atype:: DataType
5556 cases:: Vector{InliningCase}
5657 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[])
5960end
6061
6162struct InliningEdgeTracker
215216
216217function cfg_inline_unionsplit! (ir:: IRCode , idx:: Int , union_split:: UnionSplit ,
217218 state:: CFGInliningState , params:: OptimizationParams )
218- (; fully_covered, #= atype,=# cases, bbs) = union_split
219+ (; handled_all_cases, fully_covered, #= atype,=# cases, bbs) = union_split
219220 inline_into_block! (state, block_for_inst (ir, idx))
220221 from_bbs = Int[]
221222 delete! (state. split_targets, length (state. new_cfg_blocks))
@@ -235,7 +236,7 @@ function cfg_inline_unionsplit!(ir::IRCode, idx::Int, union_split::UnionSplit,
235236 end
236237 end
237238 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) )
239240 # This block will have the next condition or the final else case
240241 push! (state. new_cfg_blocks, BasicBlock (StmtRange (idx, idx)))
241242 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,
244245 end
245246 end
246247 # 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))
248252 # This block will be the block everyone returns to
249253 push! (state. new_cfg_blocks, BasicBlock (StmtRange (idx, idx), from_bbs, orig_succs))
250254 join_bb = length (state. new_cfg_blocks)
@@ -523,7 +527,7 @@ assuming their order stays the same post-discovery in `ml_matches`.
523527function ir_inline_unionsplit! (compact:: IncrementalCompact , idx:: Int , argexprs:: Vector{Any} ,
524528 union_split:: UnionSplit , boundscheck:: Symbol ,
525529 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
527531 stmt, typ, line = compact. result[idx][:stmt ], compact. result[idx][:type ], compact. result[idx][:line ]
528532 join_bb = bbs[end ]
529533 pn = PhiNode ()
@@ -538,7 +542,7 @@ function ir_inline_unionsplit!(compact::IncrementalCompact, idx::Int, argexprs::
538542 cond = true
539543 nparams = fieldcount (atype)
540544 @assert nparams == fieldcount (mtype)
541- if ! (i == ncases && fully_covered)
545+ if ! (i == ncases && fully_covered && handled_all_cases )
542546 for i = 1 : nparams
543547 aft, mft = fieldtype (atype, i), fieldtype (mtype, i)
544548 # 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::
597601 end
598602 bb += 1
599603 # We're now in the fall through block, decide what to do
600- if ! fully_covered
604+ if ! handled_all_cases
601605 ssa = insert_node_here! (compact, NewInstruction (stmt, typ, line))
602606 push! (pn. edges, bb)
603607 push! (pn. values, ssa)
604608 insert_node_here! (compact, NewInstruction (GotoNode (join_bb), Any, line))
605609 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))
606615 end
607-
608616 # We're now in the join block.
609617 return insert_node_here! (compact, NewInstruction (pn, typ, line))
610618end
@@ -1348,10 +1356,6 @@ function compute_inlining_cases(@nospecialize(info::CallInfo), flag::UInt32, sig
13481356 # Too many applicable methods
13491357 # Or there is a (partial?) ambiguity
13501358 return nothing
1351- elseif length (meth) == 0
1352- # No applicable methods; try next union split
1353- handled_all_cases = false
1354- continue
13551359 end
13561360 local split_fully_covered = false
13571361 for (j, match) in enumerate (meth)
@@ -1392,22 +1396,26 @@ function compute_inlining_cases(@nospecialize(info::CallInfo), flag::UInt32, sig
13921396 handled_all_cases &= handle_any_const_result! (cases,
13931397 result, match, argtypes, info, flag, state; allow_typevars= true )
13941398 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
13951404 elseif ! isempty (cases)
13961405 # if we've not seen all candidates, union split is valid only for dispatch tuples
13971406 filter! (case:: InliningCase -> isdispatchtuple (case. sig), cases)
13981407 end
1399-
1400- return cases, (handled_all_cases & fully_covered), joint_effects
1408+ return cases, handled_all_cases, fully_covered, joint_effects
14011409end
14021410
14031411function handle_call! (todo:: Vector{Pair{Int,Any}} ,
14041412 ir:: IRCode , idx:: Int , stmt:: Expr , @nospecialize (info:: CallInfo ), flag:: UInt32 , sig:: Signature ,
14051413 state:: InliningState )
14061414 cases = compute_inlining_cases (info, flag, sig, state)
14071415 cases === nothing && return nothing
1408- cases, all_covered , joint_effects = cases
1416+ cases, handled_all_cases, fully_covered , joint_effects = cases
14091417 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)
14111419end
14121420
14131421function handle_match! (cases:: Vector{InliningCase} ,
@@ -1496,19 +1504,19 @@ function concrete_result_item(result::ConcreteResult, @nospecialize(info::CallIn
14961504end
14971505
14981506function 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 ,
15001508 joint_effects:: Effects )
15011509 # If we only have one case and that case is fully covered, we may either
15021510 # be able to do the inlining now (for constant cases), or push it directly
15031511 # onto the todo list
1504- if all_covered && length (cases) == 1
1512+ if fully_covered && handled_all_cases && length (cases) == 1
15051513 handle_single_case! (todo, ir, idx, stmt, cases[1 ]. item)
1506- elseif length (cases) > 0
1514+ elseif length (cases) > 0 || handled_all_cases
15071515 isa (atype, DataType) || return nothing
15081516 for case in cases
15091517 isa (case. sig, DataType) || return nothing
15101518 end
1511- push! (todo, idx=> UnionSplit (all_covered , atype, cases))
1519+ push! (todo, idx=> UnionSplit (handled_all_cases, fully_covered , atype, cases))
15121520 else
15131521 add_flag! (ir[SSAValue (idx)], flags_for_effects (joint_effects))
15141522 end
0 commit comments