Skip to content

Commit 6e80df2

Browse files
committed
inlining: bail out unless match.spec_types <: match.method.sig (#53720)
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 a66bbfa commit 6e80df2

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
@@ -577,11 +577,11 @@ function ir_inline_unionsplit!(compact::IncrementalCompact, idx::Int, argexprs::
577577
@assert nparams == fieldcount(mtype)
578578
if !(i == ncases && fully_covered)
579579
for i = 1:nparams
580-
a, m = fieldtype(atype, i), fieldtype(mtype, i)
580+
aft, mft = fieldtype(atype, i), fieldtype(mtype, i)
581581
# If this is always true, we don't need to check for it
582-
a <: m && continue
582+
aft <: mft && continue
583583
# Generate isa check
584-
isa_expr = Expr(:call, isa, argexprs[i], m)
584+
isa_expr = Expr(:call, isa, argexprs[i], mft)
585585
ssa = insert_node_here!(compact, NewInstruction(isa_expr, Bool, line))
586586
if cond === true
587587
cond = ssa
@@ -600,10 +600,10 @@ function ir_inline_unionsplit!(compact::IncrementalCompact, idx::Int, argexprs::
600600
for i = 1:nparams
601601
argex = argexprs[i]
602602
(isa(argex, SSAValue) || isa(argex, Argument)) || continue
603-
a, m = fieldtype(atype, i), fieldtype(mtype, i)
604-
if !(a <: m)
603+
aft, mft = fieldtype(atype, i), fieldtype(mtype, i)
604+
if !(aft <: mft)
605605
argexprs′[i] = insert_node_here!(compact,
606-
NewInstruction(PiNode(argex, m), m, line))
606+
NewInstruction(PiNode(argex, mft), mft, line))
607607
end
608608
end
609609
end
@@ -964,7 +964,8 @@ function analyze_method!(match::MethodMatch, argtypes::Vector{Any},
964964
if !match.fully_covers
965965
# type-intersection was not able to give us a simple list of types, so
966966
# ir_inline_unionsplit won't be able to deal with inlining this
967-
if !(spec_types isa DataType && length(spec_types.parameters) == length(argtypes) && !isvarargtype(spec_types.parameters[end]))
967+
if !(spec_types isa DataType && length(spec_types.parameters) == npassedargs &&
968+
!isvarargtype(spec_types.parameters[end]))
968969
return nothing
969970
end
970971
end
@@ -1374,16 +1375,18 @@ function compute_inlining_cases(@nospecialize(info::CallInfo), flag::UInt32, sig
13741375
joint_effects = merge_effects(joint_effects, info_effects(result, match, state))
13751376
split_fully_covered |= match.fully_covers
13761377
if !validate_sparams(match.sparams)
1377-
if !match.fully_covers
1378-
handled_all_cases = false
1379-
continue
1380-
end
1381-
if revisit_idx === nothing
1382-
revisit_idx = (i, j, all_result_count)
1378+
if match.fully_covers
1379+
if revisit_idx === nothing
1380+
revisit_idx = (i, j, all_result_count)
1381+
else
1382+
handled_all_cases = false
1383+
revisit_idx = nothing
1384+
end
13831385
else
13841386
handled_all_cases = false
1385-
revisit_idx = nothing
13861387
end
1388+
elseif !(match.spec_types <: match.method.sig) # the requirement for correct union-split
1389+
handled_all_cases = false
13871390
else
13881391
handled_all_cases &= handle_any_const_result!(cases,
13891392
result, match, argtypes, info, flag, state; allow_abstract=true, allow_typevars=false)
@@ -1418,14 +1421,15 @@ function handle_call!(todo::Vector{Pair{Int,Any}},
14181421
cases = compute_inlining_cases(info, flag, sig, state)
14191422
cases === nothing && return nothing
14201423
cases, all_covered, joint_effects = cases
1421-
handle_cases!(todo, ir, idx, stmt, argtypes_to_type(sig.argtypes), cases,
1422-
all_covered, joint_effects)
1424+
atype = argtypes_to_type(sig.argtypes)
1425+
handle_cases!(todo, ir, idx, stmt, atype, cases, all_covered, joint_effects)
14231426
end
14241427

14251428
function handle_match!(cases::Vector{InliningCase},
14261429
match::MethodMatch, argtypes::Vector{Any}, @nospecialize(info::CallInfo), flag::UInt32,
14271430
state::InliningState;
1428-
allow_abstract::Bool, allow_typevars::Bool, volatile_inf_result::Union{Nothing,VolatileInferenceResult})
1431+
allow_abstract::Bool, allow_typevars::Bool,
1432+
volatile_inf_result::Union{Nothing,VolatileInferenceResult})
14291433
spec_types = match.spec_types
14301434
allow_abstract || isdispatchtuple(spec_types) || return false
14311435
# We may see duplicated dispatch signatures here when a signature gets widened
@@ -1512,19 +1516,19 @@ function concrete_result_item(result::ConcreteResult, @nospecialize(info::CallIn
15121516
end
15131517

15141518
function handle_cases!(todo::Vector{Pair{Int,Any}}, ir::IRCode, idx::Int, stmt::Expr,
1515-
@nospecialize(atype), cases::Vector{InliningCase}, fully_covered::Bool,
1519+
@nospecialize(atype), cases::Vector{InliningCase}, all_covered::Bool,
15161520
joint_effects::Effects)
15171521
# If we only have one case and that case is fully covered, we may either
15181522
# be able to do the inlining now (for constant cases), or push it directly
15191523
# onto the todo list
1520-
if fully_covered && length(cases) == 1
1524+
if all_covered && length(cases) == 1
15211525
handle_single_case!(todo, ir, idx, stmt, cases[1].item)
15221526
elseif length(cases) > 0
15231527
isa(atype, DataType) || return nothing
15241528
for case in cases
15251529
isa(case.sig, DataType) || return nothing
15261530
end
1527-
push!(todo, idx=>UnionSplit(fully_covered, atype, cases))
1531+
push!(todo, idx=>UnionSplit(all_covered, atype, cases))
15281532
else
15291533
add_flag!(ir[SSAValue(idx)], flags_for_effects(joint_effects))
15301534
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)