Skip to content

Commit 63e75e8

Browse files
committed
inlining: bail out unless match.spec_types <: match.method.sig
As Jameson pointed out in the link below, while the union-split handles cases when there are uncovered matches, sometimes the expected condition `spec_types <: method.sig` that the union-split algorithm relies on isn't met in such cases, which caused issues like #52644. This commit fixes the problem by adding explicit checks for such cases. Note that this is based on #52092. The extra handling for single method match unmatched static parameters based on `only_method` that was removed in JuliaLang/#52092 has been ineffective and would otherwise cause problematic inlining on this PR. We'll likely need to come back to this later and figure out a safe and effective way to deal with such cases in the future when the handling for either case turns out to be necessary. - closes #52644 - xref: <#53600 (review)>
1 parent 48e89db commit 63e75e8

File tree

2 files changed

+45
-20
lines changed

2 files changed

+45
-20
lines changed

base/compiler/ssair/inlining.jl

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -542,11 +542,11 @@ function ir_inline_unionsplit!(compact::IncrementalCompact, idx::Int, argexprs::
542542
@assert nparams == fieldcount(mtype)
543543
if !(i == ncases && fully_covered)
544544
for i = 1:nparams
545-
a, m = fieldtype(atype, i), fieldtype(mtype, i)
545+
aft, mft = fieldtype(atype, i), fieldtype(mtype, i)
546546
# If this is always true, we don't need to check for it
547-
a <: m && continue
547+
aft <: mft && continue
548548
# Generate isa check
549-
isa_expr = Expr(:call, isa, argexprs[i], m)
549+
isa_expr = Expr(:call, isa, argexprs[i], mft)
550550
ssa = insert_node_here!(compact, NewInstruction(isa_expr, Bool, line))
551551
if cond === true
552552
cond = ssa
@@ -565,10 +565,10 @@ function ir_inline_unionsplit!(compact::IncrementalCompact, idx::Int, argexprs::
565565
for i = 1:nparams
566566
argex = argexprs[i]
567567
(isa(argex, SSAValue) || isa(argex, Argument)) || continue
568-
a, m = fieldtype(atype, i), fieldtype(mtype, i)
569-
if !(a <: m)
568+
aft, mft = fieldtype(atype, i), fieldtype(mtype, i)
569+
if !(aft <: mft)
570570
argexprs′[i] = insert_node_here!(compact,
571-
NewInstruction(PiNode(argex, m), m, line))
571+
NewInstruction(PiNode(argex, mft), mft, line))
572572
end
573573
end
574574
end
@@ -944,7 +944,8 @@ function analyze_method!(match::MethodMatch, argtypes::Vector{Any},
944944
if !match.fully_covers
945945
# type-intersection was not able to give us a simple list of types, so
946946
# ir_inline_unionsplit won't be able to deal with inlining this
947-
if !(spec_types isa DataType && length(spec_types.parameters) == length(argtypes) && !isvarargtype(spec_types.parameters[end]))
947+
if !(spec_types isa DataType && length(spec_types.parameters) == npassedargs &&
948+
!isvarargtype(spec_types.parameters[end]))
948949
return nothing
949950
end
950951
end
@@ -1355,16 +1356,18 @@ function compute_inlining_cases(@nospecialize(info::CallInfo), flag::UInt32, sig
13551356
joint_effects = merge_effects(joint_effects, info_effects(result, match, state))
13561357
split_fully_covered |= match.fully_covers
13571358
if !validate_sparams(match.sparams)
1358-
if !match.fully_covers
1359-
handled_all_cases = false
1360-
continue
1361-
end
1362-
if revisit_idx === nothing
1363-
revisit_idx = (i, j, all_result_count)
1359+
if match.fully_covers
1360+
if revisit_idx === nothing
1361+
revisit_idx = (i, j, all_result_count)
1362+
else
1363+
handled_all_cases = false
1364+
revisit_idx = nothing
1365+
end
13641366
else
13651367
handled_all_cases = false
1366-
revisit_idx = nothing
13671368
end
1369+
elseif !(match.spec_types <: match.method.sig) # the requirement for correct union-split
1370+
handled_all_cases = false
13681371
else
13691372
handled_all_cases &= handle_any_const_result!(cases,
13701373
result, match, argtypes, info, flag, state; allow_abstract=true, allow_typevars=false)
@@ -1399,14 +1402,15 @@ function handle_call!(todo::Vector{Pair{Int,Any}},
13991402
cases = compute_inlining_cases(info, flag, sig, state)
14001403
cases === nothing && return nothing
14011404
cases, all_covered, joint_effects = cases
1402-
handle_cases!(todo, ir, idx, stmt, argtypes_to_type(sig.argtypes), cases,
1403-
all_covered, joint_effects)
1405+
atype = argtypes_to_type(sig.argtypes)
1406+
handle_cases!(todo, ir, idx, stmt, atype, cases, all_covered, joint_effects)
14041407
end
14051408

14061409
function handle_match!(cases::Vector{InliningCase},
14071410
match::MethodMatch, argtypes::Vector{Any}, @nospecialize(info::CallInfo), flag::UInt32,
14081411
state::InliningState;
1409-
allow_abstract::Bool, allow_typevars::Bool, volatile_inf_result::Union{Nothing,VolatileInferenceResult})
1412+
allow_abstract::Bool, allow_typevars::Bool,
1413+
volatile_inf_result::Union{Nothing,VolatileInferenceResult})
14101414
spec_types = match.spec_types
14111415
allow_abstract || isdispatchtuple(spec_types) || return false
14121416
# We may see duplicated dispatch signatures here when a signature gets widened
@@ -1493,19 +1497,19 @@ function concrete_result_item(result::ConcreteResult, @nospecialize(info::CallIn
14931497
end
14941498

14951499
function handle_cases!(todo::Vector{Pair{Int,Any}}, ir::IRCode, idx::Int, stmt::Expr,
1496-
@nospecialize(atype), cases::Vector{InliningCase}, fully_covered::Bool,
1500+
@nospecialize(atype), cases::Vector{InliningCase}, all_covered::Bool,
14971501
joint_effects::Effects)
14981502
# If we only have one case and that case is fully covered, we may either
14991503
# be able to do the inlining now (for constant cases), or push it directly
15001504
# onto the todo list
1501-
if fully_covered && length(cases) == 1
1505+
if all_covered && length(cases) == 1
15021506
handle_single_case!(todo, ir, idx, stmt, cases[1].item)
15031507
elseif length(cases) > 0
15041508
isa(atype, DataType) || return nothing
15051509
for case in cases
15061510
isa(case.sig, DataType) || return nothing
15071511
end
1508-
push!(todo, idx=>UnionSplit(fully_covered, atype, cases))
1512+
push!(todo, idx=>UnionSplit(all_covered, atype, cases))
15091513
else
15101514
add_flag!(ir[SSAValue(idx)], flags_for_effects(joint_effects))
15111515
end

test/compiler/inline.jl

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2159,3 +2159,24 @@ end
21592159
@test !Core.Compiler.is_nothrow(Base.infer_effects(issue53062, (Bool,)))
21602160
@test issue53062(false) == -1
21612161
@test_throws MethodError issue53062(true)
2162+
2163+
struct Issue52644
2164+
tuple::Type{<:Tuple}
2165+
end
2166+
issue52644(::DataType) = :DataType
2167+
issue52644(::UnionAll) = :UnionAll
2168+
let ir = Base.code_ircode((Issue52644,); optimize_until="Inlining") do t
2169+
issue52644(t.tuple)
2170+
end |> only |> first
2171+
irfunc = Core.OpaqueClosure(ir)
2172+
@test irfunc(Issue52644(Tuple{})) === :DataType
2173+
@test irfunc(Issue52644(Tuple{<:Integer})) === :UnionAll
2174+
end
2175+
issue52644_single(x::DataType) = :DataType
2176+
let ir = Base.code_ircode((Issue52644,); optimize_until="Inlining") do t
2177+
issue52644_single(t.tuple)
2178+
end |> only |> first
2179+
irfunc = Core.OpaqueClosure(ir)
2180+
@test irfunc(Issue52644(Tuple{})) === :DataType
2181+
@test_throws MethodError irfunc(Issue52644(Tuple{<:Integer}))
2182+
end

0 commit comments

Comments
 (0)