Skip to content

Commit 3f3632c

Browse files
Ian Atolaviatesk
Ian Atol
andauthored
Relax constraints on inlining for some single calls (#43113)
Co-authored-by: Shuhei Kadowaki <[email protected]>
1 parent 9ab57e8 commit 3f3632c

File tree

2 files changed

+66
-27
lines changed

2 files changed

+66
-27
lines changed

base/compiler/ssair/inlining.jl

+23-27
Original file line numberDiff line numberDiff line change
@@ -1176,26 +1176,24 @@ function analyze_single_call!(
11761176
end
11771177
end
11781178

1179-
# if the signature is fully covered and there is only one applicable method,
1179+
# if the signature is fully or mostly covered and there is only one applicable method,
11801180
# we can try to inline it even if the signature is not a dispatch tuple
1181-
if atype <: signature_union
1182-
if length(cases) == 0 && only_method isa Method
1183-
if length(infos) > 1
1184-
(metharg, methsp) = ccall(:jl_type_intersection_with_env, Any, (Any, Any),
1185-
atype, only_method.sig)::SimpleVector
1186-
match = MethodMatch(metharg, methsp::SimpleVector, only_method, true)
1187-
else
1188-
meth = meth::MethodLookupResult
1189-
@assert length(meth) == 1
1190-
match = meth[1]
1191-
end
1192-
item = analyze_method!(match, argtypes, flag, state)
1193-
item === nothing && return
1194-
push!(cases, InliningCase(match.spec_types, item))
1195-
fully_covered = true
1181+
if length(cases) == 0 && only_method isa Method
1182+
if length(infos) > 1
1183+
(metharg, methsp) = ccall(:jl_type_intersection_with_env, Any, (Any, Any),
1184+
atype, only_method.sig)::SimpleVector
1185+
match = MethodMatch(metharg, methsp::SimpleVector, only_method, true)
1186+
else
1187+
meth = meth::MethodLookupResult
1188+
@assert length(meth) == 1
1189+
match = meth[1]
11961190
end
1191+
item = analyze_method!(match, argtypes, flag, state)
1192+
item === nothing && return
1193+
push!(cases, InliningCase(match.spec_types, item))
1194+
fully_covered = match.fully_covers
11971195
else
1198-
fully_covered = false
1196+
fully_covered &= atype <: signature_union
11991197
end
12001198

12011199
# If we only have one case and that case is fully covered, we may either
@@ -1244,17 +1242,15 @@ function maybe_handle_const_call!(
12441242

12451243
# if the signature is fully covered and there is only one applicable method,
12461244
# we can try to inline it even if the signature is not a dispatch tuple
1247-
if atype <: signature_union
1248-
if length(cases) == 0 && length(results) == 1
1249-
(; mi) = item = InliningTodo(results[1]::InferenceResult, argtypes)
1250-
state.mi_cache !== nothing && (item = resolve_todo(item, state, flag))
1251-
validate_sparams(mi.sparam_vals) || return true
1252-
item === nothing && return true
1253-
push!(cases, InliningCase(mi.specTypes, item))
1254-
fully_covered = true
1255-
end
1245+
if length(cases) == 0 && length(results) == 1
1246+
(; mi) = item = InliningTodo(results[1]::InferenceResult, argtypes)
1247+
state.mi_cache !== nothing && (item = resolve_todo(item, state, flag))
1248+
validate_sparams(mi.sparam_vals) || return true
1249+
item === nothing && return true
1250+
push!(cases, InliningCase(mi.specTypes, item))
1251+
fully_covered = atype <: mi.specTypes
12561252
else
1257-
fully_covered = false
1253+
fully_covered &= atype <: signature_union
12581254
end
12591255

12601256
# If we only have one case and that case is fully covered, we may either

test/compiler/inline.jl

+43
Original file line numberDiff line numberDiff line change
@@ -817,3 +817,46 @@ let
817817
invoke(xs) = validate_unionsplit_inlining(true, xs[1])
818818
@test invoke(Any[10]) === false
819819
end
820+
821+
# issue 43104
822+
823+
@inline isGoodType(@nospecialize x::Type) =
824+
x !== Any && !(@noinline Base.has_free_typevars(x))
825+
let # aggressive inlining of single, abstract method match
826+
src = code_typed((Type, Any,)) do x, y
827+
isGoodType(x), isGoodType(y)
828+
end |> only |> first
829+
# both callsites should be inlined
830+
@test count(isinvoke(:has_free_typevars), src.code) == 2
831+
# `isGoodType(y::Any)` isn't fully covered, thus a runtime type check and fallback dynamic dispatch should be inserted
832+
@test count(iscall((src,isGoodType)), src.code) == 1
833+
end
834+
835+
@inline isGoodType2(cnd, @nospecialize x::Type) =
836+
x !== Any && !(@noinline (cnd ? Core.Compiler.isType : Base.has_free_typevars)(x))
837+
let # aggressive inlining of single, abstract method match (with constant-prop'ed)
838+
src = code_typed((Type, Any,)) do x, y
839+
isGoodType2(true, x), isGoodType2(true, y)
840+
end |> only |> first
841+
# both callsite should be inlined with constant-prop'ed result
842+
@test count(isinvoke(:isType), src.code) == 2
843+
@test count(isinvoke(:has_free_typevars), src.code) == 0
844+
# `isGoodType(y::Any)` isn't fully convered, thus a runtime type check and fallback dynamic dispatch should be inserted
845+
@test count(iscall((src,isGoodType2)), src.code) == 1
846+
end
847+
848+
@noinline function checkBadType!(@nospecialize x::Type)
849+
if x === Any || Base.has_free_typevars(x)
850+
println(x)
851+
end
852+
return nothing
853+
end
854+
let # aggressive static dispatch of single, abstract method match
855+
src = code_typed((Type, Any,)) do x, y
856+
checkBadType!(x), checkBadType!(y)
857+
end |> only |> first
858+
# both callsites should be resolved statically
859+
@test count(isinvoke(:checkBadType!), src.code) == 2
860+
# `checkBadType!(y::Any)` isn't fully covered, thus a runtime type check and fallback dynamic dispatch should be inserted
861+
@test count(iscall((src,checkBadType!)), src.code) == 1
862+
end

0 commit comments

Comments
 (0)