From 28dc0ebbf352ce9d6ce1cd5e322e9fd64418bb63 Mon Sep 17 00:00:00 2001 From: Ian Atol Date: Tue, 16 Nov 2021 19:20:11 -0500 Subject: [PATCH] Relax constraints on inlining for some single calls --- base/compiler/ssair/inlining.jl | 20 +++++++++++--- test/compiler/inline.jl | 47 +++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index 49f21f59936041..af6f8b394bc685 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -1169,9 +1169,21 @@ function analyze_single_call!( end end - # if the signature is fully covered and there is only one applicable method, + local mostly_covered = true + if typeof(signature_union) == DataType && typeof(atype) == DataType + sig_union_ts = datatype_fieldtypes(signature_union) + atype_ts = datatype_fieldtypes(atype) + for (idx, atype_t) in enumerate(atype_ts) + sigu_t = sig_union_ts[idx] + sub = (atype_t <: sigu_t) || atype_t == Any + mostly_covered = mostly_covered && sub + end + end + mostly_covered = mostly_covered && !(atype <: signature_union) + + # if the signature is fully or mostly covered and there is only one applicable method, # we can try to inline it even if the signature is not a dispatch tuple - if atype <: signature_union + if atype <: signature_union || mostly_covered if length(cases) == 0 && only_method isa Method if length(infos) > 1 (metharg, methsp) = ccall(:jl_type_intersection_with_env, Any, (Any, Any), @@ -1185,7 +1197,9 @@ function analyze_single_call!( item = analyze_method!(match, argtypes, state, flag) item === nothing && return push!(cases, InliningCase(match.spec_types, item)) - fully_covered = true + if !mostly_covered + fully_covered = true + end end else fully_covered = false diff --git a/test/compiler/inline.jl b/test/compiler/inline.jl index 36acf4a8d299f2..456e2c933ba1f4 100644 --- a/test/compiler/inline.jl +++ b/test/compiler/inline.jl @@ -817,3 +817,50 @@ let invoke(xs) = validate_unionsplit_inlining(true, xs[1]) @test invoke(Any[10]) === false end + +# issue 43104 +import Core.Compiler: argextype, singleton_type +const EMPTY_SPTYPES = Core.Compiler.EMPTY_SLOTTYPES + +# check if `x` is a dynamic call of a given function +iscall(y) = @nospecialize(x) -> iscall(y, x) +function iscall((src, f)::Tuple{Core.CodeInfo,Function}, @nospecialize(x)) + return iscall(x) do @nospecialize x + singleton_type(argextype(x, src, EMPTY_SPTYPES)) === f + end +end +iscall(pred::Function, @nospecialize(x)) = Meta.isexpr(x, :call) && pred(x.args[1]) + +# check if `x` is a statically-resolved call of a function whose name is `sym` +isinvoke(y) = @nospecialize(x) -> isinvoke(y, x) +isinvoke(sym::Symbol, @nospecialize(x)) = isinvoke(mi->mi.def.name===sym, x) +isinvoke(pred::Function, @nospecialize(x)) = Meta.isexpr(x, :invoke) && pred(x.args[1]::Core.MethodInstance) + +# NOTE we actually don't need any `@nospecialize` annotation here +@inline isGoodType(@nospecialize x::Type) = + x !== Any && !(@noinline Base.has_free_typevars(x)) +let # aggressive inlining of single, abstract method match + src = code_typed((Type, Any,)) do x, y + isGoodType(x), isGoodType(y) + end |> only |> first + # both callsite should be inlined + @test count(isinvoke(:has_free_typevars), src.code) == 2 + # `isGoodType(y::Any)` isn't fully convered, thus the inlining should be via runtime type check + @test count(iscall((src,isGoodType)), src.code) == 1 +end + +@noinline function checkBadType!(@nospecialize x::Type) + if x === Any || Base.has_free_typevars(x) + println(x) + end + return nothing +end +let # aggressive inlining of single, abstract method match + src = code_typed((Type, Any,)) do x, y + checkBadType!(x), checkBadType!(y) + end |> only |> first + # both callsite should be resolved statically + @test count(isinvoke(:checkBadType!), src.code) == 2 + # `checkBadType!(y::Any)` isn't fully convered, thus the static dispatch should be via runtime type check + @test count(iscall((src,checkBadType!)), src.code) == 1 +end