Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

more aggressive inlining of single, abstract method match #43104

Closed
aviatesk opened this issue Nov 16, 2021 · 0 comments · Fixed by #43113
Closed

more aggressive inlining of single, abstract method match #43104

aviatesk opened this issue Nov 16, 2021 · 0 comments · Fixed by #43113
Labels
compiler:optimizer Optimization passes (mostly in base/compiler/ssair/)

Comments

@aviatesk
Copy link
Sponsor Member

After having some discussion with @vtjnash on #43097, I found our inlining algorithm could inline single, abstract method match more aggressively.
For example, we can inline isGoodType in the example below:

julia> @inline isGoodType(@nospecialize x::Type) = x !== Any && !Base.has_free_typevars(x)
isGoodType (generic function with 1 method)

julia> code_typed((Any,)) do x
           isGoodType(x)
       end |> only
CodeInfo(
1%1 = Main.isGoodType(x)::Bool
└──      return %1
) => Bool

Especially, we want to relax the condition here:

if atype <: signature_union

so that it can inline it even if the call signature isn't fully covered by the method signature.

In order to assert the functionality, I think these test cases would be enough:

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

  • @vtjnash do you have any further comment on this?
  • @ianatol / @oscardssmith do either of you want to work on this? It might be a good opportunity to get familiar with our inlining algorithm, and I'm welcome to have a review on it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler:optimizer Optimization passes (mostly in base/compiler/ssair/)
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant